2025-09-27 22:33:47 +01:00

356 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>();
character.onAllStatsUpdated.AddListener(CalculateMaxValueBasedOnStat);
}
protected override void Start()
{
baseRegenValue = character.GetStat("healthregen").BaseValue;
baseMaxValue = character.GetStat("maxhealth").BaseValue;
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.RollChancePercent(character.GetStat("dodgechance").Value);
}
protected bool HasBlocked()
{
return MathHelpers.RollChancePercent(character.GetStat("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.GetStat("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.GetStat("armor").Value * GameConstants.CharacterStatsBalancing.PercentArmorIntoDamageReduction), 0,GameConstants.CharacterStatsBalancing.MaximumPercentDamageReductionFromArmor);
}
break;
case DamageType.Spell:
{
percentStatMitigation = Mathf.Clamp((character.GetStat("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.GetStat("maxhealth").Value;
CalculateRegenValueBasedOnStat();
onMaxHealthChanged.Invoke(maxValue);
}
public void CalculateRegenValueBasedOnStat()
{
flatRegen = character.GetStat("healthregen").Value;
}
public override void SetMaxValue(float value)
{
base.SetMaxValue(value);
onMaxHealthChanged.Invoke(maxValue);
}
}