590 lines
18 KiB
C#
590 lines
18 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Firebase.Database;
|
|
using Firebase.Extensions;
|
|
using Newtonsoft.Json;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
/// <summary>
|
|
/// This is the game state manager on the phone side
|
|
/// </summary>
|
|
public class GameManager : MonoBehaviour
|
|
{
|
|
public static GameManager Instance;
|
|
|
|
private List<Player> players = new();
|
|
public Player currentPlayer = null;
|
|
|
|
[Header("Other component")]
|
|
public float explanationTime = 4f;
|
|
|
|
[Header("Home Connection Component")]
|
|
public TMP_InputField roomCodeField;
|
|
public TextMeshProUGUI roomError;
|
|
public TMP_InputField playerNameField;
|
|
public TextMeshProUGUI nameError;
|
|
public Button submitNewPlayer;
|
|
|
|
[Header("WaitingRoom Component")]
|
|
public TextMeshProUGUI listPlayersUI;
|
|
public GameObject submitStartGame;
|
|
|
|
[Header("Explanation Component")]
|
|
public TextMeshProUGUI counterExplanation;
|
|
|
|
[Header("MakeProposition Component")]
|
|
public TextMeshProUGUI counterMakeProposition;
|
|
|
|
[Header("Vote Component")]
|
|
public TextMeshProUGUI counterVote;
|
|
|
|
[Header("EndGame Components")]
|
|
[SerializeField] private GameObject endGameFirstPlayer;
|
|
[SerializeField] private GameObject endGameOtherPlayers;
|
|
|
|
[Header("Pages")]
|
|
public GameObject HomeConnection;
|
|
public GameObject WaitingRoom;
|
|
public GameObject BeforeStart;
|
|
public GameObject TakePicture;
|
|
public GameObject VotePicture;
|
|
public GameObject WaitingOtherPlayers;
|
|
public GameObject EndGame;
|
|
|
|
internal Room myRoom;
|
|
|
|
private DatabaseReference realtimeDB;
|
|
private DatabaseReference myOnlineRoom;
|
|
private DateTime endOfViewDate = DateTime.MinValue;
|
|
|
|
private void Awake()
|
|
{
|
|
submitNewPlayer.interactable = false;
|
|
Instance = this;
|
|
FirebaseInitializer.Instance.onFirebaseReady += Initialize;
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
InitializeHomePage();
|
|
}
|
|
|
|
private void InitializeHomePage()
|
|
{
|
|
HomeConnection.SetActive(true);
|
|
WaitingRoom.SetActive(false);
|
|
BeforeStart.SetActive(false);
|
|
TakePicture.SetActive(false);
|
|
VotePicture.SetActive(false);
|
|
WaitingOtherPlayers.SetActive(false);
|
|
EndGame.SetActive(false);
|
|
submitStartGame.SetActive(false);
|
|
submitNewPlayer.interactable = true;
|
|
|
|
if (PlayerPrefs.HasKey("lastplayername"))
|
|
playerNameField.text = PlayerPrefs.GetString("lastplayername");
|
|
}
|
|
|
|
private void OnApplicationQuit()
|
|
{
|
|
if (myOnlineRoom == null)
|
|
return;
|
|
|
|
myOnlineRoom.Child("players").Child(currentPlayer.id).RemoveValueAsync().ContinueWithOnMainThread(task =>
|
|
{
|
|
Debug.Log($"delete player {currentPlayer.name}");
|
|
if (myOnlineRoom != null)
|
|
{
|
|
myOnlineRoom.ValueChanged -= OnRoomUpdate;
|
|
}
|
|
});
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (myRoom == null)
|
|
return;
|
|
|
|
if (myRoom.currentState == (int)GameState.Explanation && endOfViewDate != DateTime.MinValue)
|
|
{
|
|
TimeSpan duration = endOfViewDate - DateTime.Now;
|
|
counterExplanation.text = ((int)duration.TotalSeconds).ToString("D1");
|
|
|
|
if (duration.TotalMilliseconds <= 0)
|
|
{
|
|
Debug.Log("It's time to make proposition !");
|
|
endOfViewDate = DateTime.MinValue;
|
|
}
|
|
}
|
|
|
|
if (myRoom.currentState == (int)GameState.MakeProposition && endOfViewDate != DateTime.MinValue)
|
|
{
|
|
TimeSpan duration = endOfViewDate - DateTime.Now;
|
|
counterMakeProposition.text = ((int)duration.TotalSeconds).ToString("D1");
|
|
|
|
if (duration.TotalMilliseconds <= 0)
|
|
{
|
|
Debug.Log("It's time to finish proposition !");
|
|
endOfViewDate = DateTime.MinValue;
|
|
}
|
|
}
|
|
|
|
|
|
if (myRoom.currentState == (int)GameState.MakeVote && endOfViewDate != DateTime.MinValue)
|
|
{
|
|
TimeSpan duration = endOfViewDate - DateTime.Now;
|
|
counterVote.text = ((int)duration.TotalSeconds).ToString("D1");
|
|
|
|
if (duration.TotalMilliseconds <= 0)
|
|
{
|
|
Debug.Log("It's time to finish proposition !");
|
|
endOfViewDate = DateTime.MinValue;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private void Initialize()
|
|
{
|
|
FirebaseInitializer.Instance.onFirebaseReady -= Initialize;
|
|
realtimeDB = FirebaseDatabase.DefaultInstance.RootReference;
|
|
submitNewPlayer.interactable = true;
|
|
Debug.Log("Realtime DB initialized");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send your name and game room to the server
|
|
/// </summary>
|
|
public void PlayerValidateNameAndServerRoom(string _name, string _code)
|
|
{
|
|
nameError.gameObject.SetActive(false);
|
|
roomError.gameObject.SetActive(false);
|
|
|
|
if (string.IsNullOrEmpty(_name))
|
|
{
|
|
Debug.LogError("Player name is empty", this);
|
|
|
|
string errorText = LanguageManager.Instance.currentStringTable.GetTable().GetEntry("PhoneView/Canvas/Background/HomeState/NameField/ErrorNameLabel").LocalizedValue;
|
|
nameError.text = errorText;
|
|
nameError.gameObject.SetActive(true);
|
|
submitNewPlayer.interactable = true;
|
|
|
|
return;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(_code))
|
|
{
|
|
Debug.LogError("Room code is empty", this);
|
|
|
|
string errorText = LanguageManager.Instance.currentStringTable.GetTable().GetEntry("PhoneView/Canvas/Background/HomeState/NameField/EmptyRoom").LocalizedValue;
|
|
roomError.text = errorText;
|
|
roomError.gameObject.SetActive(true);
|
|
submitNewPlayer.interactable = true;
|
|
|
|
return;
|
|
}
|
|
|
|
currentPlayer = new Player(_name);
|
|
PlayerPrefs.SetString("lastplayername", _name);
|
|
PlayerPrefs.Save();
|
|
|
|
//check if the room exists, if not display an error message
|
|
CheckIfRoomExists(_code, room =>
|
|
{
|
|
if (room == null)
|
|
{
|
|
Debug.LogError("The room doesn't exists");
|
|
string errorText = LanguageManager.Instance.currentStringTable.GetTable().GetEntry("PhoneView/Canvas/Background/HomeState/NameField/ErrorRoom").LocalizedValue;
|
|
roomError.text = errorText;
|
|
roomError.gameObject.SetActive(true);
|
|
submitNewPlayer.interactable = true;
|
|
}
|
|
else
|
|
{
|
|
myOnlineRoom = realtimeDB.Child("rooms").Child(_code);
|
|
//subscribe to it
|
|
myOnlineRoom.ValueChanged += OnRoomUpdate;
|
|
//if room exists, join it
|
|
JoinRoom(() =>
|
|
{
|
|
|
|
myRoom.currentState = (int)GameState.WaitingForOtherPlayersToJoin;
|
|
players.Add(currentPlayer);
|
|
|
|
WaitingRoom.SetActive(true);
|
|
HomeConnection.SetActive(false);
|
|
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
private void CheckIfRoomExists(string _roomCode, Action<Room> callback_Room)
|
|
{
|
|
realtimeDB.Child("rooms").Child(_roomCode).GetValueAsync().ContinueWithOnMainThread(task =>
|
|
{
|
|
if (task.IsFaulted)
|
|
{
|
|
Debug.LogException(task.Exception);
|
|
}
|
|
else if (task.IsCompleted)
|
|
{
|
|
DataSnapshot snapshot = task.Result;
|
|
if (snapshot == null)
|
|
{
|
|
callback_Room?.Invoke(null);
|
|
}
|
|
else
|
|
{
|
|
callback_Room?.Invoke(JsonUtility.FromJson<Room>(snapshot.GetRawJsonValue()));
|
|
}
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add this player to the room
|
|
/// </summary>
|
|
private void JoinRoom(Action callback_OnRoomJoined)
|
|
{
|
|
string JSON = JsonUtility.ToJson(currentPlayer);
|
|
Debug.Log(JSON);
|
|
try
|
|
{
|
|
myOnlineRoom.Child("players").Child(currentPlayer.id).SetRawJsonValueAsync(JSON).ContinueWithOnMainThread(task =>
|
|
{
|
|
if (task.IsFaulted)
|
|
{
|
|
Debug.LogException(task.Exception);
|
|
}
|
|
else
|
|
{
|
|
Debug.Log($"{currentPlayer.name} has been added to the room", this);
|
|
callback_OnRoomJoined?.Invoke();
|
|
}
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogException(ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Call this only by the first player
|
|
/// </summary>
|
|
public void StartGame()
|
|
{
|
|
// send Start Game
|
|
try
|
|
{
|
|
SendCurrentState(GameState.Explanation);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogException(ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Automatically called when something change in your room
|
|
/// </summary>
|
|
private void OnRoomUpdate(object sender, ValueChangedEventArgs e)
|
|
{
|
|
GameState lastState = myRoom != null ? (GameState)myRoom.currentState : GameState.EnteringName;
|
|
|
|
try
|
|
{
|
|
if (e?.Snapshot?.GetRawJsonValue() != null)
|
|
{
|
|
string JSON = e.Snapshot.GetRawJsonValue();
|
|
Debug.Log($"Room has been updated : {JSON}", this);
|
|
myRoom = JsonConvert.DeserializeObject<Room>(e.Snapshot.GetRawJsonValue());
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogException(ex);
|
|
}
|
|
if (myRoom == null)
|
|
{
|
|
Debug.LogError("Got an update for an empty room.", this);
|
|
return;
|
|
}
|
|
|
|
|
|
Debug.Log($"lasState = {lastState}. Currentstep = {(GameState)myRoom.currentState}", this);
|
|
if (myRoom.currentState != (int)lastState)
|
|
{
|
|
OnNewGameState();
|
|
}
|
|
|
|
//call this every time we are on this state
|
|
switch (myRoom.currentState)
|
|
{
|
|
case (int)GameState.WaitingForOtherPlayersToJoin:
|
|
{
|
|
List<Player> players = myRoom.GetOrderedPlayerList();
|
|
UpdateDisplayedListUser(players);
|
|
|
|
if (players.Count >= 3 && CheckIfIAmTheFirst(players))
|
|
{
|
|
submitStartGame.SetActive(true);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Call this when Game state change
|
|
/// </summary>
|
|
private void OnNewGameState()
|
|
{
|
|
switch (myRoom.currentState)
|
|
{
|
|
case (int)GameState.EnteringName:
|
|
{
|
|
InitializeHomePage();
|
|
break;
|
|
}
|
|
case (int)GameState.Explanation:
|
|
{
|
|
Debug.Log("Explanation time.", this);
|
|
|
|
WaitingOtherPlayers.SetActive(false);
|
|
TakePicture.SetActive(false);
|
|
WaitingRoom.SetActive(false);
|
|
VotePicture.SetActive(false);
|
|
EndGame.SetActive(false);
|
|
|
|
BeforeStart.SetActive(true);
|
|
endOfViewDate = DateTime.Now.AddSeconds(4);
|
|
break;
|
|
}
|
|
case (int)GameState.MakeProposition:
|
|
{
|
|
Debug.Log("It's photo time !", this);
|
|
WaitingOtherPlayers.SetActive(false);
|
|
BeforeStart.SetActive(false);
|
|
WaitingRoom.SetActive(false);
|
|
VotePicture.SetActive(false);
|
|
EndGame.SetActive(false);
|
|
|
|
TakePicture.SetActive(true);
|
|
endOfViewDate = DateTime.Now.AddSeconds(60);
|
|
break;
|
|
}
|
|
case (int)GameState.MakeVote:
|
|
{
|
|
Debug.Log("It's voting time !", this);
|
|
WaitingOtherPlayers.SetActive(false);
|
|
BeforeStart.SetActive(false);
|
|
WaitingRoom.SetActive(false);
|
|
TakePicture.SetActive(false);
|
|
EndGame.SetActive(false);
|
|
|
|
VotePicture.SetActive(true);
|
|
endOfViewDate = DateTime.Now.AddSeconds(20);
|
|
Debug.Log("subscribe to question ID");
|
|
myOnlineRoom.Child("currentQuestionId").ValueChanged += OnCurrentQuestionChanged;
|
|
break;
|
|
}
|
|
case (int)GameState.Score:
|
|
{
|
|
Debug.Log("it's scoring time !", this);
|
|
Debug.Log("unsubscribe to question ID");
|
|
myOnlineRoom.Child("currentQuestionId").ValueChanged -= OnCurrentQuestionChanged;
|
|
|
|
WaitingOtherPlayers.SetActive(false);
|
|
BeforeStart.SetActive(false);
|
|
WaitingRoom.SetActive(false);
|
|
TakePicture.SetActive(false);
|
|
VotePicture.SetActive(false);
|
|
|
|
EndGame.SetActive(true);
|
|
|
|
endGameFirstPlayer.SetActive(CheckIfIAmTheFirst(myRoom.GetPlayerList()));
|
|
endGameOtherPlayers.SetActive(!CheckIfIAmTheFirst(myRoom.GetPlayerList()));
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Call this to wait for other players
|
|
/// </summary>
|
|
public void WaitForPlayers()
|
|
{
|
|
if (TakePicture.activeInHierarchy)
|
|
{
|
|
TakePicture.SetActive(false);
|
|
}
|
|
if (VotePicture.activeInHierarchy)
|
|
{
|
|
VotePicture.SetActive(false);
|
|
}
|
|
WaitingOtherPlayers.SetActive(true);
|
|
}
|
|
|
|
|
|
private void UpdateDisplayedListUser(List<Player> players)
|
|
{
|
|
listPlayersUI.text = string.Empty;
|
|
for (int i = 0; i < players.Count; i++)
|
|
{
|
|
listPlayersUI.text += "\n" + players[i].name;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// return true if you are the fist player
|
|
/// </summary>
|
|
/// <param name="players"></param>
|
|
/// <returns></returns>
|
|
private bool CheckIfIAmTheFirst(List<Player> players)
|
|
{
|
|
bool isFirst = false;
|
|
if (players.Count > 1)
|
|
{
|
|
|
|
List<Player> sortedList = players.OrderBy(x => x.creationDate).ToList();
|
|
|
|
if (sortedList[0].id == currentPlayer.id)
|
|
{
|
|
isFirst = true;
|
|
}
|
|
}
|
|
return isFirst;
|
|
}
|
|
|
|
public void SendCurrentState(GameState state, Action callback_onCurrentStateSent = null)
|
|
{
|
|
myOnlineRoom.Child("currentState").SetValueAsync((int)state).ContinueWithOnMainThread(task =>
|
|
{
|
|
if (task.IsFaulted)
|
|
{
|
|
Debug.LogException(task.Exception);
|
|
}
|
|
else
|
|
{
|
|
callback_onCurrentStateSent?.Invoke();
|
|
}
|
|
});
|
|
}
|
|
|
|
public void OnClickSubmitSignIn()
|
|
{
|
|
string playerName = playerNameField.text;
|
|
string roomCode = roomCodeField.text;
|
|
submitNewPlayer.interactable = false;
|
|
PlayerValidateNameAndServerRoom(playerName, roomCode);
|
|
}
|
|
|
|
public void onClickSamePlayers()
|
|
{
|
|
Debug.Log("Play with same players !", this);
|
|
Room newRoom = myRoom.Copy();
|
|
newRoom.currentState = (int)GameState.Explanation;
|
|
newRoom.questions.Clear();
|
|
newRoom.currentQuestionId = 0;
|
|
string json = JsonConvert.SerializeObject(newRoom);
|
|
|
|
myOnlineRoom.SetRawJsonValueAsync(json).ContinueWithOnMainThread(task =>
|
|
{
|
|
if (task.IsFaulted)
|
|
{
|
|
Debug.LogException(task.Exception);
|
|
}
|
|
});
|
|
|
|
}
|
|
public void onClickNewPlayers()
|
|
{
|
|
Debug.Log("Play with new players !", this);
|
|
Room newRoom = myRoom.Copy();
|
|
newRoom.currentState = (int)GameState.EnteringName;
|
|
newRoom.currentQuestionId = 0;
|
|
newRoom.questions = null;
|
|
newRoom.players = null;
|
|
string json = JsonConvert.SerializeObject(newRoom);
|
|
|
|
myOnlineRoom.SetRawJsonValueAsync(json).ContinueWithOnMainThread(task =>
|
|
{
|
|
if (task.IsFaulted)
|
|
{
|
|
Debug.LogException(task.Exception);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
private void OnCurrentQuestionChanged(object sender, ValueChangedEventArgs onlineValue)
|
|
{
|
|
if (onlineValue == null || onlineValue.Snapshot == null || onlineValue.Snapshot.Value == null || myRoom.questions == null || myRoom.questions.Count == 0)
|
|
return;
|
|
|
|
int questionId = (int)onlineValue.Snapshot.Value;
|
|
Debug.Log($"new question ! Q{((int)onlineValue.Snapshot.Value)}");
|
|
|
|
Question q = myRoom.questions[questionId];
|
|
|
|
//do not vote for your question
|
|
if (myRoom.GetQuestionsByPlayer(currentPlayer).Contains(q))
|
|
{
|
|
WaitingOtherPlayers.SetActive(true);
|
|
VotePicture.SetActive(false);
|
|
}
|
|
else
|
|
{
|
|
VotePicture.SetActive(true);
|
|
WaitingOtherPlayers.SetActive(false);
|
|
endOfViewDate = DateTime.Now.AddSeconds(20);
|
|
VotePicture.GetComponent<PropositionHandler>().ShowPlayersProposition(q);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Call this function from the vote button in the scene
|
|
/// </summary>
|
|
/// <param name="propositionNumber"></param>
|
|
public void OnClickProposition(int propositionNumber)
|
|
{
|
|
/*
|
|
Debug.Log($"Room has {myRoom.questions.Count} questions. the current Question is Q({myRoom.currentQuestionId}).");
|
|
Debug.Log($"Q({myRoom.currentQuestionId}) has {myRoom.questions[myRoom.currentQuestionId].propositions.Count} propositions.");
|
|
Debug.Log($"Player click on proposition {propositionNumber}.");
|
|
Debug.Log($"it has {myRoom.questions[myRoom.currentQuestionId].propositions[propositionNumber].voters.Count} voters.");
|
|
*/
|
|
|
|
List<string> voters = myRoom.questions[myRoom.currentQuestionId].propositions[propositionNumber].voters;
|
|
if (voters == null)
|
|
voters = new List<string>();
|
|
|
|
voters.Add(currentPlayer.id);
|
|
myOnlineRoom.Child("questions").Child(myRoom.currentQuestionId.ToString()).Child("propositions").Child(propositionNumber.ToString()).Child("voters").SetValueAsync(voters);
|
|
|
|
WaitingOtherPlayers.SetActive(true);
|
|
VotePicture.SetActive(false);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public enum GameState
|
|
{
|
|
EnteringName = 0,
|
|
WaitingForOtherPlayersToJoin = 1,
|
|
Explanation = 2,
|
|
MakeProposition = 3,
|
|
MakeVote = 4,
|
|
Score = 5
|
|
}
|