using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; using TMPro; using Mirror; public class GameManager : NetworkBehaviour { public static Action OnTeamChanged; public static bool isMoving{ get{ return instance.IsMoving(); } } [SyncVar] public bool m_isMoving =false; [SyncVar(hook = nameof(OnGameStartedChanged))] public bool gameStarted = false; void OnGameStartedChanged(bool oldStarted, bool newStarted){ if(newStarted){ GameCanvas.instance.HideWaitingForOpponentPanel(); }else{ GameCanvas.instance.ShowWaitingForOpponentPanel(); } } [SyncVar(hook = nameof(OnSelectedTeamChanged))] public Team SelectedTeam = Team.Red; void OnSelectedTeamChanged(Team oldTeam, Team newTeam){ Debug.Log($"Selected team changed from {oldTeam} to {newTeam}"); OnTeamChanged?.Invoke(newTeam); } public static bool NoPuckSelected { get{ return instance.selectedPuck == null; } } public int turnTimer = 10; [SyncVar] public float turnTimerCounter; public Rigidbody2D ball; public float puckForce = 20f; public AnimationCurve puckForceCurve = AnimationCurve.Linear(0,3,1,1); public float ballMoveTime = 3f; public float puckDragClampMax = 5f; public float puckDragMinClamp = 1f; [SyncVar(hook = nameof(OnScoreChanged))] public int redScore=0; [SyncVar(hook = nameof(OnScoreChanged))] public int blueScore=0; [Header("UI")] public TMP_Text redScoreText; public TMP_Text blueScoreText; public GameObject gameOverPanel; public GameObject blueWon,redWon; [Header("Misc")] public List pucks = new List(); public float curPuckPullForce; [SyncVar] public int hitCounter = 0; public static GameManager instance; public float puckMass{ get{ if(pucks.Count > 0){return pucks[0].rb.mass;}else{return 1f;} }set{ foreach(Puck puck in pucks){ puck.rb.mass = value; } } } public float puckDrag{ get{ if(pucks.Count > 0){return pucks[0].rb.linearDamping;}else{return 0f;} }set{ foreach(Puck puck in pucks){ puck.rb.linearDamping = value; } } } void Awake() { instance = this; } void Start() { GameEvents.OnSelectedPuckChanged?.Invoke(null); #if UNITY_EDITOR #else Application.targetFrameRate = isServer ? 30 : 100; #endif } public void RegisterPuck(Puck puck){ pucks.Add(puck); } public void DisposePuck(Puck puck){ pucks.Remove(puck); } [SyncVar(hook = nameof(OnSelectedPuckChanged))] [SerializeField]Puck selectedPuck; void OnSelectedPuckChanged(Puck oldPuck, Puck newPuck){ // Debug.Log($"Selected puck changed from {oldPuck} to {newPuck}"); GameEvents.OnSelectedPuckChanged?.Invoke(newPuck); } public static Puck SelectedPuck{ get{ return instance.selectedPuck; } set{ instance.SetSelectedPuck(value); } } void SetSelectedPuck(Puck puck) { selectedPuck = puck; GameEvents.OnSelectedPuckChanged?.Invoke(puck); } public Puck GetClosestPuck(Vector2 position){ if(IsMoving()){return null;} //Do not do anything if shits moving // Convert screen position to world position // Vector3 worldPosition = Camera.main.ScreenToWorldPoint(new Vector3(position.x, position.y, Camera.main.nearClipPlane)); // Find all pucks in the scene Puck[] pucks = FindObjectsOfType(); float minDistance = float.MaxValue; Puck closestPuck = null; foreach (Puck puck in pucks) { if(puck.team != SelectedTeam){continue;} float dist = Vector2.Distance(position, puck.transform.position); if (dist < minDistance) { minDistance = dist; closestPuck = puck; } } return closestPuck; } Coroutine coroutinePostLaunch; public void OnPointerUp(Vector2 direction) { if(direction.magnitude < puckDragMinClamp){ selectedPuck = null; GameEvents.OnSelectedPuckChanged?.Invoke(null); return; } if(coroutinePostLaunch != null){ StopCoroutine(coroutinePostLaunch); } coroutinePostLaunch = StartCoroutine(CoroutinePostLaunch()); curPuckPullForce = direction.magnitude; float force = puckForce * puckForceCurve.Evaluate(curPuckPullForce); Debug.Log($"force = {puckForce} * {puckForceCurve.Evaluate(curPuckPullForce)} = {force}"); Debug.Log($"launching puck at {direction} with {force * -direction} force"); selectedPuck.GetComponent().AddForce(-direction * force, ForceMode2D.Impulse); hitCounter++; selectedPuck = null; GameEvents.OnSelectedPuckChanged?.Invoke(null); SwitchTeams(); } void SwitchTeams(){ if(!isServer){ Debug.LogWarning("SwitchTeams called on client, skipping"); return; } StartCoroutine(CoroutineSwitchTeams()); } bool switchingTeams=false; IEnumerator CoroutineSwitchTeams(){ switchingTeams=true; for(int i=0; i < 3; i++){ yield return null; } while(m_isMoving){ yield return null; } turnTimerCounter = 0; SelectedTeam = SelectedTeam == Team.Red ? Team.Blue : Team.Red; OnTeamChanged?.Invoke(SelectedTeam); switchingTeams=false; } IEnumerator CoroutinePostLaunch(){ float t = 0; while ( t < 1){ t += Time.deltaTime / ballMoveTime; ball.linearDamping = Mathf.Lerp(0, 10, t); yield return null; } } bool IsMoving(){ if(freezeInput){return true;} float minMagnitude = 0.01f; foreach(Puck puck in pucks){ if(puck.rb.linearVelocity.magnitude > minMagnitude){ return true; } } if(ball.linearVelocity.magnitude > minMagnitude){ return true; } return false; } void Update() { if(isServer){ m_isMoving=IsMoving(); HandleTurnTimer(); if(!gameStarted){ NetPlayer[] players = FindObjectsOfType(); if (players.Length == 2) { gameStarted = true; GameCanvas.instance.HideWaitingForOpponentPanel(); Debug.Log("Game started On Server"); } } } } void HandleTurnTimer(){ if(turnTimerCounter < turnTimer){ if(!isMoving && !switchingTeams && gameStarted){ turnTimerCounter += Time.deltaTime; } }else{ SwitchTeams(); turnTimerCounter = 0; } } public void OnGoal(Team team){ if(!isServer){ Debug.LogWarning("OnGoal called on client, skipping"); return;} freezeInput=true; if(hitCounter == 1){ //kickoff goal Debug.Log("Kickoff goal"); StartCoroutine(CoroutineOnGoal(team,true));//true = Kickoff goal, reset only the ball hitCounter = 0; return; } if(team == Team.Blue){ blueScore++; blueScoreText.text = blueScore.ToString(); }else{ redScore++; redScoreText.text = redScore.ToString(); } SwitchTeams(); if(blueScore >= 3){ RpcGameOver(Team.Blue); gameOver(Team.Blue); }else if(redScore >= 3){ RpcGameOver(Team.Red); gameOver(Team.Red); }else{ StartCoroutine(CoroutineOnGoal(team)); } hitCounter = 0; } [ClientRpc] void RpcGameOver(Team team){ gameOver(team); } void gameOver(Team team){ gameOverPanel.SetActive(true); if(team == Team.Blue){ blueWon.SetActive(true); redWon.SetActive(false); }else{ blueWon.SetActive(false); redWon.SetActive(true); } } bool freezeInput = false; IEnumerator CoroutineOnGoal(Team team, bool kickoff = false){ if(coroutinePostLaunch!=null){ StopCoroutine(coroutinePostLaunch); } float t=0; while(t < 1){ t +=Time.deltaTime * 2f; ball.linearDamping = Mathf.Lerp(0, 100, t); yield return null; } yield return new WaitForSeconds(2f); Reset(); } void OnScoreChanged(int oldScore, int newScore){ Debug.Log($"Score changed from {oldScore} to {newScore}"); redScoreText.text = redScore.ToString(); blueScoreText.text = blueScore.ToString(); } public void Reset(bool kickoff = false) { StartCoroutine(CoroutineReset(kickoff)); } IEnumerator CoroutineReset(bool kickoff = false){ float resetDuration = 0.5f; if(!kickoff){ foreach(Puck puck in pucks){ puck.Reset(null, resetDuration); } } ball.GetComponent().Reset(resetDuration); yield return new WaitForSeconds(resetDuration); freezeInput=false; } }