using UnityEngine; using UnityEngine.InputSystem; using System.Collections; using System.Collections.Generic; namespace BulletHellTemplate { /// /// Handles skill activation for PC using keyboard and mouse inputs. /// Manages skill activation, aiming, cooldowns, and prevents skill spamming by enforcing a delay between skill uses. /// public class PCSkillController : MonoBehaviour { [Tooltip("Mappings for each skill's keyboard and mouse buttons")] public List skillKeyMappings; [Tooltip("Delay before allowing the next skill to be used to prevent spamming.")] public float delayToNextSkill = 0.3f; private bool[] isSkillActive; // Tracks whether a skill is being held private bool[] isOnCooldown; // Tracks whether a skill is on cooldown private float[] cooldownTime; // Initial cooldown times per skill private float[] currentCooldown; // Current cooldowns per skill private CharacterEntity characterEntity; // Reference to the character entity private bool canUseSkill = true; // Flag to determine if a new skill can be used private void Awake() { int skillCount = skillKeyMappings.Count; isSkillActive = new bool[skillCount]; isOnCooldown = new bool[skillCount]; cooldownTime = new float[skillCount]; currentCooldown = new float[skillCount]; } private void Start() { characterEntity = UIGameplay.Singleton.GetCharacterEntity(); if (characterEntity == null) { Debug.LogError("CharacterEntity is not assigned or missing."); } for (int i = 0; i < skillKeyMappings.Count; i++) { int skillIndex = skillKeyMappings[i].skillIndex; cooldownTime[i] = characterEntity.GetSkillCooldown(skillIndex); currentCooldown[i] = 0f; isOnCooldown[i] = false; } } private void Update() { if (characterEntity == null) return; for (int i = 0; i < skillKeyMappings.Count; i++) { UpdateSkillCooldown(i); CheckInputForSkill(i); } } /// /// Sets up the character entity and initializes skill cooldown data. /// /// The character entity to associate with this controller. public void Setup(CharacterEntity entity) { characterEntity = entity; for (int i = 0; i < skillKeyMappings.Count; i++) { int skillIndex = skillKeyMappings[i].skillIndex; cooldownTime[i] = characterEntity.GetSkillCooldown(skillIndex); currentCooldown[i] = 0f; isOnCooldown[i] = false; } } /// /// Retrieves the current cooldown for a specified skill. /// /// The skill index to query. /// The current cooldown time remaining for the skill. public float GetCurrentCooldown(int skillIndex) { int mappingIndex = skillKeyMappings.FindIndex(mapping => mapping.skillIndex == skillIndex); if (mappingIndex >= 0) { return currentCooldown[mappingIndex]; } else { return 0f; } } /// /// Retrieves the initial cooldown time for a specified skill. /// /// The skill index to query. /// The initial cooldown time of the skill. public float GetCooldownTime(int skillIndex) { int mappingIndex = skillKeyMappings.FindIndex(mapping => mapping.skillIndex == skillIndex); if (mappingIndex >= 0) { return cooldownTime[mappingIndex]; } else { return 0f; } } /// /// Checks input for skill activation, holding, and release events. /// private void CheckInputForSkill(int index) { if (GameplayManager.Singleton != null && GameplayManager.Singleton.IsPaused()) return; SkillKeyMapping skillMapping = skillKeyMappings[index]; foreach (Key key in skillMapping.keyboardKeys) { if (key == Key.None) continue; if (Keyboard.current[key].wasPressedThisFrame) ActivateSkill(index); else if (Keyboard.current[key].isPressed) UpdateSkillAim(index); else if (Keyboard.current[key].wasReleasedThisFrame) ReleaseSkill(index); } foreach (MouseButton mouseButton in skillMapping.mouseButtons) { if (IsMouseButtonPressed(mouseButton)) ActivateSkill(index); else if (IsMouseButtonHeld(mouseButton)) UpdateSkillAim(index); else if (IsMouseButtonReleased(mouseButton)) ReleaseSkill(index); } } /// /// Activates the skill if not on cooldown, if a skill can be used, and enables directional aiming. /// private void ActivateSkill(int index) { if (!isSkillActive[index] && !isOnCooldown[index] && canUseSkill) { int skillIndex = skillKeyMappings[index].skillIndex; if (characterEntity.GetSkillData(skillIndex).manaCost > characterEntity.GetCurrentMP()) { characterEntity.DisplayMPInsufficientMessage("Not enough MP to use skill"); UIGameplay.Singleton.DisplayMPInsufficientMessage("Not enough MP to use skill"); return; } isSkillActive[index] = true; characterEntity.SetDirectionalAimActive(true, index,true); // Enable directional aiming canUseSkill = false; // Prevent other skills from being activated immediately StartCoroutine(SkillDelay()); // Start the delay coroutine } } /// /// Coroutine to handle the delay before allowing the next skill to be used. /// private IEnumerator SkillDelay() { yield return new WaitForSeconds(delayToNextSkill); canUseSkill = true; } /// /// Updates the aiming direction while a skill is being held. /// private void UpdateSkillAim(int index) { if (isSkillActive[index]) { Vector2 direction = GetMouseDirection(); characterEntity.UpdateDirectionalAim(direction, index); } } /// /// Releases the skill, triggers its use, starts cooldown, and disables directional aiming. /// private void ReleaseSkill(int index) { if (isSkillActive[index]) { Vector2 direction = GetMouseDirection(); int skillMappingIndex = skillKeyMappings[index].skillIndex; characterEntity.UseSkill(skillMappingIndex, direction); StartCooldown(index); isSkillActive[index] = false; characterEntity.SetDirectionalAimActive(false, index, true); // Disable directional aiming } } /// /// Starts the cooldown for a specific skill, adjusting for any cooldown reduction. /// private void StartCooldown(int index) { isOnCooldown[index] = true; float cooldownReduction = characterEntity.GetCurrentCooldownReduction() / 100f; currentCooldown[index] = cooldownTime[index] * (1 - cooldownReduction); } /// /// Updates the cooldown timer for a specific skill. /// private void UpdateSkillCooldown(int index) { if (GameplayManager.Singleton != null && GameplayManager.Singleton.IsPaused()) return; if (isOnCooldown[index]) { currentCooldown[index] -= Time.deltaTime; if (currentCooldown[index] <= 0f) { currentCooldown[index] = 0f; isOnCooldown[index] = false; } } } /// /// Calculates the aiming direction based on the mouse position. /// private Vector2 GetMouseDirection() { Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue()); Plane plane = new Plane(Vector3.up, characterEntity.transform.position); if (plane.Raycast(ray, out float distance)) { Vector3 point = ray.GetPoint(distance); Vector3 direction = point - characterEntity.transform.position; return new Vector2(direction.x, direction.z).normalized; } return Vector2.zero; } private bool IsMouseButtonPressed(MouseButton mouseButton) { return mouseButton switch { MouseButton.Left => Mouse.current.leftButton.wasPressedThisFrame, MouseButton.Right => Mouse.current.rightButton.wasPressedThisFrame, MouseButton.Middle => Mouse.current.middleButton.wasPressedThisFrame, _ => false, }; } private bool IsMouseButtonHeld(MouseButton mouseButton) { return mouseButton switch { MouseButton.Left => Mouse.current.leftButton.isPressed, MouseButton.Right => Mouse.current.rightButton.isPressed, MouseButton.Middle => Mouse.current.middleButton.isPressed, _ => false, }; } private bool IsMouseButtonReleased(MouseButton mouseButton) { return mouseButton switch { MouseButton.Left => Mouse.current.leftButton.wasReleasedThisFrame, MouseButton.Right => Mouse.current.rightButton.wasReleasedThisFrame, MouseButton.Middle => Mouse.current.middleButton.wasReleasedThisFrame, _ => false, }; } } [System.Serializable] public class SkillKeyMapping { public int skillIndex; public List keyboardKeys = new List { Key.None }; public List mouseButtons = new List(); } public enum MouseButton { Left, Right, Middle } }