FaceoffManager.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using UnityEngine.InputSystem;
  6. public class FaceoffManager : MonoBehaviour
  7. {
  8. [Header("References")]
  9. [SerializeField] private PuckController puck;
  10. [Header("Faceoff Spots")]
  11. [SerializeField] private Transform centerIceFaceoffSpot;
  12. [SerializeField] private Transform homeZoneLeftFaceoffSpot;
  13. [SerializeField] private Transform homeZoneRightFaceoffSpot;
  14. [SerializeField] private Transform awayZoneLeftFaceoffSpot;
  15. [SerializeField] private Transform awayZoneRightFaceoffSpot;
  16. [SerializeField] private Transform homeZoneBlueLineLeftFaceoffSpot;
  17. [SerializeField] private Transform homeZoneBlueLineRightFaceoffSpot;
  18. [SerializeField] private Transform awayZoneBlueLineLeftFaceoffSpot;
  19. [SerializeField] private Transform awayZoneBlueLineRightFaceoffSpot;
  20. private Transform currentFaceoffSpot;
  21. [Header("Faceoff Settings")]
  22. [SerializeField] private float faceoffDuration = 2.0f;
  23. [SerializeField] private float struggleDuration = 3.0f;
  24. [SerializeField] private float struggleChance = 0.3f;
  25. [SerializeField] private float winThreshold = 0.7f;
  26. [Header("Puck Launch Settings")]
  27. [SerializeField] private float passForce = 8f; // Force to send puck to teammate
  28. [SerializeField] private float struggleLaunchForce = 5f; // Force when puck is loose
  29. private PlayerController homeCenterPlayer;
  30. private PlayerController awayCenterPlayer;
  31. private List<PlayerController> homeTeam = new List<PlayerController>();
  32. private List<PlayerController> awayTeam = new List<PlayerController>();
  33. private bool faceoffInProgress = false;
  34. private FaceoffOutcome currentOutcome;
  35. public enum FaceoffOutcome
  36. {
  37. None,
  38. HomeWin,
  39. AwayWin,
  40. Struggle
  41. }
  42. void Start()
  43. {
  44. FindPlayers();
  45. }
  46. void FindPlayers()
  47. {
  48. PlayerController[] allPlayers = FindObjectsByType<PlayerController>(FindObjectsSortMode.None);
  49. homeTeam.Clear();
  50. awayTeam.Clear();
  51. foreach (var player in allPlayers)
  52. {
  53. if (player.CompareTag("HomeTeam"))
  54. {
  55. homeTeam.Add(player);
  56. if (player.stats.position == PlayerPosition.C)
  57. {
  58. homeCenterPlayer = player;
  59. }
  60. }
  61. else if (player.CompareTag("AwayTeam"))
  62. {
  63. awayTeam.Add(player);
  64. if (player.stats.position == PlayerPosition.C)
  65. {
  66. awayCenterPlayer = player;
  67. }
  68. }
  69. }
  70. Debug.Log($"Found {homeTeam.Count} home players and {awayTeam.Count} away players");
  71. }
  72. public void StartFaceoffAt(Transform faceoffSpot)
  73. {
  74. if (faceoffSpot == null)
  75. {
  76. Debug.LogWarning("Faceoff spot is not assigned!");
  77. return;
  78. }
  79. currentFaceoffSpot = faceoffSpot;
  80. StartFaceoff();
  81. }
  82. public void StartFaceoff()
  83. {
  84. if (faceoffInProgress)
  85. {
  86. Debug.LogWarning("Faceoff already in progress!");
  87. return;
  88. }
  89. if (currentFaceoffSpot == null)
  90. {
  91. currentFaceoffSpot = centerIceFaceoffSpot;
  92. }
  93. if (homeCenterPlayer == null || awayCenterPlayer == null)
  94. {
  95. Debug.LogError("Missing center players for faceoff!");
  96. return;
  97. }
  98. StartCoroutine(FaceoffSequence());
  99. }
  100. private IEnumerator FaceoffSequence()
  101. {
  102. faceoffInProgress = true;
  103. Debug.Log("Faceoff starting...");
  104. // Position puck at faceoff spot
  105. if (puck != null && currentFaceoffSpot != null)
  106. {
  107. puck.transform.position = currentFaceoffSpot.position + Vector3.up * 0.025f; // Slight lift
  108. puck.Release(); // Make sure puck is free
  109. }
  110. FreezeAllPlayers(true);
  111. yield return new WaitForSeconds(1.0f);
  112. currentOutcome = DetermineFaceoffOutcome();
  113. Debug.Log($"Faceoff outcome: {currentOutcome}");
  114. yield return new WaitForSeconds(faceoffDuration);
  115. switch (currentOutcome)
  116. {
  117. case FaceoffOutcome.HomeWin:
  118. ExecuteCleanWin(homeCenterPlayer, homeTeam, awayTeam);
  119. break;
  120. case FaceoffOutcome.AwayWin:
  121. ExecuteCleanWin(awayCenterPlayer, awayTeam, homeTeam);
  122. break;
  123. case FaceoffOutcome.Struggle:
  124. ExecuteStruggle();
  125. yield return new WaitForSeconds(struggleDuration);
  126. break;
  127. }
  128. FreezeAllPlayers(false);
  129. faceoffInProgress = false;
  130. Debug.Log("Faceoff complete!");
  131. }
  132. private FaceoffOutcome DetermineFaceoffOutcome()
  133. {
  134. if (homeCenterPlayer == null || awayCenterPlayer == null)
  135. return FaceoffOutcome.Struggle;
  136. float homeStrength = CalculateFaceoffStrength(homeCenterPlayer);
  137. float awayStrength = CalculateFaceoffStrength(awayCenterPlayer);
  138. float totalStrength = homeStrength + awayStrength;
  139. float homeWinChance = homeStrength / totalStrength;
  140. if (Mathf.Abs(homeWinChance - 0.5f) < (0.5f - winThreshold))
  141. {
  142. return FaceoffOutcome.Struggle;
  143. }
  144. float roll = Random.value;
  145. if (roll < struggleChance)
  146. {
  147. return FaceoffOutcome.Struggle;
  148. }
  149. return Random.value < homeWinChance ? FaceoffOutcome.HomeWin : FaceoffOutcome.AwayWin;
  150. }
  151. private float CalculateFaceoffStrength(PlayerController player)
  152. {
  153. return (player.stats.strength * 0.3f) +
  154. (player.stats.stickHandling * 0.3f) +
  155. (player.stats.awareness * 0.2f) +
  156. (player.stats.agility * 0.2f);
  157. }
  158. private void ExecuteCleanWin(PlayerController winner, List<PlayerController> winningTeam, List<PlayerController> losingTeam)
  159. {
  160. Debug.Log($"{winner.stats.playerName} wins the faceoff cleanly!");
  161. // Find best pass target FIRST (before assigning puck)
  162. PlayerController target = ChoosePassTarget(winner, winningTeam);
  163. if (target != null && target != winner)
  164. {
  165. Debug.Log($"Puck sent to {target.stats.playerName}");
  166. // Calculate direction from FACEOFF SPOT (not puck position, since puck might be carried)
  167. Vector3 startPosition = currentFaceoffSpot.position;
  168. Vector3 targetPosition = target.transform.position;
  169. Vector3 passDirection = (targetPosition - startPosition).normalized;
  170. float distance = Vector3.Distance(startPosition, targetPosition);
  171. float adjustedForce = passForce * Mathf.Clamp(distance / 5f, 0.8f, 2f);
  172. // Make sure puck is free and at faceoff spot
  173. if (puck != null)
  174. {
  175. puck.Release();
  176. puck.transform.position = startPosition + Vector3.up * 0.025f;
  177. // Apply force
  178. puck.ApplyForce(passDirection * adjustedForce, ForceMode.Impulse);
  179. }
  180. // Delay assignment to let puck travel
  181. StartCoroutine(AssignPuckAfterDelay(target, 0.3f));
  182. }
  183. else
  184. {
  185. Debug.Log($"{winner.stats.playerName} keeps the puck!");
  186. if (puck != null)
  187. {
  188. puck.AssignToPlayer(winner);
  189. }
  190. }
  191. }
  192. private IEnumerator AssignPuckAfterDelay(PlayerController target, float delay)
  193. {
  194. yield return new WaitForSeconds(delay);
  195. // Check if puck is close enough to target
  196. if (puck != null && target != null && puck.IsLoose)
  197. {
  198. float distance = Vector3.Distance(puck.transform.position, target.transform.position);
  199. if (distance < 3f) // Increased pickup range
  200. {
  201. // Stop the puck's movement before assigning
  202. Rigidbody puckRb = puck.GetComponent<Rigidbody>();
  203. if (puckRb != null)
  204. {
  205. puckRb.linearVelocity *= 0.3f; // Slow down significantly
  206. puckRb.angularVelocity = Vector3.zero; // Stop spinning
  207. }
  208. puck.AssignToPlayer(target);
  209. Debug.Log($"{target.stats.playerName} received the puck!");
  210. }
  211. else
  212. {
  213. Debug.Log($"Puck too far from {target.stats.playerName} ({distance:F1}m)");
  214. }
  215. }
  216. }
  217. private void ExecuteStruggle()
  218. {
  219. Debug.Log("Faceoff is a struggle! Puck is loose!");
  220. if (puck != null)
  221. {
  222. puck.Release();
  223. // Launch puck in random direction
  224. Vector3 randomDirection = new Vector3(
  225. Random.Range(-1f, 1f),
  226. 0.1f, // Slight upward component
  227. Random.Range(-1f, 1f)
  228. ).normalized;
  229. puck.ApplyForce(randomDirection * struggleLaunchForce, ForceMode.Impulse);
  230. }
  231. StartCoroutine(ResolveStruggle());
  232. }
  233. private IEnumerator ResolveStruggle()
  234. {
  235. yield return new WaitForSeconds(struggleDuration * 0.5f);
  236. // Find closest player to puck
  237. PlayerController closestPlayer = null;
  238. float closestDistance = float.MaxValue;
  239. List<PlayerController> allPlayers = new List<PlayerController>();
  240. allPlayers.AddRange(homeTeam);
  241. allPlayers.AddRange(awayTeam);
  242. foreach (var player in allPlayers)
  243. {
  244. float distance = Vector3.Distance(player.transform.position, puck.transform.position);
  245. if (distance < closestDistance)
  246. {
  247. closestDistance = distance;
  248. closestPlayer = player;
  249. }
  250. }
  251. if (closestPlayer != null && closestDistance < 3f)
  252. {
  253. puck.AssignToPlayer(closestPlayer);
  254. Debug.Log($"{closestPlayer.stats.playerName} gains control after struggle!");
  255. }
  256. }
  257. private PlayerController ChoosePassTarget(PlayerController winner, List<PlayerController> team)
  258. {
  259. // Prefer defenders for breakout
  260. List<PlayerController> defenders = team.Where(p =>
  261. p != winner && (p.stats.position == PlayerPosition.LD || p.stats.position == PlayerPosition.RD)
  262. ).ToList();
  263. if (defenders.Count > 0)
  264. {
  265. // Choose closest defender
  266. return defenders.OrderBy(d =>
  267. Vector3.Distance(d.transform.position, winner.transform.position)
  268. ).First();
  269. }
  270. // If no defenders, choose any teammate
  271. List<PlayerController> availableTeammates = team.Where(p => p != winner).ToList();
  272. if (availableTeammates.Count > 0)
  273. {
  274. return availableTeammates[Random.Range(0, availableTeammates.Count)];
  275. }
  276. return winner;
  277. }
  278. private void FreezeAllPlayers(bool freeze)
  279. {
  280. foreach (var player in homeTeam)
  281. {
  282. if (player != null)
  283. {
  284. player.SetFrozen(freeze);
  285. }
  286. }
  287. foreach (var player in awayTeam)
  288. {
  289. if (player != null)
  290. {
  291. player.SetFrozen(freeze);
  292. }
  293. }
  294. }
  295. public void InitiateFaceoff(Vector3 faceoffPosition)
  296. {
  297. if (currentFaceoffSpot == null)
  298. {
  299. GameObject spot = new GameObject("FaceoffSpot");
  300. currentFaceoffSpot = spot.transform;
  301. }
  302. currentFaceoffSpot.position = faceoffPosition;
  303. StartFaceoff();
  304. }
  305. void Update()
  306. {
  307. if (Keyboard.current != null && !faceoffInProgress)
  308. {
  309. if (Keyboard.current.fKey.wasPressedThisFrame)
  310. {
  311. StartFaceoffAt(centerIceFaceoffSpot);
  312. }
  313. else if (Keyboard.current.digit1Key.wasPressedThisFrame)
  314. {
  315. StartFaceoffAt(homeZoneLeftFaceoffSpot);
  316. }
  317. else if (Keyboard.current.digit2Key.wasPressedThisFrame)
  318. {
  319. StartFaceoffAt(homeZoneRightFaceoffSpot);
  320. }
  321. else if (Keyboard.current.digit3Key.wasPressedThisFrame)
  322. {
  323. StartFaceoffAt(awayZoneLeftFaceoffSpot);
  324. }
  325. else if (Keyboard.current.digit4Key.wasPressedThisFrame)
  326. {
  327. StartFaceoffAt(awayZoneRightFaceoffSpot);
  328. }
  329. else if (Keyboard.current.digit5Key.wasPressedThisFrame)
  330. {
  331. StartFaceoffAt(homeZoneBlueLineLeftFaceoffSpot);
  332. }
  333. else if (Keyboard.current.digit6Key.wasPressedThisFrame)
  334. {
  335. StartFaceoffAt(homeZoneBlueLineRightFaceoffSpot);
  336. }
  337. else if (Keyboard.current.digit7Key.wasPressedThisFrame)
  338. {
  339. StartFaceoffAt(awayZoneBlueLineLeftFaceoffSpot);
  340. }
  341. else if (Keyboard.current.digit8Key.wasPressedThisFrame)
  342. {
  343. StartFaceoffAt(awayZoneBlueLineRightFaceoffSpot);
  344. }
  345. }
  346. }
  347. }