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);
+ }
+
}