diff --git a/Assets/GameData/Action/Abilities/Crows Foresight Ability.asset b/Assets/GameData/Action/Abilities/Crows Foresight Ability.asset index 848e444..a10085f 100644 --- a/Assets/GameData/Action/Abilities/Crows Foresight Ability.asset +++ b/Assets/GameData/Action/Abilities/Crows Foresight Ability.asset @@ -16,7 +16,7 @@ MonoBehaviour: abilityName: Crows Foresight abilityRadius: 10 abilityMagnitude: 10 - abilityDuration: 30 + abilityDuration: 10 abilityCooldownTime: 10 abilityApplicationRadius: 20 - prefab: {fileID: 0} + prefab: {fileID: 6129284537519539166, guid: 2054403c32efac24999b3167985d56f2, type: 3} diff --git a/Assets/GameData/NetworkPrefabs.asset b/Assets/GameData/NetworkPrefabs.asset index 7265f14..089c87a 100644 --- a/Assets/GameData/NetworkPrefabs.asset +++ b/Assets/GameData/NetworkPrefabs.asset @@ -89,3 +89,8 @@ MonoBehaviour: SourcePrefabToOverride: {fileID: 0} SourceHashToOverride: 0 OverridingTargetPrefab: {fileID: 0} + - Override: 0 + Prefab: {fileID: 6129284537519539166, guid: 2054403c32efac24999b3167985d56f2, type: 3} + SourcePrefabToOverride: {fileID: 0} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} diff --git a/Assets/Prefabs/Abilities/CrowsForesightPrefab.prefab b/Assets/Prefabs/Abilities/CrowsForesightPrefab.prefab new file mode 100644 index 0000000..ce98eec --- /dev/null +++ b/Assets/Prefabs/Abilities/CrowsForesightPrefab.prefab @@ -0,0 +1,69 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &6129284537519539166 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1069926530772311797} + - component: {fileID: 8893380544067200444} + - component: {fileID: 1316219484843411118} + m_Layer: 0 + m_Name: CrowsForesightPrefab + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1069926530772311797 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129284537519539166} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &8893380544067200444 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129284537519539166} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 17096565 + InScenePlacedSourceGlobalObjectIdHash: 0 + AlwaysReplicateAsRoot: 0 + SynchronizeTransform: 1 + ActiveSceneSynchronization: 0 + SceneMigrationSynchronization: 1 + SpawnWithObservers: 1 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 +--- !u!114 &1316219484843411118 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129284537519539166} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 24b344d29ff558b4197f1bb01ec11343, type: 3} + m_Name: + m_EditorClassIdentifier: + Ability: {fileID: 11400000, guid: 6b4485e4bc8be814db7fc22a6778fbd5, type: 2} diff --git a/Assets/Prefabs/Abilities/CrowsForesightPrefab.prefab.meta b/Assets/Prefabs/Abilities/CrowsForesightPrefab.prefab.meta new file mode 100644 index 0000000..51e9f1e --- /dev/null +++ b/Assets/Prefabs/Abilities/CrowsForesightPrefab.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2054403c32efac24999b3167985d56f2 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Gameplay/AbilitySystem.cs b/Assets/Scripts/Gameplay/AbilitySystem.cs index 6ec8019..a4a244c 100644 --- a/Assets/Scripts/Gameplay/AbilitySystem.cs +++ b/Assets/Scripts/Gameplay/AbilitySystem.cs @@ -391,7 +391,10 @@ public class AbilitySystem : NetworkBehaviour 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); + if (abilityUI) + { + abilityUI.StopWatchFiller(ability.abilityCooldownTime); + } yield return new WaitForSeconds(ability.abilityCooldownTime); abilitiesOnCooldown.Remove(ability); diff --git a/Assets/Scripts/Gameplay/CrowManager.cs b/Assets/Scripts/Gameplay/CrowManager.cs index 1f9003b..123c47e 100644 --- a/Assets/Scripts/Gameplay/CrowManager.cs +++ b/Assets/Scripts/Gameplay/CrowManager.cs @@ -50,7 +50,7 @@ public class CrowManager : NetworkBehaviour /// /// Determines which player should be the crow. /// - int unoccupiedPlayers = 0; + int unoccupiedPlayers = 0; private void DetermineCrow() { @@ -136,12 +136,27 @@ public class CrowManager : NetworkBehaviour } else { + // If a player was the crow but is now losing it, request foresight removal + if (player.IsCrow) + { + Debug.Log($"[CrowManager] {player.name} is no longer the Crow. Requesting foresight removal."); + if (IsServer) + { + var foresightPrefab = FindObjectOfType(); + if (foresightPrefab) + { + foresightPrefab.DespawnForesight(); + } + } + } + player.SetAsCrow(false); player.SwitchToPlayerModel(); } } } + /// /// Adds a character to the players list. /// @@ -191,4 +206,103 @@ public class CrowManager : NetworkBehaviour return currentCrow; } + public void NotifyForesightSwap(ulong senderId, ulong receiverId, Vector3 senderPos, Vector3 receiverPos) + { + Debug.Log($"[CrowManager] NotifyForesightSwap called - Sender: {senderId}, Receiver: {receiverId}, SenderPos: {senderPos}, ReceiverPos: {receiverPos}"); + + if (currentCrow) + { + Debug.Log($"[CrowManager] Sending foresight swap notification to the Crow: {currentCrow.OwnerClientId}"); + NotifySwapForesightClientRpc(senderId, receiverId, senderPos, receiverPos); + } + else + { + Debug.LogWarning("[CrowManager] No current crow found to receive the foresight swap update."); + } + } + + public void NotifyForesightSwapDecision(ulong senderId, Vector3 receiverPos, bool isAccepted) + { + if (currentCrow) + { + NotifySwapForesightDecisionClientRpc(senderId, receiverPos, isAccepted); + } + } + + [Rpc(SendTo.Everyone)] + private void NotifySwapForesightDecisionClientRpc(ulong senderId, Vector3 receiverPos, bool isAccepted) + { + if (currentCrow && currentCrow.clientCharacter) + { + if (isAccepted) + { + FindObjectOfType().OnSwapAccepted(senderId, receiverPos); + } + else + { + FindObjectOfType().OnSwapRejected(senderId, receiverPos); + } + } + } + + + [Rpc(SendTo.Everyone)] + private void NotifySwapForesightClientRpc(ulong senderId, ulong receiverId, Vector3 senderPos, Vector3 receiverPos) + { + Debug.Log($"[CrowManager] NotifySwapForesightClientRpc received - Sender: {senderId}, Receiver: {receiverId}, SenderPos: {senderPos}, ReceiverPos: {receiverPos}"); + + if (currentCrow == null) + { + Debug.LogError("[CrowManager] Error: Current Crow is null!"); + return; + } + + if (currentCrow.clientCharacter == null) + { + Debug.LogError("[CrowManager] Error: Current Crow clientCharacter is null!"); + return; + } + + var foresightPrefab = FindObjectOfType(); + + if (foresightPrefab == null) + { + Debug.LogError("[CrowManager] Error: CrowsForesightPrefab not found on Crow!"); + return; + } + + foresightPrefab.OnSwapRequested(senderId, receiverId, senderPos, receiverPos); + } + + + [Rpc(SendTo.Everyone)] + private void NotifySwapForesightAcceptedClientRpc(ulong senderId, Vector3 receiverPos) + { + Debug.Log($"[CrowManager] NotifySwapForesightAcceptedClientRpc received - Sender: {senderId}, ReceiverPos: {receiverPos}"); + + if (currentCrow == null) + { + Debug.LogError("[CrowManager] Error: Current Crow is null!"); + return; + } + + if (currentCrow.clientCharacter == null) + { + Debug.LogError("[CrowManager] Error: Current Crow clientCharacter is null!"); + return; + } + + var foresightPrefab = FindObjectOfType(); + + if (foresightPrefab == null) + { + Debug.LogError("[CrowManager] Error: CrowsForesightPrefab not found on Crow!"); + return; + } + + foresightPrefab.OnSwapAccepted(senderId, receiverPos); + } + + + } diff --git a/Assets/Scripts/Gameplay/CrowsForesightAbility.cs b/Assets/Scripts/Gameplay/CrowsForesightAbility.cs index e7e8c53..5a8b37a 100644 --- a/Assets/Scripts/Gameplay/CrowsForesightAbility.cs +++ b/Assets/Scripts/Gameplay/CrowsForesightAbility.cs @@ -1,13 +1,30 @@ -using System.Collections; -using System.Collections.Generic; -using Unity.BossRoom.Gameplay.GameplayObjects.Character; +using Unity.Netcode; using UnityEngine; +using Unity.BossRoom.Gameplay.GameplayObjects.Character; [CreateAssetMenu(menuName = "Abilities/CrowsForesight")] public class CrowsForesightAbility : Ability { public override void Execute(ServerCharacter character, Vector3 targetPosition, Vector3 targetRotation) { - throw new System.NotImplementedException(); + if (!NetworkManager.Singleton.IsServer) + { + Debug.LogError("[CrowsForesightAbility] Execute should only be called on the server."); + return; + } + + Debug.Log($"[CrowsForesightAbility] Spawning foresight prefab for Crow {character.OwnerClientId}"); + + GameObject foresightInstance = Instantiate(GetPrefab(), character.transform.position, Quaternion.identity); + NetworkObject netObj = foresightInstance.GetComponent(); + + if (netObj) + { + netObj.Spawn(); + CrowsForesightPrefab foresightPrefab = foresightInstance.GetComponent(); + foresightPrefab.Initialize(CrowManager.Instance.GetCurrentCrow().OwnerClientId); + Debug.Log($"[CrowsForesightAbility] Assigned foresight prefab to Crow {character.OwnerClientId}"); + } } + } diff --git a/Assets/Scripts/Gameplay/CrowsForesightPrefab.cs b/Assets/Scripts/Gameplay/CrowsForesightPrefab.cs new file mode 100644 index 0000000..e19b7a8 --- /dev/null +++ b/Assets/Scripts/Gameplay/CrowsForesightPrefab.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using Unity.Netcode; + +public class CrowsForesightPrefab : NetworkBehaviour +{ + private Dictionary foresightLines = new Dictionary(); + private ulong crowClientId; + private Coroutine destroyCoroutine; + private void Start() + { + if (IsServer) + { + Debug.Log("[CrowsForesightPrefab] Starting auto-destroy coroutine."); + destroyCoroutine = StartCoroutine(DelayedDestroy()); + } + } + + public void Initialize(ulong crowId) + { + crowClientId = crowId; + } + + public override void OnNetworkSpawn() + { + Debug.Log($"[CrowsForesightPrefab] OnNetworkSpawn called - LocalClientId: {NetworkManager.Singleton.LocalClientId}, crowClientId: {crowClientId}"); + if (NetworkManager.Singleton.LocalClientId != CrowManager.Instance.GetCurrentCrow().OwnerClientId && !NetworkManager.Singleton.IsServer) + { + gameObject.SetActive(false); // Hide from non-crow clients + } + } + + private IEnumerator DelayedDestroy(float time = 10) + { + yield return new WaitForSeconds(time); // Adjust according to abilityDuration + + if (IsServer) + { + Debug.Log("[CrowsForesightPrefab] Ability duration expired. Despawning on server."); + DespawnForesight(); + } + } + + public void DespawnForesight() + { + if (!IsServer) return; + + Debug.Log("[CrowsForesightPrefab] Destroying foresight effect and clearing all lines."); + + // Remove all foresight lines before despawning + foreach (var line in foresightLines.Values) + { + if (line != null) + { + Destroy(line.gameObject); + } + } + foresightLines.Clear(); + + if (NetworkObject.IsSpawned) + { + Debug.Log("[CrowsForesightPrefab] Despawning network object."); + NetworkObject.Despawn(true); + } + else + { + Debug.LogWarning("[CrowsForesightPrefab] WARNING: Tried to despawn, but NetworkObject was already despawned or missing!"); + } + } + + + public void OnSwapRequested(ulong senderId, ulong receiverId, Vector3 senderPos, Vector3 receiverPos) + { + Debug.Log($"[CrowsForesightPrefab] OnSwapRequested - Sender: {senderId}, Receiver: {receiverId}, SenderPos: {senderPos}, ReceiverPos: {receiverPos}"); + + if (!foresightLines.ContainsKey(senderId)) + { + Debug.Log($"[CrowsForesightPrefab] Creating foresight line for swap request {senderId} -> {receiverId}"); + foresightLines[senderId] = CreateLine(); + } + + foresightLines[senderId].SetPosition(0, senderPos); + foresightLines[senderId].SetPosition(1, Vector3.Lerp(senderPos, receiverPos, 0.5f)); + + Debug.Log($"[CrowsForesightPrefab] Halfway foresight line drawn from {senderPos} to midpoint."); + } + + public void OnSwapAccepted(ulong senderId, Vector3 receiverPos) + { + Debug.Log($"[CrowsForesightPrefab] OnSwapAccepted - Sender: {senderId}, ReceiverPos: {receiverPos}"); + + if (!foresightLines.ContainsKey(senderId)) + { + Debug.LogError($"[CrowsForesightPrefab] ERROR: No foresight line found for sender {senderId} when swap was accepted!"); + return; + } + + foresightLines[senderId].SetPosition(1, receiverPos); + if (foresightLines.ContainsKey(senderId)) + { + // Destroy the line associated with the sender + Destroy(foresightLines[senderId].gameObject ,1f); + foresightLines.Remove(senderId); + + Debug.Log($"[CrowsForesightPrefab] Removed foresight line for rejected swap request from {senderId}."); + } + Debug.Log($"[CrowsForesightPrefab] Foresight line completed from sender {senderId} to {receiverPos}."); + } + + + public void OnSwapRejected(ulong senderId, Vector3 receiverPos) + { + Debug.Log($"[CrowsForesightPrefab] OnSwapRejected - Sender: {senderId}, ReceiverPos: {receiverPos}"); + + if (foresightLines.ContainsKey(senderId)) + { + // Destroy the line associated with the sender + Destroy(foresightLines[senderId].gameObject); + foresightLines.Remove(senderId); + + Debug.Log($"[CrowsForesightPrefab] Removed foresight line for rejected swap request from {senderId}."); + } + else + { + Debug.LogWarning($"[CrowsForesightPrefab] WARNING: No foresight line found for sender {senderId}. Swap rejection may have happened before drawing."); + } + } + + + private LineRenderer CreateLine() + { + GameObject lineObj = new GameObject("ForesightLine"); + LineRenderer line = lineObj.AddComponent(); + line.startWidth = 0.1f; + line.endWidth = 0.1f; + line.material = new Material(Shader.Find("Sprites/Default")); + line.transform.SetParent(transform); + return line; + } +} diff --git a/Assets/Scripts/Gameplay/CrowsForesightPrefab.cs.meta b/Assets/Scripts/Gameplay/CrowsForesightPrefab.cs.meta new file mode 100644 index 0000000..ad6f966 --- /dev/null +++ b/Assets/Scripts/Gameplay/CrowsForesightPrefab.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 24b344d29ff558b4197f1bb01ec11343 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerCharacter.cs b/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerCharacter.cs index f7996e3..3554fcb 100644 --- a/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerCharacter.cs +++ b/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerCharacter.cs @@ -391,41 +391,47 @@ namespace Unity.BossRoom.Gameplay.GameplayObjects.Character [Rpc(SendTo.Server, RequireOwnership = false)] - public void NotifySwapRequestRpc(ulong initiatingPlayerId,string name) + public void NotifySwapRequestRpc(ulong senderId, string senderName) { - PendingSwapRequest = initiatingPlayerId; + PendingSwapRequest = senderId; + ShowSwapConfirmationPanelClientRpc(senderName); - // Notify all clients except the server, filtered by target - ShowSwapConfirmationPanelClientRpc(name); + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(senderId, out var senderObj) && + senderObj.TryGetComponent(out ServerCharacter senderChar)) + { + Vector3 senderPos = senderChar.transform.position; + Vector3 receiverPos = transform.position; - Debug.Log($"Swap request received from Player {initiatingPlayerId}. Waiting for confirmation."); + CrowManager.Instance.NotifyForesightSwap(senderChar.OwnerClientId, OwnerClientId, senderPos, receiverPos); + } } + + [Rpc(SendTo.Server, RequireOwnership = false)] public void NotifySwapDecisionRpc(bool isAccepted) { if (!PendingSwapRequest.HasValue) return; ulong initiatingPlayerId = PendingSwapRequest.Value; - - if (isAccepted) + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(initiatingPlayerId, out var initiatingPlayerObj) && + initiatingPlayerObj.TryGetComponent(out ServerCharacter initiatingPlayer)) { - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(initiatingPlayerId, out var initiatingPlayerObj) && - initiatingPlayerObj.TryGetComponent(out ServerCharacter initiatingPlayer)) + Vector3 receiverPos = transform.position; + CrowManager.Instance.NotifyForesightSwapDecision(initiatingPlayerObj.OwnerClientId, receiverPos, isAccepted); + if (isAccepted) { InitiateSwap(initiatingPlayer, this); Debug.Log($"Swap confirmed: {initiatingPlayer.name} and {this.name} are swapping."); + } } - else - { - Debug.Log($"Swap request denied by {this.name}."); - } - PendingSwapRequest = null; } + public void InitiateSwap(ServerCharacter initiatingPlayer, ServerCharacter targetPlayer) { var initiatingPlatform = PlatformManager.Instance.GetPlatformOccupiedByPlayer(initiatingPlayer); diff --git a/Assets/Scripts/Gameplay/UserInput/ClientInputSender.cs b/Assets/Scripts/Gameplay/UserInput/ClientInputSender.cs index 04759f2..68c1e5b 100644 --- a/Assets/Scripts/Gameplay/UserInput/ClientInputSender.cs +++ b/Assets/Scripts/Gameplay/UserInput/ClientInputSender.cs @@ -572,6 +572,16 @@ namespace Unity.BossRoom.Gameplay.UserInput } ActivateAbilityIfAllowed(GameDataSource.Instance.TheExecutionerKey); } + if (Input.GetKeyDown(KeyCode.V)) // Crow's Foresight + { + if (!m_ServerCharacter.IsCrow) + { + m_UIMessageFeed.DisplayMessage("You must be the Crow to activate this ability"); + return; + } + ActivateAbilityIfAllowed(GameDataSource.Instance.CrowsForesightKey); + } + }