354 lines
11 KiB
C#
354 lines
11 KiB
C#
using Kryz.CharacterStats.Examples;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
|
|
public class Health : Resource
|
|
{
|
|
protected CharacterStats character;
|
|
|
|
public ScrollingText scrollingTextPrefab;
|
|
protected ScrollingText scrollingText;
|
|
|
|
protected AbsorbEffectInstance absorbEffectInstance;
|
|
protected DamageIncomeModifierEffectInstance damageIncomeModifierEffectInstance;
|
|
|
|
public UnityEvent<float> onMaxHealthChanged = new UnityEvent<float>();
|
|
public UnityEvent onDeath = new UnityEvent();
|
|
|
|
private bool noCost = false;
|
|
public bool NoCost => noCost;
|
|
|
|
public bool invulnerable = false;
|
|
public bool Invulnerable => invulnerable;
|
|
|
|
float incomingValue;
|
|
float percentStatMitigation;
|
|
float reducedDamage;
|
|
|
|
private bool isDead;
|
|
|
|
public UnityEvent<bool> OnInvulnerabilityStateChanged = new UnityEvent<bool>();
|
|
public UnityEvent OnDodgedSuccessfully = new UnityEvent();
|
|
|
|
public void SetIsDeadState(bool isDead)
|
|
{
|
|
this.isDead = isDead;
|
|
canRegen = !isDead;
|
|
}
|
|
|
|
protected virtual void Awake()
|
|
{
|
|
character = GetComponent<CharacterStats>();
|
|
absorbEffectInstance = GetComponent<AbsorbEffectInstance>();
|
|
damageIncomeModifierEffectInstance = GetComponent<DamageIncomeModifierEffectInstance>();
|
|
|
|
baseRegenValue = character.HealthRegen.BaseValue;
|
|
|
|
baseMaxValue = character.MaxHealth.BaseValue;
|
|
|
|
character.onUpdateStatValues.AddListener(CalculateMaxValueBasedOnStat);
|
|
}
|
|
|
|
protected override void Start()
|
|
{
|
|
base.Start();
|
|
}
|
|
|
|
public bool EnoughHealth(float cost)
|
|
{
|
|
if (noCost || invulnerable) return true;
|
|
|
|
return cost <= currentValue;
|
|
}
|
|
|
|
public void SetInvulnerabilityState(bool isInvulnerable)
|
|
{
|
|
invulnerable = isInvulnerable;
|
|
OnInvulnerabilityStateChanged.Invoke(invulnerable);
|
|
}
|
|
|
|
|
|
protected void HandleDamageIncomeModifierEffects()
|
|
{
|
|
if (incomingValue < 0)
|
|
{
|
|
|
|
if (damageIncomeModifierEffectInstance.IsActive)
|
|
{
|
|
//Debug.Log("Incoming damage b4 mitigation: " + incomingValue);
|
|
|
|
incomingValue = damageIncomeModifierEffectInstance.ModifyDamageIncome(incomingValue);
|
|
|
|
if (incomingValue > 0) //avoid damage ultra mitigated turning into healing
|
|
incomingValue = 0;
|
|
}
|
|
//Debug.Log("Incoming damage after mitigation: " + incomingValue);
|
|
}
|
|
}
|
|
|
|
protected bool HasDodged()
|
|
{
|
|
return MathHelpers.RollChance(MathHelpers.NormalizePercentageDecimal(character.DodgeChance.Value));
|
|
}
|
|
protected bool HasBlocked()
|
|
{
|
|
return MathHelpers.RollChance(MathHelpers.NormalizePercentageDecimal(character.BlockChance.Value));
|
|
}
|
|
|
|
protected void HandleBlockMitigation()
|
|
{
|
|
if (incomingValue >= 0) return;
|
|
|
|
Debug.Log("Blocked!");
|
|
|
|
if (scrollingTextPrefab != null)
|
|
{
|
|
scrollingText = GameObjectPoolManager.Instance.Get(scrollingTextPrefab.gameObject, this.transform.position + new Vector3(Random.Range(-0.5f, 0.5f), 2f, Random.Range(-0.5f, 0.5f)), Quaternion.identity).GetComponent<ScrollingText>();
|
|
|
|
scrollingText.ShowBlock("Blocked!");
|
|
}
|
|
|
|
percentStatMitigation = MathHelpers.NormalizePercentageDecimal(character.BlockEffectiveness.Value);
|
|
|
|
percentStatMitigation = Mathf.Clamp(percentStatMitigation, 0, GameConstants.CharacterStatsBalancing.MaximumPercentDamageReductionFromBlock);
|
|
|
|
reducedDamage = incomingValue * percentStatMitigation;
|
|
incomingValue += Mathf.Abs(reducedDamage);
|
|
|
|
if (incomingValue > 0) //avoid damage ultra mitigated turning into healing
|
|
incomingValue = 0;
|
|
|
|
}
|
|
|
|
protected void HandleStatMitigation(DamageType dmgType)
|
|
{
|
|
if (incomingValue < 0)
|
|
{
|
|
switch (dmgType)
|
|
{
|
|
case DamageType.Attack:
|
|
default:
|
|
{
|
|
percentStatMitigation = Mathf.Clamp((character.Armor.Value * GameConstants.CharacterStatsBalancing.PercentArmorIntoDamageReduction), 0,GameConstants.CharacterStatsBalancing.MaximumPercentDamageReductionFromArmor);
|
|
}
|
|
break;
|
|
case DamageType.Spell:
|
|
{
|
|
percentStatMitigation = Mathf.Clamp((character.MagicResistance.Value * GameConstants.CharacterStatsBalancing.PercentMagicResistanceIntoDamageReduction), 0, GameConstants.CharacterStatsBalancing.MaximumPercentDamageReductionFromMagicResistance);
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
//Debug.Log(gameObject.name + " mitigating = " + percentStatMitigation + " percent");
|
|
reducedDamage = incomingValue * percentStatMitigation;
|
|
incomingValue += Mathf.Abs(reducedDamage);
|
|
//Debug.Log(gameObject.name + " receiving dmg = " + incomingValue + $" after mitigations from {(dmgType == DamageType.Attack ? nameof(character.Armor) : nameof(character.MagicResistance))}");
|
|
|
|
if (incomingValue > 0) //avoid damage ultra mitigated turning into healing
|
|
incomingValue = 0;
|
|
}
|
|
}
|
|
|
|
protected void HandleAbsorbEffects()
|
|
{
|
|
if (incomingValue < 0)
|
|
{
|
|
if (absorbEffectInstance.IsActive)
|
|
{
|
|
//Debug.Log("Incoming damage b4 absorbs: " + incomingValue);
|
|
|
|
incomingValue = absorbEffectInstance.AbsorbDamage(incomingValue);
|
|
|
|
if (incomingValue > 0) //avoid complete absorbs turning into healing
|
|
incomingValue = 0;
|
|
}
|
|
//Debug.Log("Incoming damage after absorbs: " + incomingValue);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public override void ChangeValue(float value)
|
|
{
|
|
//Debug.Log("Value to change: " + value);
|
|
|
|
if (isDead) return;
|
|
|
|
incomingValue = value;
|
|
|
|
if (invulnerable && incomingValue < 0) return;
|
|
|
|
HandleDamageIncomeModifierEffects();
|
|
|
|
HandleAbsorbEffects();
|
|
|
|
currentValue += incomingValue;
|
|
|
|
currentValue = Mathf.Clamp(currentValue, 0, maxValue);
|
|
if (currentValue == 0)
|
|
{
|
|
//dead;
|
|
onDeath.Invoke();
|
|
}
|
|
//Debug.Log("CurrentHealth: " + currentValue);
|
|
onResourceChanged.Invoke(currentValue);
|
|
}
|
|
|
|
public void ChangeValueDirect(float value)
|
|
{
|
|
//Debug.Log("Value to change: " + value);
|
|
|
|
if (isDead) return;
|
|
|
|
incomingValue = value;
|
|
|
|
if (invulnerable && incomingValue < 0) return;
|
|
|
|
currentValue += incomingValue;
|
|
|
|
currentValue = Mathf.Clamp(currentValue, 0, maxValue);
|
|
if (currentValue == 0)
|
|
{
|
|
//dead;
|
|
onDeath.Invoke();
|
|
}
|
|
//Debug.Log("CurrentHealth: " + currentValue);
|
|
onResourceChanged.Invoke(currentValue);
|
|
}
|
|
|
|
public void ChangeValue(float value, int dmgType, bool isCrit)
|
|
{
|
|
//Debug.Log("Value to change: " + value);
|
|
|
|
if (isDead) return;
|
|
|
|
incomingValue = value;
|
|
|
|
//Debug.Log(gameObject.name + " receiving dmg = " + incomingValue + " before mitigations " + (DamageType)dmgType);
|
|
|
|
if (invulnerable && incomingValue < 0)
|
|
{
|
|
if (scrollingTextPrefab != null)
|
|
{
|
|
scrollingText = GameObjectPoolManager.Instance.Get(scrollingTextPrefab.gameObject, this.transform.position + new Vector3(Random.Range(-0.5f, 0.5f), 2f, Random.Range(-0.5f, 0.5f)), Quaternion.identity).GetComponent<ScrollingText>();
|
|
|
|
scrollingText.Show("Invulnerable", Color.gray, 1.5f);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (incomingValue < 0)
|
|
{
|
|
HandleNegativeValue(dmgType, isCrit);
|
|
}
|
|
else
|
|
{
|
|
HandlePositiveValue();
|
|
}
|
|
}
|
|
|
|
protected void HandleNegativeValue(int dmgType, bool isCrit)
|
|
{
|
|
if (incomingValue >= 0) return;
|
|
|
|
if (HasDodged())
|
|
{
|
|
Debug.Log("Dodged!");
|
|
|
|
if (scrollingTextPrefab != null)
|
|
{
|
|
scrollingText = GameObjectPoolManager.Instance.Get(scrollingTextPrefab.gameObject, this.transform.position + new Vector3(Random.Range(-0.5f, 0.5f), 2f, Random.Range(-0.5f, 0.5f)), Quaternion.identity).GetComponent<ScrollingText>();
|
|
|
|
scrollingText.ShowDodge("Dodge!");
|
|
}
|
|
|
|
OnDodgedSuccessfully.Invoke();
|
|
return;
|
|
}
|
|
|
|
HandleDamageIncomeModifierEffects();
|
|
|
|
if (HasBlocked())
|
|
HandleBlockMitigation();
|
|
|
|
HandleStatMitigation((DamageType)dmgType);
|
|
|
|
HandleAbsorbEffects();
|
|
|
|
currentValue += incomingValue;
|
|
|
|
currentValue = Mathf.Clamp(currentValue, 0, maxValue);
|
|
|
|
if (scrollingTextPrefab != null)
|
|
{
|
|
scrollingText = GameObjectPoolManager.Instance.Get(scrollingTextPrefab.gameObject, this.transform.position + new Vector3(Random.Range(-0.5f, 0.5f), 2f, Random.Range(-0.5f, 0.5f)), Quaternion.identity).GetComponent<ScrollingText>();
|
|
|
|
if (isCrit)
|
|
scrollingText.ShowCrit(incomingValue.ToString("0.0"));
|
|
else
|
|
scrollingText.ShowHit(incomingValue.ToString("0.0"));
|
|
|
|
}
|
|
|
|
if (currentValue == 0)
|
|
{
|
|
//dead;
|
|
onDeath.Invoke();
|
|
}
|
|
//Debug.Log("CurrentHealth: " + currentValue);
|
|
onResourceChanged.Invoke(currentValue);
|
|
}
|
|
|
|
protected void HandlePositiveValue()
|
|
{
|
|
currentValue += incomingValue;
|
|
|
|
currentValue = Mathf.Clamp(currentValue, 0, maxValue);
|
|
|
|
if (scrollingTextPrefab != null && incomingValue != 0)
|
|
{
|
|
scrollingText = GameObjectPoolManager.Instance.Get(scrollingTextPrefab.gameObject, this.transform.position + new Vector3(Random.Range(-0.5f, 0.5f), 2f, Random.Range(-0.5f, 0.5f)), Quaternion.identity).GetComponent<ScrollingText>();
|
|
|
|
scrollingText.ShowHeal(incomingValue.ToString("0.0"));
|
|
}
|
|
|
|
if (currentValue == 0)
|
|
{
|
|
//dead;
|
|
onDeath.Invoke();
|
|
}
|
|
//Debug.Log("CurrentHealth: " + currentValue);
|
|
onResourceChanged.Invoke(currentValue);
|
|
}
|
|
|
|
public override float GetMaxValue()
|
|
{
|
|
return base.GetMaxValue();
|
|
}
|
|
|
|
public virtual void CalculateMaxValueBasedOnStat()
|
|
{
|
|
maxValue = character.MaxHealth.Value;
|
|
|
|
CalculateRegenValueBasedOnStat();
|
|
|
|
onMaxHealthChanged.Invoke(maxValue);
|
|
}
|
|
public void CalculateRegenValueBasedOnStat()
|
|
{
|
|
flatRegen = character.HealthRegen.Value;
|
|
}
|
|
|
|
public override void SetMaxValue(float value)
|
|
{
|
|
base.SetMaxValue(value);
|
|
|
|
onMaxHealthChanged.Invoke(maxValue);
|
|
}
|
|
|
|
|
|
}
|