182 lines
5.8 KiB
C#
182 lines
5.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using OTGIntegrated.Core;
|
|
using OTGIntegrated.Player;
|
|
using OTGIntegrated.Stats;
|
|
using UnityEngine;
|
|
|
|
namespace OTGIntegrated.Abilities
|
|
{
|
|
public sealed class AbilityManager : MonoBehaviour
|
|
{
|
|
public List<AbilityData> learnedAbilities = new List<AbilityData>();
|
|
public Transform castOrigin;
|
|
|
|
private readonly List<float> cooldownRemaining = new List<float>();
|
|
private PlayerStats playerStats;
|
|
private PlayerController playerController;
|
|
|
|
public event Action OnAbilitiesChanged;
|
|
public IReadOnlyList<AbilityData> LearnedAbilities => learnedAbilities;
|
|
|
|
public void Initialize(PlayerStats stats)
|
|
{
|
|
playerStats = stats != null ? stats : GetComponent<PlayerStats>();
|
|
playerController = GetComponent<PlayerController>();
|
|
if (castOrigin == null)
|
|
{
|
|
castOrigin = transform;
|
|
}
|
|
|
|
EnsureCooldownSlots();
|
|
OnAbilitiesChanged?.Invoke();
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
if (playerStats == null)
|
|
{
|
|
playerStats = GetComponent<PlayerStats>();
|
|
}
|
|
|
|
playerController = GetComponent<PlayerController>();
|
|
EnsureCooldownSlots();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
TickCooldowns();
|
|
|
|
if (playerController != null && !playerController.InputEnabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Input.GetKeyDown(KeyCode.Alpha1)) TryUseAbility(0);
|
|
if (Input.GetKeyDown(KeyCode.Alpha2)) TryUseAbility(1);
|
|
if (Input.GetKeyDown(KeyCode.Alpha3)) TryUseAbility(2);
|
|
}
|
|
|
|
public bool TryUseAbility(int index)
|
|
{
|
|
EnsureCooldownSlots();
|
|
if (index < 0 || index >= learnedAbilities.Count)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
AbilityData ability = learnedAbilities[index];
|
|
if (ability == null || playerStats == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (cooldownRemaining[index] > 0f)
|
|
{
|
|
GameEvents.RaiseNotification("Способность на перезарядке");
|
|
return false;
|
|
}
|
|
|
|
if (!playerStats.SpendMana(ability.manaCost))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ability.effectPrefab == null)
|
|
{
|
|
playerStats.RestoreMana(ability.manaCost);
|
|
Debug.LogWarning($"Ability has no effect prefab: {ability.displayName}", ability);
|
|
return false;
|
|
}
|
|
|
|
Vector3 forward = GetCastForward();
|
|
Vector3 origin = castOrigin != null ? castOrigin.position : transform.position + Vector3.up;
|
|
GameObject effectObject = Instantiate(ability.effectPrefab, origin, Quaternion.LookRotation(forward, Vector3.up));
|
|
AbilityEffect effect = effectObject.GetComponent<AbilityEffect>();
|
|
if (effect == null)
|
|
{
|
|
playerStats.RestoreMana(ability.manaCost);
|
|
Destroy(effectObject);
|
|
Debug.LogWarning($"Ability effect prefab misses AbilityEffect: {ability.effectPrefab.name}", ability.effectPrefab);
|
|
return false;
|
|
}
|
|
|
|
effect.Activate(new AbilityContext(ability, transform, playerStats, origin, forward));
|
|
cooldownRemaining[index] = GetEffectiveCooldown(ability);
|
|
GameEvents.RaiseAbilityUsed(ability);
|
|
GameEvents.RaiseNotification($"Способность: {ability.displayName}");
|
|
OnAbilitiesChanged?.Invoke();
|
|
return true;
|
|
}
|
|
|
|
public float GetCooldownRemaining(int index)
|
|
{
|
|
EnsureCooldownSlots();
|
|
return index >= 0 && index < cooldownRemaining.Count ? cooldownRemaining[index] : 0f;
|
|
}
|
|
|
|
public float GetCooldownNormalized(int index)
|
|
{
|
|
if (index < 0 || index >= learnedAbilities.Count || learnedAbilities[index] == null)
|
|
{
|
|
return 0f;
|
|
}
|
|
|
|
float cooldown = GetEffectiveCooldown(learnedAbilities[index]);
|
|
return cooldown <= 0f ? 0f : Mathf.Clamp01(GetCooldownRemaining(index) / cooldown);
|
|
}
|
|
|
|
private float GetEffectiveCooldown(AbilityData ability)
|
|
{
|
|
float reduction = playerStats != null ? playerStats.CooldownReduction : 0f;
|
|
return Mathf.Max(0f, ability.cooldown * (1f - Mathf.Clamp(reduction, 0f, 0.9f)));
|
|
}
|
|
|
|
private void TickCooldowns()
|
|
{
|
|
EnsureCooldownSlots();
|
|
bool changed = false;
|
|
for (int i = 0; i < cooldownRemaining.Count; i++)
|
|
{
|
|
if (cooldownRemaining[i] <= 0f)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
cooldownRemaining[i] = Mathf.Max(0f, cooldownRemaining[i] - Time.deltaTime);
|
|
changed = true;
|
|
}
|
|
|
|
if (changed)
|
|
{
|
|
OnAbilitiesChanged?.Invoke();
|
|
}
|
|
}
|
|
|
|
private void EnsureCooldownSlots()
|
|
{
|
|
while (cooldownRemaining.Count < learnedAbilities.Count)
|
|
{
|
|
cooldownRemaining.Add(0f);
|
|
}
|
|
|
|
while (cooldownRemaining.Count > learnedAbilities.Count)
|
|
{
|
|
cooldownRemaining.RemoveAt(cooldownRemaining.Count - 1);
|
|
}
|
|
}
|
|
|
|
private Vector3 GetCastForward()
|
|
{
|
|
if (playerController != null)
|
|
{
|
|
return playerController.GetAimForward();
|
|
}
|
|
|
|
Vector3 forward = transform.forward;
|
|
forward.y = 0f;
|
|
return forward.sqrMagnitude > 0.001f ? forward.normalized : Vector3.forward;
|
|
}
|
|
}
|
|
}
|