You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
HighGroundRoyaleNetcode/Assets/Scripts/Gameplay/AbilitySystem.cs

358 lines
13 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.BossRoom.Gameplay.GameplayObjects;
using Unity.BossRoom.Gameplay.GameplayObjects.Character;
using Unity.Multiplayer.Samples.BossRoom;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.AI;
//using static Codice.Client.Common.WebApi.WebApiEndpoints;
public class AbilitySystem : NetworkBehaviour
{
[Header("Assigned Abilities")]
public List<Ability> abilities = new List<Ability>();
public AbilityUI[] abilitiesUI;
private Ability activeAbility;
private bool isAbilityActive = false;
private HashSet<Ability> abilitiesOnCooldown = new HashSet<Ability>();
[SerializeField] private GameObject currentAbilityIndicator;
[SerializeField] private GameObject wallIndicator;
[SerializeField] private GameObject radiusIndicator;
[SerializeField] private Material validPlacementMaterial;
[SerializeField] private Material invalidPlacementMaterial;
[Header("Wall Placement Settings")]
[SerializeField] private float wallRotationSpeed = 0.5f;
[SerializeField] private LayerMask playerLayer; // Layer for detecting players
[SerializeField] private float mouseMoveThreshold = 0.1f; // Minimum mouse movement threshold
private Vector3 initialMousePosition;
private Vector3 wallSpawnPosition;
private bool isWallPlacementStarted = false;
private bool isValidPlacement = true;
NavMeshAgent m_Agent;
private Dictionary<string, CursorState> abilityCursorMap;
private void InitializeAbilityCursorMap()
{
abilityCursorMap = new Dictionary<string, CursorState>
{
{ GameDataSource.Instance.DashNCrashAbilityKey, CursorState.Dash },
{ GameDataSource.Instance.FreezeThrowAbilityKey, CursorState.Freeze },
{ GameDataSource.Instance.VectorWallAbilityKey, CursorState.VectorWall }
};
}
private void Awake()
{
m_Agent = GetComponent<NavMeshAgent>();
}
private void Start()
{
abilitiesUI = FindObjectsOfType<AbilityUI>();
InitializeAbilityCursorMap();
}
private void UpdateCursorState(string abilityKey)
{
if (abilityCursorMap.TryGetValue(abilityKey, out CursorState cursorState))
{
Debug.Log("UpdateCursorState: " + abilityKey);
GameStateManager.Instance.ChangeState(cursorState);
}
}
void Update()
{
HandleAbilityMode();
}
private void HandleAbilityMode()
{
if (isAbilityActive)
{
if (activeAbility.abilityKey == "VectorFence")
{
//GameStateManager.Instance.ChangeState(CursorState.VectorWall);
ManageVectorFenceIndicator();
if (!isWallPlacementStarted)
{
UpdateWallIndicatorPosition(); // Follow the mouse when ability is activated
}
if (Input.GetMouseButtonDown(0))
{
StartWallPlacement();
}
if (Input.GetMouseButton(0) && isWallPlacementStarted)
{
RotateWallIndicator(); // Rotate while holding LMB
}
if (Input.GetMouseButtonUp(0) && isWallPlacementStarted)
{
UseActiveAbility(); // Place the wall when LMB is released
isWallPlacementStarted = false;
}
}
else
{
ManageStandardAbilityIndicator();
if (Input.GetMouseButtonDown(0))
{
UseActiveAbility();
}
}
}
else
{
// GameStateManager.Instance.ChangeState(CursorState.Default);
DeactivateIndicators();
}
}
private void ManageVectorFenceIndicator()
{
if (wallIndicator != null)
{
wallIndicator.SetActive(true);
currentAbilityIndicator.SetActive(false);
// Apply ability-specific scale to the wall indicator
if (activeAbility is VectorFenceAbility vectorFence)
{
wallIndicator.transform.localScale = new Vector3(vectorFence.wallLength, vectorFence.wallHeight, vectorFence.wallWidth);
}
}
}
private void UpdateWallIndicatorPosition()
{
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit))
{
//wallIndicator.transform.position = hit.point; // Update position to follow the mouse
wallIndicator.transform.position = new Vector3(hit.point.x, 0, hit.point.z);
isValidPlacement = IsPlacementValid(hit.point, wallIndicator.transform.rotation, playerLayer);
}
}
private void StartWallPlacement()
{
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit))
{
wallSpawnPosition = new Vector3(hit.point.x, 0, hit.point.z); // Save spawn position
initialMousePosition = Input.mousePosition; // Store the initial mouse position on click
isWallPlacementStarted = true;
Debug.Log($"[AbilitySystem] Wall placement started at {wallSpawnPosition}");
}
}
private void RotateWallIndicator()
{
if (isWallPlacementStarted && wallIndicator != null)
{
// Get the current mouse position in screen space
Vector3 currentMousePosition = Input.mousePosition;
// Calculate the distance the mouse has moved since the initial click
float mouseDistance = Vector3.Distance(initialMousePosition, currentMousePosition);
// Check if the movement exceeds the threshold
if (mouseDistance >= mouseMoveThreshold)
{
Ray ray = Camera.main.ScreenPointToRay(currentMousePosition);
if (Physics.Raycast(ray, out RaycastHit hit))
{
Vector3 direction = (new Vector3(hit.point.x, 0, hit.point.z) - wallIndicator.transform.position).normalized;
float angle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
angle -= 90f;
Quaternion targetRotation = Quaternion.Euler(0f, angle, 0f);
wallIndicator.transform.rotation = Quaternion.Lerp(wallIndicator.transform.rotation, targetRotation, 0.5f);
isValidPlacement = IsPlacementValid(wallIndicator.transform.position, wallIndicator.transform.rotation, playerLayer);
}
}
}
}
private bool IsPlacementValid(Vector3 position, Quaternion rotation, LayerMask layerMask)
{
// Perform overlap check after applying rotation
Vector3 halfExtents = new Vector3(
wallIndicator.transform.localScale.x / 2f,
wallIndicator.transform.localScale.y / 2f,
wallIndicator.transform.localScale.z / 2f
);
// Perform a CheckBox for the given parameters
bool isOverlap = Physics.CheckBox(position, halfExtents, rotation, layerMask);
return !isOverlap; // Return true if valid placement (no overlap)
}
private void ManageStandardAbilityIndicator()
{
if (currentAbilityIndicator != null)
{
currentAbilityIndicator.SetActive(true);
}
if (wallIndicator != null)
{
wallIndicator.SetActive(false);
}
UpdateIndicatorPosition();
}
private void DeactivateIndicators()
{
if (currentAbilityIndicator != null) currentAbilityIndicator.SetActive(false);
if (wallIndicator != null) wallIndicator.SetActive(false);
}
public void ActivateAbilityByKey(string key)
{
var ability = abilities.FirstOrDefault(a => a.abilityKey == key);
if (ability == null)
{
Debug.LogWarning($"No ability assigned to key {key}.");
return;
}
if (abilitiesOnCooldown.Contains(ability))
{
Debug.Log($"{ability.abilityName} is on cooldown.");
}
else
{
ToggleAbilityMode(ability,key);
}
}
public bool IsAbilityModeActive() => isAbilityActive;
private void ToggleAbilityMode(Ability ability,string abilitykey)
{
if (isAbilityActive && activeAbility == ability)
{
DeactivateAbilityMode();
}
else
{
ActivateAbilityMode(ability);
UpdateCursorState(abilitykey);
}
}
private void ActivateAbilityMode(Ability ability)
{
isAbilityActive = true;
activeAbility = ability;
Debug.Log($"Ability {ability.abilityName} activated! Click to use.");
radiusIndicator.transform.localScale = new Vector3(ability.abilityApplicationRadius * 2, 0, ability.abilityApplicationRadius * 2);
radiusIndicator.SetActive(true);
}
public void DeactivateAbilityMode()
{
GameStateManager.Instance.ChangeState(CursorState.Default);
isAbilityActive = false;
activeAbility = null;
isWallPlacementStarted = false;
radiusIndicator.SetActive(false);
Debug.Log("Ability mode deactivated.");
}
private bool IsPlacementWithinRadius(Vector3 targetPosition)
{
float placementRadius = activeAbility.abilityApplicationRadius; // Fetch radius from the active ability
float distance = Vector3.Distance(transform.position, targetPosition);
return distance <= placementRadius;
}
public void UseActiveAbility()
{
if (activeAbility == null)
{
Debug.LogWarning("[AbilitySystem] No active ability to use.");
return;
}
GameObject indicator = activeAbility.abilityKey == "VectorFence" ? wallIndicator : currentAbilityIndicator;
Vector3 targetPosition = indicator.transform.position;
Vector3 targetRotation = indicator.transform.eulerAngles;
if (!IsPlacementWithinRadius(targetPosition))
{
Debug.Log("[AbilitySystem] Not in Radius.");
return;
}
Debug.Log($"[AbilitySystem] Using active ability {activeAbility.abilityName}.");
RequestAbilityActivationServerRpc(activeAbility.abilityKey, targetPosition, targetRotation);
StartCoroutine(StartCooldown(activeAbility));
DeactivateAbilityMode();
}
[ServerRpc(RequireOwnership = false)]
private void RequestAbilityActivationServerRpc(string abilityKey, Vector3 targetPosition, Vector3 targetRotation, ServerRpcParams rpcParams = default)
{
ulong ownerClientId = rpcParams.Receive.SenderClientId;
Debug.Log($"[AbilitySystem] Received activation request for ability '{abilityKey}' from client {ownerClientId}.");
ExecuteAbilityOnServer(abilityKey, ownerClientId, targetPosition, targetRotation);
}
private void ExecuteAbilityOnServer(string abilityKey, ulong ownerClientId, Vector3 targetPosition, Vector3 targetRotation)
{
var playerObject = NetworkManager.Singleton.SpawnManager.SpawnedObjectsList
.FirstOrDefault(obj => obj.OwnerClientId == ownerClientId && obj.GetComponent<ServerCharacter>());
if (playerObject == null || !playerObject.TryGetComponent(out ServerCharacter character))
{
Debug.LogError($"[AbilitySystem] No ServerCharacter component found for player {ownerClientId}.");
return;
}
var ability = abilities.FirstOrDefault(a => a.abilityKey == abilityKey);
if (ability == null)
{
Debug.LogError($"[AbilitySystem] Ability {abilityKey} not found.");
return;
}
Debug.Log($"[AbilitySystem] Activating ability {ability.abilityName} for player {ownerClientId}.");
ability.Execute(character, targetPosition, targetRotation);
}
private IEnumerator StartCooldown(Ability ability)
{
abilitiesOnCooldown.Add(ability);
Debug.Log($"{ability.abilityName} is now on cooldown for {ability.abilityCooldownTime} seconds.");
var abilityUI = abilitiesUI.FirstOrDefault(a => a.key == ability.abilityKey);
abilityUI.StopWatchFiller(ability.abilityCooldownTime);
yield return new WaitForSeconds(ability.abilityCooldownTime);
abilitiesOnCooldown.Remove(ability);
Debug.Log($"{ability.abilityName} is off cooldown.");
}
private void UpdateIndicatorPosition()
{
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit))
{
currentAbilityIndicator.transform.position = new Vector3(hit.point.x, 0, hit.point.z);
currentAbilityIndicator.transform.localScale = Vector3.one * activeAbility.abilityRadius;
}
}
}