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 abilities = new List(); public AbilityUI[] abilitiesUI; private Ability activeAbility; private bool isAbilityActive = false; private HashSet abilitiesOnCooldown = new HashSet(); [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(); } private void Start() { abilitiesUI = FindObjectsOfType(); } 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 wallIndicator.transform.position = new Vector3(hit.point.x, 0, hit.point.z); //isValidPlacement = true; //AliSharoz isValidPlacement = IsPlacementValid(hit.point, wallIndicator.transform.rotation, playerLayer); /*var meshRenderer = wallIndicator.GetComponent(); 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 = 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}"); } /*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 = (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); // Smooth rotation with Lerp // wallIndicator.transform.rotation = targetRotation; wallIndicator.transform.rotation = Quaternion.Lerp(wallIndicator.transform.rotation, targetRotation, 0.5f); //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(); 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()); 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; } } }