696 lines
22 KiB
C#
696 lines
22 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.Networking;
|
|
using UnityEngine.Serialization;
|
|
using TMPro;
|
|
using Mirror;
|
|
|
|
public class GameManager : NetworkBehaviour
|
|
{
|
|
public static MatchmadePlayer RedPlayer { get; set; }
|
|
public static MatchmadePlayer BluePlayer { get; set; }
|
|
/// <summary>Set by dedicated server startup (e.g. CupidConnector parsing <c>-matchId</c>). PATCH is skipped if id ≤ 0 or secret is empty.</summary>
|
|
public static int DedicatedMatchId { get; private set; }
|
|
/// <summary>Sent as <c>X-Dedicated-Server-Secret</c>. From <c>-dedicatedSecret</c> arg or <c>DEDICATED_SERVER_SECRET</c> env.</summary>
|
|
public static string DedicatedMatchSecret { get; private set; } = "";
|
|
/// <summary>Base URL for internal match API (no trailing slash), default <c>http://127.0.0.1:2612</c>.</summary>
|
|
public static string DedicatedInternalApiBase { get; private set; } = LoginManager.AuthBaseUrl;
|
|
|
|
public static void ConfigureDedicatedMatchReporting(int matchId, string secret=null, string internalApiBaseUrl = null)
|
|
{
|
|
DedicatedMatchId = matchId;
|
|
DedicatedMatchSecret = secret ?? "38516e39d3406225b7c3015a66e5d25c54e9b2024c0755212122bdcea3e3a328";
|
|
if (!string.IsNullOrWhiteSpace(internalApiBaseUrl))
|
|
DedicatedInternalApiBase = internalApiBaseUrl.TrimEnd('/');
|
|
|
|
Logger.Log($"Configured dedicated match reporting for match id {matchId} with secret {DedicatedMatchSecret} and internal api base {DedicatedInternalApiBase}");
|
|
}
|
|
|
|
/// <summary>Sets match <c>status</c> to <c>-1</c> (room closed). Safe to call from shutdown paths; runs synchronously so it completes before <c>Application.Quit</c>.</summary>
|
|
public static void ReportDedicatedMatchRoomClosed()
|
|
{
|
|
if (_dedicatedRoomClosedReported || DedicatedMatchId <= 0 || string.IsNullOrEmpty(DedicatedMatchSecret))
|
|
return;
|
|
_dedicatedRoomClosedReported = true;
|
|
|
|
if (DedicatedMatchId <= 0 || string.IsNullOrEmpty(DedicatedMatchSecret))
|
|
return;
|
|
|
|
using (var req = DedicatedMatschInternalApi.BuildRequest(DedicatedInternalApiBase, DedicatedMatchId, DedicatedMatchSecret, "{\"status\":-1}"))
|
|
{
|
|
var op = req.SendWebRequest();
|
|
while (!op.isDone)
|
|
System.Threading.Thread.Sleep(16);
|
|
DedicatedMatschInternalApi.LogIfFailed(req);
|
|
}
|
|
}
|
|
|
|
static bool _dedicatedRoomClosedReported;
|
|
static string Iso8601UtcNow() => DateTime.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'", System.Globalization.CultureInfo.InvariantCulture);
|
|
|
|
bool _dedicatedReportedRedJoin;
|
|
bool _dedicatedReportedBlueJoin;
|
|
bool _dedicatedReportedGameOver;
|
|
|
|
public static Action<Team> 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();
|
|
if (AudioManager.instance != null)
|
|
AudioManager.instance.SetCrowdNoiseType(CrowdNoiseType.Normal);
|
|
}else{
|
|
GameCanvas.instance.ShowWaitingForOpponentPanel();
|
|
}
|
|
}
|
|
|
|
[SyncVar(hook = nameof(OnSelectedTeamChanged))]
|
|
public Team SelectedTeam = Team.Red;
|
|
void OnSelectedTeamChanged(Team oldTeam, Team newTeam){
|
|
Logger.Log($"Selected team changed from {oldTeam} to {newTeam}");
|
|
OnTeamChanged?.Invoke(newTeam);
|
|
|
|
if (!isClient || !gameStarted || oldTeam == newTeam)
|
|
return;
|
|
if (AudioManager.instance != null)
|
|
AudioManager.instance.PlayRefereeWhistle();
|
|
}
|
|
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("Effects")]
|
|
public ParticleSystem[] redGoalEffects;
|
|
public ParticleSystem[] blueGoalEffects;
|
|
|
|
[Header("UI")]
|
|
public TMP_Text redScoreText;
|
|
public TMP_Text blueScoreText;
|
|
public CanvasGroupUtils blueTimerGroup;
|
|
[FormerlySerializedAs("timerGroup")] public CanvasGroupUtils redTimerGroup;
|
|
[Header("Timer Flash Settings")]
|
|
[Tooltip("Minimum flash speed when timer is at 5 seconds")]
|
|
public float minFlashSpeed = 10f;
|
|
[Tooltip("Maximum flash speed when timer is at 0 seconds")]
|
|
public float maxFlashSpeed = 20f;
|
|
|
|
[Header("Misc")]
|
|
public List<Puck> pucks = new List<Puck>();
|
|
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;
|
|
}
|
|
|
|
/// <summary>Server-only: called from <see cref="NetPlayer.CmdSetTeam"/> after <c>myTeam</c> is set.</summary>
|
|
public void OnMatchPlayerTeamAssigned(Team team)
|
|
{
|
|
if (!isServer || DedicatedMatchId <= 0 || string.IsNullOrEmpty(DedicatedMatchSecret))
|
|
return;
|
|
|
|
int n = FindObjectsByType<NetPlayer>(FindObjectsSortMode.None).Length;
|
|
string ts = Iso8601UtcNow();
|
|
|
|
if (team == Team.Red && !_dedicatedReportedRedJoin)
|
|
{
|
|
_dedicatedReportedRedJoin = true;
|
|
int status = n >= 2 ? 2 : 1;
|
|
string json = "{\"red_joined_at\":\"" + ts + "\",\"status\":" + status + "}";
|
|
StartCoroutine(DedicatedMatchPatch(DedicatedInternalApiBase, DedicatedMatchId, DedicatedMatchSecret, json));
|
|
// StartCoroutine(DedicatedMatchInternalApi.Patch(DedicatedInternalApiBase, DedicatedMatchId, DedicatedMatchSecret, json));
|
|
}
|
|
else if (team == Team.Blue && !_dedicatedReportedBlueJoin)
|
|
{
|
|
_dedicatedReportedBlueJoin = true;
|
|
int status = n >= 2 ? 2 : 1;
|
|
string json = "{\"blue_joined_at\":\"" + ts + "\",\"status\":" + status + "}";
|
|
StartCoroutine(DedicatedMatchPatch(DedicatedInternalApiBase, DedicatedMatchId, DedicatedMatchSecret, json));
|
|
}
|
|
}
|
|
|
|
IEnumerator DedicatedMatchPatch(string baseUrl, int matchId, string secret, string json){
|
|
if (matchId <= 0 || string.IsNullOrEmpty(secret))
|
|
yield break;
|
|
|
|
using (var req = DedicatedMatschInternalApi.BuildRequest(baseUrl, matchId, secret, json))
|
|
{
|
|
yield return req.SendWebRequest();
|
|
DedicatedMatschInternalApi.LogIfFailed(req);
|
|
}
|
|
}
|
|
|
|
public IEnumerator DedicatedMatchPatchWinner(string baseUrl, int matchId, string secret, string winnerLower)
|
|
{
|
|
if (matchId <= 0 || string.IsNullOrEmpty(secret))
|
|
yield break;
|
|
|
|
string jsonBody = "{\"winner\":\"" + winnerLower + "\"}";
|
|
using (var req = DedicatedMatschInternalApi.BuildWinnerRequest(baseUrl, matchId, secret, jsonBody))
|
|
{
|
|
yield return req.SendWebRequest();
|
|
DedicatedMatschInternalApi.LogIfFailed(req);
|
|
}
|
|
}
|
|
|
|
void ReportDedicatedMatchGameOver(Team winningTeam)
|
|
{
|
|
if (!isServer || DedicatedMatchId <= 0 || string.IsNullOrEmpty(DedicatedMatchSecret) || _dedicatedReportedGameOver)
|
|
return;
|
|
_dedicatedReportedGameOver = true;
|
|
StartCoroutine(CoReportDedicatedMatchGameOver(winningTeam));
|
|
}
|
|
|
|
IEnumerator CoReportDedicatedMatchGameOver(Team winningTeam)
|
|
{
|
|
yield return DedicatedMatchPatch(DedicatedInternalApiBase, DedicatedMatchId, DedicatedMatchSecret, "{\"status\":3}");
|
|
string winner = winningTeam == Team.Red ? "red" : "blue";
|
|
yield return DedicatedMatchPatchWinner(DedicatedInternalApiBase, DedicatedMatchId, DedicatedMatchSecret, winner);
|
|
}
|
|
|
|
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){
|
|
// Logger.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<Puck>();
|
|
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);
|
|
Logger.Log($"force = {puckForce} * {puckForceCurve.Evaluate(curPuckPullForce)} = {force}");
|
|
Logger.Log($"launching puck at {direction} with {force * -direction} force");
|
|
selectedPuck.GetComponent<Rigidbody2D>().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<NetPlayer>();
|
|
#if UNITY_EDITOR
|
|
if(Input.GetKeyDown(KeyCode.Space)){
|
|
gameStarted = true;
|
|
GameCanvas.instance.HideWaitingForOpponentPanel();
|
|
Logger.Log("Game started On Server, EDITOR ONLY, DEBUG ONLY");
|
|
}
|
|
#endif
|
|
if (players.Length == 2)
|
|
{
|
|
gameStarted = true;
|
|
GameCanvas.instance.HideWaitingForOpponentPanel();
|
|
Logger.Log("Game started On Server");
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateLocalTurnTimerBeep();
|
|
UpdateTimerColor();
|
|
}
|
|
|
|
void UpdateLocalTurnTimerBeep()
|
|
{
|
|
if (!isClient || NetPlayer.localPlayer == null)
|
|
return;
|
|
|
|
if (!gameStarted || SelectedTeam != NetPlayer.localPlayer.myTeam)
|
|
{
|
|
_lastTurnTimerBeepFloor = -1;
|
|
return;
|
|
}
|
|
|
|
if (m_isMoving || turnTimerCounter >= turnTimer)
|
|
return;
|
|
|
|
float remaining = turnTimer - turnTimerCounter;
|
|
int floorSec = Mathf.FloorToInt(remaining);
|
|
|
|
if (floorSec != _lastTurnTimerBeepFloor)
|
|
{
|
|
if (_lastTurnTimerBeepFloor >= 0 && floorSec < _lastTurnTimerBeepFloor
|
|
&& AudioManager.instance != null)
|
|
AudioManager.instance.PlayTimerBeep();
|
|
|
|
_lastTurnTimerBeepFloor = floorSec;
|
|
}
|
|
}
|
|
|
|
void HandleTurnTimer(){
|
|
if(turnTimerCounter < turnTimer){
|
|
if(!isMoving && !switchingTeams && gameStarted){
|
|
turnTimerCounter += Time.deltaTime;
|
|
}
|
|
}else{
|
|
SwitchTeams();
|
|
turnTimerCounter = 0;
|
|
}
|
|
}
|
|
|
|
float flashPhase = 0f;
|
|
float lastUpdateTime = 0f;
|
|
float lastRemainingTime = 10f;
|
|
|
|
int _lastTurnTimerBeepFloor = -1;
|
|
|
|
void UpdateTimerColor(){
|
|
float remainingTime = turnTimer - turnTimerCounter;
|
|
|
|
// Detect timer reset: if remainingTime jumped from low to high, reset phase
|
|
if(remainingTime > lastRemainingTime + 2f){
|
|
flashPhase = 0f;
|
|
lastUpdateTime = 0f;
|
|
}
|
|
lastRemainingTime = remainingTime;
|
|
|
|
CanvasGroupUtils activeTimerGroup = SelectedTeam == Team.Red ? redTimerGroup : blueTimerGroup;
|
|
CanvasGroupUtils inactiveTimerGroup = SelectedTeam == Team.Red ? blueTimerGroup : redTimerGroup;
|
|
|
|
if(inactiveTimerGroup != null){
|
|
inactiveTimerGroup.overrideColor = Color.white;
|
|
}
|
|
|
|
if(activeTimerGroup == null){
|
|
if(remainingTime >= 5f || !gameStarted){
|
|
flashPhase = 0f;
|
|
lastUpdateTime = 0f;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(remainingTime < 5f && gameStarted){
|
|
// Calculate intensity based on how close timer is to 0
|
|
// Intensity increases from 0 to 1 as timer goes from 5 to 0
|
|
float intensity = 1f - (remainingTime / 5f);
|
|
|
|
// Create flashing effect using sine wave
|
|
// Flash speed increases as timer approaches 0
|
|
float flashSpeed = Mathf.Lerp(minFlashSpeed, maxFlashSpeed, intensity);
|
|
|
|
// Update phase based on delta time and current flash speed
|
|
// This ensures smooth flashing that resets with the timer
|
|
if(lastUpdateTime > 0f){
|
|
float deltaTime = Time.time - lastUpdateTime;
|
|
flashPhase += flashSpeed * deltaTime;
|
|
} else {
|
|
// First frame in warning zone, initialize phase
|
|
flashPhase = 0f;
|
|
}
|
|
lastUpdateTime = Time.time;
|
|
|
|
float flashValue = (Mathf.Sin(flashPhase) + 1f) * 0.5f; // 0 to 1
|
|
|
|
// Combine intensity with flash for final red intensity
|
|
// As timer approaches 0, intensity increases and flash becomes more prominent
|
|
float finalRedIntensity = intensity * (0.3f + flashValue * 0.7f); // Range: intensity*0.3 to intensity*1
|
|
|
|
// Lerp between white and red based on final intensity, keeping alpha at 1
|
|
Color white = Color.white;
|
|
Color red = Color.red;
|
|
activeTimerGroup.overrideColor = Color.Lerp(white, red, finalRedIntensity);
|
|
}else{
|
|
// Reset phase when timer is above 5 seconds or game hasn't started
|
|
flashPhase = 0f;
|
|
lastUpdateTime = 0f;
|
|
activeTimerGroup.overrideColor = Color.white;
|
|
}
|
|
}
|
|
|
|
|
|
public void OnGoal(Team team){
|
|
if(!isServer){
|
|
Debug.LogWarning("OnGoal called on client, skipping");
|
|
return;}
|
|
|
|
RpcPlayGoalScoredSfx();
|
|
|
|
freezeInput=true;
|
|
|
|
if(hitCounter == 1){
|
|
//kickoff goal
|
|
Logger.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();
|
|
PlayGoalEffects(Team.Blue);
|
|
}else{
|
|
redScore++;
|
|
redScoreText.text = redScore.ToString();
|
|
PlayGoalEffects(Team.Red);
|
|
}
|
|
|
|
SwitchTeams();
|
|
|
|
|
|
if(blueScore >= 3){
|
|
ReportDedicatedMatchGameOver(Team.Blue);
|
|
RpcGameOver(Team.Blue);
|
|
gameOver(Team.Blue);
|
|
}else if(redScore >= 3){
|
|
ReportDedicatedMatchGameOver(Team.Red);
|
|
RpcGameOver(Team.Red);
|
|
gameOver(Team.Red);
|
|
}else{
|
|
StartCoroutine(CoroutineOnGoal(team));
|
|
}
|
|
|
|
hitCounter = 0;
|
|
}
|
|
|
|
void PlayGoalEffects(Team team){
|
|
if(isServer){
|
|
m_PlayGoalEffects(team);
|
|
RpcPlayGoalEffects(team);
|
|
}else{
|
|
CmdPlayGoalEffects(team);
|
|
}
|
|
}
|
|
|
|
[Command]
|
|
void CmdPlayGoalEffects(Team team){
|
|
m_PlayGoalEffects(team);
|
|
RpcPlayGoalEffects(team);
|
|
}
|
|
|
|
[ClientRpc]
|
|
void RpcPlayGoalEffects(Team team){
|
|
m_PlayGoalEffects(team);
|
|
}
|
|
|
|
void m_PlayGoalEffects(Team team){
|
|
if(team == Team.Blue){
|
|
foreach(ParticleSystem effect in blueGoalEffects){
|
|
effect.Play();
|
|
}
|
|
}else{
|
|
foreach(ParticleSystem effect in redGoalEffects){
|
|
effect.Play();
|
|
}
|
|
}
|
|
}
|
|
|
|
[ClientRpc]
|
|
void RpcPlayGoalScoredSfx()
|
|
{
|
|
if (AudioManager.instance != null)
|
|
AudioManager.instance.PlayGoalScoredSfxSequence();
|
|
}
|
|
|
|
[ClientRpc]
|
|
void RpcGameOver(Team team){
|
|
gameOver(team);
|
|
}
|
|
|
|
void gameOver(Team team){
|
|
StartCoroutine(CoroutineGameOver(team));
|
|
}
|
|
|
|
IEnumerator CoroutineGameOver(Team team){
|
|
yield return new WaitForSeconds(2f);
|
|
LevelLoadManager.instance.SetupGameOver(team,redScore,blueScore);
|
|
}
|
|
|
|
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){
|
|
Logger.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<Ball>().Reset(resetDuration);
|
|
|
|
yield return new WaitForSeconds(resetDuration);
|
|
|
|
freezeInput=false;
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>Headless dedicated server → matchmaker internal PATCH. Runs only where started (server-side coroutines).</summary>
|
|
static class DedicatedMatschInternalApi
|
|
{
|
|
|
|
public static UnityWebRequest BuildRequest(string baseUrl, int matchId, string secret, string jsonBody)
|
|
{
|
|
string url = baseUrl.TrimEnd('/') + "/internal/match/" + matchId;
|
|
return BuildPatchJson(url, secret, jsonBody);
|
|
}
|
|
|
|
public static UnityWebRequest BuildWinnerRequest(string baseUrl, int matchId, string secret, string jsonBody)
|
|
{
|
|
string url = baseUrl.TrimEnd('/') + "/internal/match/" + matchId + "/winner";
|
|
return BuildPatchJson(url, secret, jsonBody);
|
|
}
|
|
|
|
static UnityWebRequest BuildPatchJson(string url, string secret, string jsonBody)
|
|
{
|
|
byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonBody);
|
|
var req = new UnityWebRequest(url, "PATCH")
|
|
{
|
|
uploadHandler = new UploadHandlerRaw(bodyRaw),
|
|
downloadHandler = new DownloadHandlerBuffer()
|
|
};
|
|
req.SetRequestHeader("Content-Type", "application/json");
|
|
req.SetRequestHeader("X-Dedicated-Server-Secret", secret);
|
|
return req;
|
|
}
|
|
|
|
public static void LogIfFailed(UnityWebRequest req)
|
|
{
|
|
if (req.result == UnityWebRequest.Result.Success){
|
|
Logger.Log("Dedicated match PATCH success: " + req.responseCode + " " + (req.downloadHandler != null ? req.downloadHandler.text : ""));
|
|
}else{
|
|
Logger.Log("Dedicated match PATCH failed: " + req.responseCode + " " + req.error + " " + (req.downloadHandler != null ? req.downloadHandler.text : ""));
|
|
Logger.Log("More details : " + req.url + " " + req.method + " " + req.uploadHandler.data + " " + req.downloadHandler.text);
|
|
}
|
|
}
|
|
}
|