using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine.InputSystem; public class FaceoffManager : MonoBehaviour { [Header("References")] [SerializeField] private PuckController puck; [Header("Faceoff Spots")] [SerializeField] private Transform centerIceFaceoffSpot; [SerializeField] private Transform homeZoneLeftFaceoffSpot; [SerializeField] private Transform homeZoneRightFaceoffSpot; [SerializeField] private Transform awayZoneLeftFaceoffSpot; [SerializeField] private Transform awayZoneRightFaceoffSpot; [SerializeField] private Transform homeZoneBlueLineLeftFaceoffSpot; [SerializeField] private Transform homeZoneBlueLineRightFaceoffSpot; [SerializeField] private Transform awayZoneBlueLineLeftFaceoffSpot; [SerializeField] private Transform awayZoneBlueLineRightFaceoffSpot; private Transform currentFaceoffSpot; [Header("Faceoff Settings")] [SerializeField] private float faceoffDuration = 2.0f; [SerializeField] private float struggleDuration = 3.0f; [SerializeField] private float struggleChance = 0.3f; [SerializeField] private float winThreshold = 0.7f; [Header("Puck Launch Settings")] [SerializeField] private float passForce = 8f; // Force to send puck to teammate [SerializeField] private float struggleLaunchForce = 5f; // Force when puck is loose private PlayerController homeCenterPlayer; private PlayerController awayCenterPlayer; private List homeTeam = new List(); private List awayTeam = new List(); private bool faceoffInProgress = false; private FaceoffOutcome currentOutcome; // Public property to check if faceoff is in progress public bool IsFaceoffInProgress => faceoffInProgress; public enum FaceoffOutcome { None, HomeWin, AwayWin, Struggle } void Start() { FindPlayers(); } void FindPlayers() { PlayerController[] allPlayers = FindObjectsByType(FindObjectsSortMode.None); homeTeam.Clear(); awayTeam.Clear(); foreach (var player in allPlayers) { if (player.CompareTag("HomeTeam")) { homeTeam.Add(player); if (player.stats.position == PlayerPosition.C) { homeCenterPlayer = player; } } else if (player.CompareTag("AwayTeam")) { awayTeam.Add(player); if (player.stats.position == PlayerPosition.C) { awayCenterPlayer = player; } } } Debug.Log($"Found {homeTeam.Count} home players and {awayTeam.Count} away players"); } public void StartFaceoffAt(Transform faceoffSpot) { if (faceoffSpot == null) { Debug.LogWarning("Faceoff spot is not assigned!"); return; } currentFaceoffSpot = faceoffSpot; StartFaceoff(); } public void StartFaceoff() { if (faceoffInProgress) { Debug.LogWarning("Faceoff already in progress!"); return; } if (currentFaceoffSpot == null) { currentFaceoffSpot = centerIceFaceoffSpot; } if (homeCenterPlayer == null || awayCenterPlayer == null) { Debug.LogError("Missing center players for faceoff!"); return; } StartCoroutine(FaceoffSequence()); } private IEnumerator FaceoffSequence() { faceoffInProgress = true; Debug.Log("Faceoff starting..."); // Stop timer and freeze players during faceoff if (GameManager.Instance != null) { GameManager.Instance.SetPuckInPlay(false); } // Position puck at faceoff spot if (puck != null && currentFaceoffSpot != null) { puck.transform.position = currentFaceoffSpot.position + Vector3.up * 0.025f; // Slight lift puck.Release(); // Make sure puck is free } FreezeAllPlayers(true); yield return new WaitForSeconds(1.0f); currentOutcome = DetermineFaceoffOutcome(); Debug.Log($"Faceoff outcome: {currentOutcome}"); yield return new WaitForSeconds(faceoffDuration); switch (currentOutcome) { case FaceoffOutcome.HomeWin: ExecuteCleanWin(homeCenterPlayer, homeTeam, awayTeam); break; case FaceoffOutcome.AwayWin: ExecuteCleanWin(awayCenterPlayer, awayTeam, homeTeam); break; case FaceoffOutcome.Struggle: ExecuteStruggle(); yield return new WaitForSeconds(struggleDuration); break; } FreezeAllPlayers(false); faceoffInProgress = false; // Start timer and allow movement after faceoff completes if (GameManager.Instance != null) { GameManager.Instance.SetPuckInPlay(true); } Debug.Log("Faceoff complete!"); } private FaceoffOutcome DetermineFaceoffOutcome() { if (homeCenterPlayer == null || awayCenterPlayer == null) return FaceoffOutcome.Struggle; float homeStrength = CalculateFaceoffStrength(homeCenterPlayer); float awayStrength = CalculateFaceoffStrength(awayCenterPlayer); float totalStrength = homeStrength + awayStrength; float homeWinChance = homeStrength / totalStrength; if (Mathf.Abs(homeWinChance - 0.5f) < (0.5f - winThreshold)) { return FaceoffOutcome.Struggle; } float roll = Random.value; if (roll < struggleChance) { return FaceoffOutcome.Struggle; } return Random.value < homeWinChance ? FaceoffOutcome.HomeWin : FaceoffOutcome.AwayWin; } private float CalculateFaceoffStrength(PlayerController player) { return (player.stats.strength * 0.3f) + (player.stats.stickHandling * 0.3f) + (player.stats.awareness * 0.2f) + (player.stats.agility * 0.2f); } private void ExecuteCleanWin(PlayerController winner, List winningTeam, List losingTeam) { Debug.Log($"{winner.stats.playerName} wins the faceoff cleanly!"); // Find best pass target FIRST (before assigning puck) PlayerController target = ChoosePassTarget(winner, winningTeam); if (target != null && target != winner) { Debug.Log($"Puck sent to {target.stats.playerName} at position {target.transform.position}"); // Calculate direction from FACEOFF SPOT (not puck position, since puck might be carried) Vector3 startPosition = currentFaceoffSpot.position; Vector3 targetPosition = target.transform.position; // Keep pass flat on the ice (ignore Y difference) startPosition.y = 0.025f; targetPosition.y = 0.025f; Vector3 passDirection = (targetPosition - startPosition).normalized; float distance = Vector3.Distance(startPosition, targetPosition); float adjustedForce = passForce * Mathf.Clamp(distance / 5f, 0.8f, 2f); Debug.Log($"Pass direction: {passDirection}, Distance: {distance:F2}m, Force: {adjustedForce:F2}"); // Make sure puck is free and at faceoff spot if (puck != null) { puck.Release(); // Wait one frame to ensure Release() completes StartCoroutine(ExecutePassAfterFrame(startPosition, passDirection, adjustedForce, target)); } } else { Debug.Log($"{winner.stats.playerName} keeps the puck!"); if (puck != null) { puck.AssignToPlayer(winner); } } } private IEnumerator ExecutePassAfterFrame(Vector3 startPosition, Vector3 direction, float force, PlayerController target) { yield return new WaitForFixedUpdate(); // Wait for physics to settle if (puck != null) { // Position puck at faceoff spot puck.transform.position = startPosition; // Get rigidbody and zero everything Rigidbody puckRb = puck.GetComponent(); if (puckRb != null) { puckRb.linearVelocity = Vector3.zero; puckRb.angularVelocity = Vector3.zero; // Apply force in next physics frame yield return new WaitForFixedUpdate(); puckRb.AddForce(direction * force, ForceMode.VelocityChange); Debug.Log($"Applied force: {direction * force}"); } } // Delay assignment to let puck travel StartCoroutine(AssignPuckAfterDelay(target, 0.5f)); } private IEnumerator AssignPuckAfterDelay(PlayerController target, float delay) { yield return new WaitForSeconds(delay); // Check if puck is close enough to target if (puck != null && target != null && puck.IsLoose) { float distance = Vector3.Distance(puck.transform.position, target.transform.position); if (distance < 4f) // Increased pickup range further { // Stop the puck's movement before assigning Rigidbody puckRb = puck.GetComponent(); if (puckRb != null) { puckRb.linearVelocity = Vector3.zero; puckRb.angularVelocity = Vector3.zero; } puck.AssignToPlayer(target); Debug.Log($"{target.stats.playerName} received the puck!"); } else { Debug.Log($"Puck too far from {target.stats.playerName} ({distance:F1}m) - Puck at {puck.transform.position}, Target at {target.transform.position}"); } } } private void ExecuteStruggle() { Debug.Log("Faceoff is a struggle! Puck is loose!"); if (puck != null) { puck.Release(); // Launch puck in random direction Vector3 randomDirection = new Vector3( Random.Range(-1f, 1f), 0.1f, // Slight upward component Random.Range(-1f, 1f) ).normalized; puck.ApplyForce(randomDirection * struggleLaunchForce, ForceMode.Impulse); } StartCoroutine(ResolveStruggle()); } private IEnumerator ResolveStruggle() { yield return new WaitForSeconds(struggleDuration * 0.5f); // Find closest player to puck PlayerController closestPlayer = null; float closestDistance = float.MaxValue; List allPlayers = new List(); allPlayers.AddRange(homeTeam); allPlayers.AddRange(awayTeam); foreach (var player in allPlayers) { float distance = Vector3.Distance(player.transform.position, puck.transform.position); if (distance < closestDistance) { closestDistance = distance; closestPlayer = player; } } if (closestPlayer != null && closestDistance < 3f) { puck.AssignToPlayer(closestPlayer); Debug.Log($"{closestPlayer.stats.playerName} gains control after struggle!"); } } private PlayerController ChoosePassTarget(PlayerController winner, List team) { // Prefer defenders for breakout List defenders = team.Where(p => p != winner && (p.stats.position == PlayerPosition.LD || p.stats.position == PlayerPosition.RD) ).ToList(); if (defenders.Count > 0) { // Choose closest defender return defenders.OrderBy(d => Vector3.Distance(d.transform.position, winner.transform.position) ).First(); } // If no defenders, choose any teammate List availableTeammates = team.Where(p => p != winner).ToList(); if (availableTeammates.Count > 0) { return availableTeammates[Random.Range(0, availableTeammates.Count)]; } return winner; } private void FreezeAllPlayers(bool freeze) { foreach (var player in homeTeam) { if (player != null) { player.SetFrozen(freeze); } } foreach (var player in awayTeam) { if (player != null) { player.SetFrozen(freeze); } } } public void InitiateFaceoff(Vector3 faceoffPosition) { if (currentFaceoffSpot == null) { GameObject spot = new GameObject("FaceoffSpot"); currentFaceoffSpot = spot.transform; } currentFaceoffSpot.position = faceoffPosition; StartFaceoff(); } void Update() { if (Keyboard.current != null && !faceoffInProgress) { if (Keyboard.current.fKey.wasPressedThisFrame) { StartFaceoffAt(centerIceFaceoffSpot); } else if (Keyboard.current.digit1Key.wasPressedThisFrame) { StartFaceoffAt(homeZoneLeftFaceoffSpot); } else if (Keyboard.current.digit2Key.wasPressedThisFrame) { StartFaceoffAt(homeZoneRightFaceoffSpot); } else if (Keyboard.current.digit3Key.wasPressedThisFrame) { StartFaceoffAt(awayZoneLeftFaceoffSpot); } else if (Keyboard.current.digit4Key.wasPressedThisFrame) { StartFaceoffAt(awayZoneRightFaceoffSpot); } else if (Keyboard.current.digit5Key.wasPressedThisFrame) { StartFaceoffAt(homeZoneBlueLineLeftFaceoffSpot); } else if (Keyboard.current.digit6Key.wasPressedThisFrame) { StartFaceoffAt(homeZoneBlueLineRightFaceoffSpot); } else if (Keyboard.current.digit7Key.wasPressedThisFrame) { StartFaceoffAt(awayZoneBlueLineLeftFaceoffSpot); } else if (Keyboard.current.digit8Key.wasPressedThisFrame) { StartFaceoffAt(awayZoneBlueLineRightFaceoffSpot); } } } }