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 comboAbilities = new List(); List effects = new List(); ComboState comboState = new ComboState(); float finalHealthCost; float finalManaCost; private void Awake() { userTag = GetComponentInParent(); mana = GetComponentInParent(); health = GetComponentInParent(); cooldownTracker = userTag.GetComponentInChildren(); } 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(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 GetComboChain() { return new List(combo.comboChain); // Return copy to prevent external modification } }