337 lines
11 KiB
C#
337 lines
11 KiB
C#
using Kryz.CharacterStats.Examples;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class AbilityKeyBinder : MonoBehaviour
|
|
{
|
|
[Header("Listeners:")]
|
|
[SerializeField] private GameEventListener onLocalPlayerFainted;
|
|
[SerializeField] private GameEventListener onLocalPlayerPermaDeath;
|
|
[SerializeField] private GameEventListener onLocalPlayerRevived;
|
|
[Space]
|
|
//[SerializeField] private BaseAbility ability;
|
|
[SerializeField] private GameKey key;
|
|
[SerializeField] private CastingStateController castingStateController;
|
|
[SerializeField] private GameEvent_AbilityKeyBinder onAbilityKeyBinderSpawned;
|
|
[SerializeField] private GameInputBinding gamepadBinding; // New field for gamepad binding
|
|
|
|
[SerializeField] private BaseAbility ability;
|
|
|
|
private Taggable userTag;
|
|
private Mana mana;
|
|
private Health health;
|
|
public Health Health => health;
|
|
public Mana Mana => mana;
|
|
|
|
public BaseAbility Ability => ability;
|
|
public GameKey GameKey => key;
|
|
|
|
Coroutine currentChanneling;
|
|
NetworkedChanneling networkedChanneling;
|
|
AbilityBindInstance abilityBindInstance;
|
|
AbilityCooldownTracker cooldownTracker;
|
|
|
|
bool isDead = false;
|
|
bool abilitySlotUnlocked = false;
|
|
bool isComboAbility = false;
|
|
ComboAbility combo;
|
|
List<BaseAbility> comboAbilities = new List<BaseAbility>();
|
|
List<BaseEffect> effects = new List<BaseEffect>();
|
|
ComboState comboState = new ComboState();
|
|
|
|
float finalHealthCost;
|
|
float finalManaCost;
|
|
|
|
private void Awake()
|
|
{
|
|
userTag = GetComponentInParent<Taggable>();
|
|
mana = GetComponentInParent<Mana>();
|
|
health = GetComponentInParent<Health>();
|
|
cooldownTracker = userTag.GetComponentInChildren<AbilityCooldownTracker>();
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
onLocalPlayerFainted.Response.AddListener(() =>
|
|
{
|
|
isDead = true;
|
|
});
|
|
onLocalPlayerPermaDeath.Response.AddListener(() =>
|
|
{
|
|
isDead = true;
|
|
});
|
|
onLocalPlayerRevived.Response.AddListener(() =>
|
|
{
|
|
isDead = false;
|
|
});
|
|
|
|
onAbilityKeyBinderSpawned.Raise(this);
|
|
}
|
|
bool isInputPressed;
|
|
bool isInputReleased;
|
|
void Update()
|
|
{
|
|
if (isDead) return;
|
|
|
|
if (!abilitySlotUnlocked && ability.animationType != AbilityAnimationType.Potion) return;
|
|
|
|
// Check for input using both keyboard and gamepad
|
|
isInputPressed =
|
|
(key != null && Input.GetKeyDown(key.keyCode)) ||
|
|
(!string.IsNullOrEmpty(gamepadBinding.bindingName) && GameInputManager.Instance.GetButtonDown(gamepadBinding.bindingName));
|
|
|
|
isInputReleased =
|
|
(key != null && Input.GetKeyUp(key.keyCode)) ||
|
|
(!string.IsNullOrEmpty(gamepadBinding.bindingName) && GameInputManager.Instance.GetButtonUp(gamepadBinding.bindingName));
|
|
|
|
if (isInputPressed)
|
|
{
|
|
if (abilityBindInstance != null && abilityBindInstance.pressed != null)
|
|
abilityBindInstance.pressed.SetActive(true);
|
|
|
|
if (IsAbilityOffCooldown() && mana.EnoughMana(ability.GetFinalManaCost(mana)) && health.EnoughHealth(ability.GetFinalHealthCost(health)))
|
|
{
|
|
if (ability is ChanneledAbility)
|
|
{
|
|
castingStateController.RequestAbilityChannel(ability, () =>
|
|
{
|
|
networkedChanneling = ((ChanneledAbility)ability).ExecuteChannel(userTag, ref currentChanneling);
|
|
if (ability.cooldown > 0)
|
|
{
|
|
cooldownTracker.StartAbilityCooldown(ability);
|
|
abilityBindInstance.StartCooldownTrackerUI();
|
|
}
|
|
});
|
|
}
|
|
else if (isComboAbility)
|
|
{
|
|
if (mana.EnoughMana(GetCurrentAbility().GetFinalManaCost(mana)) && health.EnoughHealth(GetCurrentAbility().GetFinalHealthCost(health)))
|
|
{
|
|
castingStateController.RequestAbilityCast(GetCurrentAbility(), () =>
|
|
{
|
|
GetCurrentAbility().Execute(userTag);
|
|
AdvanceCombo();
|
|
});
|
|
}
|
|
else
|
|
{
|
|
ResetCombo();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
castingStateController.RequestAbilityCast(ability, () =>
|
|
{
|
|
ability.Execute(userTag);
|
|
if (ability.cooldown > 0)
|
|
{
|
|
cooldownTracker.StartAbilityCooldown(ability);
|
|
abilityBindInstance.StartCooldownTrackerUI();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isInputReleased)
|
|
{
|
|
if (abilityBindInstance != null && abilityBindInstance.pressed != null)
|
|
abilityBindInstance.pressed.SetActive(false);
|
|
|
|
if (currentChanneling != null)
|
|
{
|
|
if (networkedChanneling != null)
|
|
{
|
|
networkedChanneling.StopCoroutine(currentChanneling);
|
|
}
|
|
else
|
|
{
|
|
StopCoroutine(currentChanneling);
|
|
}
|
|
castingStateController.ResetChannelingCast();
|
|
CastBarHandler.Instance.CancelChannelingOnButtonReleased();
|
|
}
|
|
|
|
if (networkedChanneling != null)
|
|
{
|
|
networkedChanneling.channeling = false;
|
|
networkedChanneling.DisableVisuals();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetupAbilityBindInstance(AbilityBindInstance abilityBindInstance)
|
|
{
|
|
this.abilityBindInstance = abilityBindInstance;
|
|
mana.onResourceChanged.AddListener(OnManaChanged);
|
|
health.onResourceChanged.AddListener(OnHealthChanged);
|
|
|
|
if (isComboAbility)
|
|
abilityBindInstance.ForceUpdateOnComboAbility(GetCurrentAbility());
|
|
}
|
|
|
|
public void OnManaChanged(float currentMana)
|
|
{
|
|
if (isComboAbility)
|
|
{
|
|
finalManaCost = GetCurrentAbility().GetFinalManaCost(mana);
|
|
}
|
|
else
|
|
{
|
|
finalManaCost = ability.GetFinalManaCost(mana);
|
|
}
|
|
if (abilityBindInstance.manaCost != null)
|
|
abilityBindInstance.manaCost.text = finalManaCost.ToString("F0");
|
|
|
|
if (abilityBindInstance.noMana != null)
|
|
abilityBindInstance.noMana.SetActive(!mana.EnoughMana(finalManaCost));
|
|
}
|
|
|
|
public void OnHealthChanged(float currentHealth)
|
|
{
|
|
if (isComboAbility)
|
|
{
|
|
finalHealthCost = GetCurrentAbility().GetFinalHealthCost(health);
|
|
}
|
|
else
|
|
{
|
|
finalHealthCost = ability.GetFinalHealthCost(health);
|
|
}
|
|
|
|
if (abilityBindInstance.healthCost != null)
|
|
abilityBindInstance.healthCost.text = finalHealthCost.ToString("F0");
|
|
|
|
if (abilityBindInstance.healthCostGO != null)
|
|
abilityBindInstance.healthCostGO.SetActive(finalHealthCost > 0);
|
|
|
|
if (abilityBindInstance.noHealth != null)
|
|
abilityBindInstance.noHealth.SetActive(!health.EnoughHealth(finalHealthCost));
|
|
}
|
|
|
|
public bool IsAbilityOffCooldown()
|
|
{
|
|
return ability.cooldown <= 0 || !cooldownTracker.OnCooldown(ability);
|
|
}
|
|
|
|
public void BindAbility(BaseAbility ability)
|
|
{
|
|
if (this.ability != null && this.ability.name.ToLower().Contains("clone"))
|
|
CleanupAbilityAndEffectsClones(); //cleanup before rebind
|
|
|
|
this.ability = ability.CreateRuntimeInstance();
|
|
|
|
if (this.ability is ComboAbility comboAbility)
|
|
{
|
|
isComboAbility = true;
|
|
combo = comboAbility;
|
|
for (int i = 0; i < combo.comboChain.Count; i++)
|
|
{
|
|
effects.Clear();
|
|
comboAbilities.Add(combo.comboChain[i].CreateRuntimeInstance());
|
|
for (int j = 0; j < combo.comboChain[i].abilityEffects.Count; j++)
|
|
{
|
|
effects.Add(combo.comboChain[i].abilityEffects[j].CreateRuntimeInstance());
|
|
}
|
|
comboAbilities[i].abilityEffects = new List<BaseEffect>(effects);
|
|
}
|
|
combo.comboChain = comboAbilities;
|
|
abilityBindInstance.ForceUpdateOnComboAbility(GetCurrentAbility());
|
|
}
|
|
else
|
|
{
|
|
isComboAbility = false;
|
|
combo = null;
|
|
|
|
for (int i = 0; i < ability.abilityEffects.Count; i++)
|
|
{
|
|
effects.Add(ability.abilityEffects[i].CreateRuntimeInstance());
|
|
}
|
|
this.ability.abilityEffects = effects;
|
|
|
|
}
|
|
onAbilityKeyBinderSpawned.Raise(this);
|
|
}
|
|
|
|
public void SetUnlockAbilitySlot(bool unlocked)
|
|
{
|
|
abilitySlotUnlocked = unlocked;
|
|
abilityBindInstance.SetUnlocked(unlocked);
|
|
}
|
|
|
|
private void OnDestroy() //cleanup all clone effects and ability scriptables
|
|
{
|
|
CleanupAbilityAndEffectsClones();
|
|
|
|
}
|
|
|
|
private void CleanupAbilityAndEffectsClones()
|
|
{
|
|
if (isComboAbility)
|
|
{
|
|
for (int i = comboAbilities.Count - 1; i >= 0; i--)
|
|
{
|
|
for (int j = comboAbilities[i].abilityEffects.Count - 1; j >= 0; j--)
|
|
{
|
|
if (comboAbilities[i].abilityEffects[j].name.ToLower().Contains("clone"))
|
|
Destroy(comboAbilities[i].abilityEffects[j]);
|
|
}
|
|
if (comboAbilities[i].name.ToLower().Contains("clone"))
|
|
{
|
|
Destroy(comboAbilities[i]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = this.ability.abilityEffects.Count - 1; i >= 0; i--)
|
|
{
|
|
if (this.ability.abilityEffects[i].name.ToLower().Contains("clone"))
|
|
Destroy(this.ability.abilityEffects[i]);
|
|
}
|
|
|
|
effects.Clear();
|
|
|
|
if (this.ability != null && this.ability.name.ToLower().Contains("clone"))
|
|
Destroy(this.ability);
|
|
|
|
}
|
|
}
|
|
|
|
private void AdvanceCombo()
|
|
{
|
|
comboState.currentComboIndex++;
|
|
comboState.currentComboIndex %= comboAbilities.Count;
|
|
|
|
abilityBindInstance.ForceUpdateOnComboAbility(GetCurrentAbility());
|
|
}
|
|
private void ResetCombo()
|
|
{
|
|
comboState.currentComboIndex = 0;
|
|
abilityBindInstance.ForceUpdateOnComboAbility(GetCurrentAbility());
|
|
}
|
|
|
|
// Public methods for external access (useful for UI or debugging)
|
|
public int GetCurrentComboIndex()
|
|
{
|
|
return comboState.currentComboIndex;
|
|
}
|
|
|
|
public BaseAbility GetCurrentAbility()
|
|
{
|
|
if (comboAbilities.Count == 0) return null;
|
|
|
|
int index = GetCurrentComboIndex();
|
|
return comboAbilities[index];
|
|
}
|
|
public int GetComboLength()
|
|
{
|
|
return comboAbilities.Count;
|
|
}
|
|
|
|
public List<BaseAbility> GetComboChain()
|
|
{
|
|
return new List<BaseAbility>(combo.comboChain); // Return copy to prevent external modification
|
|
}
|
|
}
|