|
|
|
using System.Collections;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
|
|
using Unity.BossRoom.Gameplay.GameplayObjects;
|
|
|
|
using Unity.BossRoom.Gameplay.GameplayObjects.Character;
|
|
|
|
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>();
|
|
|
|
|
|
|
|
private Ability activeAbility;
|
|
|
|
private bool isAbilityActive = false;
|
|
|
|
private HashSet<Ability> abilitiesOnCooldown = new HashSet<Ability>();
|
|
|
|
|
|
|
|
[SerializeField] private GameObject currentAbilityIndicator;
|
|
|
|
[SerializeField] private GameObject wallIndicator;
|
|
|
|
[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 void Awake()
|
|
|
|
{
|
|
|
|
m_Agent = GetComponent<NavMeshAgent>();
|
|
|
|
}
|
|
|
|
void Update()
|
|
|
|
{
|
|
|
|
HandleAbilityMode();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void HandleAbilityMode()
|
|
|
|
{
|
|
|
|
if (isAbilityActive)
|
|
|
|
{
|
|
|
|
if (activeAbility.abilityKey == "VectorFence")
|
|
|
|
{
|
|
|
|
ManageVectorFenceIndicator();
|
|
|
|
|
|
|
|
if (!isWallPlacementStarted)
|
|
|
|
{
|
|
|
|
UpdateWallIndicatorPosition(); // Follow the mouse when ability is activated
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Input.GetMouseButtonDown(0))//AliSharoz && isValidPlacement)
|
|
|
|
{
|
|
|
|
StartWallPlacement();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Input.GetMouseButton(0) && isWallPlacementStarted)
|
|
|
|
{
|
|
|
|
RotateWallIndicator(); // Rotate while holding LMB
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Input.GetMouseButtonUp(0) && isWallPlacementStarted)
|
|
|
|
{
|
|
|
|
//if (isValidPlacement)
|
|
|
|
{
|
|
|
|
UseActiveAbility(); // Place the wall when LMB is released
|
|
|
|
isWallPlacementStarted = false;
|
|
|
|
}
|
|
|
|
/*else
|
|
|
|
{
|
|
|
|
UseActiveAbility(); // Place the wall when LMB is released
|
|
|
|
isWallPlacementStarted = false;
|
|
|
|
Invoke(nameof(RepositionAgent), 0.5f);
|
|
|
|
Debug.Log("Invalid placement! Cannot place wall on top of another player.");
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ManageStandardAbilityIndicator();
|
|
|
|
|
|
|
|
if (Input.GetMouseButtonDown(0))
|
|
|
|
{
|
|
|
|
UseActiveAbility();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
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
|
|
|
|
|
|
|
|
//isValidPlacement = true;
|
|
|
|
//AliSharoz
|
|
|
|
isValidPlacement = IsPlacementValid(hit.point, wallIndicator.transform.rotation, playerLayer);
|
|
|
|
|
|
|
|
/*var meshRenderer = wallIndicator.GetComponent<MeshRenderer>();
|
|
|
|
if (meshRenderer != null)
|
|
|
|
{
|
|
|
|
meshRenderer.material = isValidPlacement ? validPlacementMaterial : invalidPlacementMaterial;
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void StartWallPlacement()
|
|
|
|
{
|
|
|
|
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit))
|
|
|
|
{
|
|
|
|
//if (isValidPlacement)
|
|
|
|
{
|
|
|
|
wallSpawnPosition = hit.point; // Save spawn position
|
|
|
|
initialMousePosition = Input.mousePosition; // Store the initial mouse position on click
|
|
|
|
isWallPlacementStarted = true;
|
|
|
|
Debug.Log($"[AbilitySystem] Wall placement started at {wallSpawnPosition}");
|
|
|
|
}
|
|
|
|
/*else
|
|
|
|
{
|
|
|
|
wallSpawnPosition = hit.point; // Save spawn position
|
|
|
|
initialMousePosition = Input.mousePosition; // Store the initial mouse position on click
|
|
|
|
isWallPlacementStarted = true;
|
|
|
|
Debug.Log("Cannot place the wall on top of another player.");
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*public float searchRadius = 5f;
|
|
|
|
|
|
|
|
private void RepositionAgent()
|
|
|
|
{
|
|
|
|
Debug.Log("RepositionAgent()1");
|
|
|
|
//if (NavMesh.SamplePosition(currentPosition, out hit, 0.01f, NavMesh.AllAreas))
|
|
|
|
//if (m_Agent.isOnNavMesh == false)
|
|
|
|
{
|
|
|
|
Debug.Log("RepositionAgent()2");
|
|
|
|
Vector3 currentPosition = m_Agent.transform.position;
|
|
|
|
NavMeshHit hit;
|
|
|
|
|
|
|
|
// Sample the nearest point on the NavMesh
|
|
|
|
if (NavMesh.SamplePosition(currentPosition, out hit, searchRadius, NavMesh.AllAreas))
|
|
|
|
{
|
|
|
|
Debug.Log("RepositionAgent()3");
|
|
|
|
Debug.Log($"Found nearest NavMesh point at {hit.position}");
|
|
|
|
m_Agent.Warp(hit.position); // Warp the agent to the nearest point without path recalculation
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Debug.Log("RepositionAgent()4");
|
|
|
|
Debug.LogError("No walkable NavMesh point found within the search radius!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//else
|
|
|
|
//{
|
|
|
|
// Debug.LogWarning("No valid NavMesh point found near the agent!");
|
|
|
|
//}
|
|
|
|
}*/
|
|
|
|
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 = (hit.point - wallIndicator.transform.position).normalized;
|
|
|
|
float angle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
|
|
|
|
Quaternion targetRotation = Quaternion.Euler(0f, angle, 0f);
|
|
|
|
|
|
|
|
// Smooth rotation with Lerp
|
|
|
|
// wallIndicator.transform.rotation = targetRotation;
|
|
|
|
wallIndicator.transform.rotation = Quaternion.Lerp(wallIndicator.transform.rotation, targetRotation, Time.deltaTime * wallRotationSpeed);
|
|
|
|
isValidPlacement = IsPlacementValid(wallIndicator.transform.position, wallIndicator.transform.rotation, playerLayer);
|
|
|
|
|
|
|
|
/*
|
|
|
|
// Change indicator color based on placement validity
|
|
|
|
AliSharoz
|
|
|
|
var meshRenderer = wallIndicator.GetComponent<MeshRenderer>();
|
|
|
|
if (meshRenderer != null)
|
|
|
|
{
|
|
|
|
meshRenderer.material = isValidPlacement ? validPlacementMaterial : invalidPlacementMaterial;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isValidPlacement)
|
|
|
|
{
|
|
|
|
Debug.Log("Cannot rotate wall here: Overlapping with another object.");
|
|
|
|
Invoke(nameof(RepositionAgent), 0.5f);
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool IsAbilityModeActive() => isAbilityActive;
|
|
|
|
|
|
|
|
private void ToggleAbilityMode(Ability ability)
|
|
|
|
{
|
|
|
|
if (isAbilityActive && activeAbility == ability)
|
|
|
|
{
|
|
|
|
DeactivateAbilityMode();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ActivateAbilityMode(ability);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void ActivateAbilityMode(Ability ability)
|
|
|
|
{
|
|
|
|
isAbilityActive = true;
|
|
|
|
activeAbility = ability;
|
|
|
|
Debug.Log($"Ability {ability.abilityName} activated! Click to use.");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void DeactivateAbilityMode()
|
|
|
|
{
|
|
|
|
isAbilityActive = false;
|
|
|
|
activeAbility = null;
|
|
|
|
isWallPlacementStarted = false;
|
|
|
|
Debug.Log("Ability mode deactivated.");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void UseActiveAbility()
|
|
|
|
{
|
|
|
|
if (activeAbility == null)
|
|
|
|
{
|
|
|
|
Debug.LogWarning("[AbilitySystem] No active ability to use.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector3 targetPosition = activeAbility.abilityKey == "VectorFence" ? wallIndicator.transform.position : currentAbilityIndicator.transform.position;
|
|
|
|
Vector3 targetRotation = activeAbility.abilityKey == "VectorFence" ? wallIndicator.transform.eulerAngles : currentAbilityIndicator.transform.eulerAngles;
|
|
|
|
|
|
|
|
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.");
|
|
|
|
|
|
|
|
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 = hit.point;
|
|
|
|
currentAbilityIndicator.transform.localScale = Vector3.one * activeAbility.abilityRadius;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|