using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
using Networking.Server;
|
|
using Networking;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
public class GameManager : MonoBehaviour
|
|
{
|
|
#region Inspector Field
|
|
[Header("Settings")]
|
|
[SerializeField]
|
|
private float AnimationTime;
|
|
[SerializeField]
|
|
private GameModeReference CurrentGameMode;
|
|
|
|
[Header("References")]
|
|
[SerializeField]
|
|
[Tooltip("Prefab of character for players to play")]
|
|
private Character characterPrefab;
|
|
|
|
[SerializeField]
|
|
private ServerObject server;
|
|
|
|
[SerializeField]
|
|
private ClientList ClientList;
|
|
|
|
public blockSpawn spawnBlock;
|
|
#endregion Inspector Field
|
|
|
|
#region Private Variables
|
|
public Dictionary<int, PlayerData> playerData;
|
|
private ActiveBlock[] EnvironmentBlocks;
|
|
#endregion Private Variables
|
|
|
|
#region Read Only
|
|
/// <summary>
|
|
/// Easy access to IEnumerable in playerData so we can Enumerate through it
|
|
/// </summary>
|
|
private PlayerData[] playerDataAsArray { get { return playerData.Values.ToArray(); } }
|
|
|
|
/// <summary>
|
|
/// Easy access to GameMode value in CurrentGameMode reference
|
|
/// </summary>
|
|
private GameMode gameMode { get { return CurrentGameMode.Value; } }
|
|
#endregion Read Only
|
|
|
|
#region Unity Functions
|
|
private void Start()
|
|
{
|
|
spawnBlock = gameObject.GetComponent<blockSpawn>();
|
|
StartCoroutine(GameRoutine());
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
server.ServerUpdate();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
//Let Server know we want to recieve some messages
|
|
RegisterHandlers();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
//Let server know to not send messages this way
|
|
UnRegisterHandlers();
|
|
}
|
|
#endregion Unity Functions
|
|
|
|
#region Class Functions
|
|
private IEnumerator GameRoutine()
|
|
{
|
|
//Allows game mode to instantiate anything it might need;
|
|
gameMode.PreGameStart();
|
|
|
|
//Spawn Characters and tell let the GameMode do anything with the characters it might want
|
|
SpawnCharacters();
|
|
playerDataAsArray.ForEach(p => p.client.SendLives());
|
|
gameMode.GameStart(playerDataAsArray);
|
|
|
|
//Loop until the GameMode lets us know the game is over
|
|
while (!gameMode.isGameOver(playerDataAsArray))
|
|
{
|
|
int playersalive_left = 0;
|
|
int spotinarray = 0;
|
|
int players = 0;
|
|
|
|
foreach (PlayerData data in playerDataAsArray)
|
|
{
|
|
players++;
|
|
if (data.client.Lives > 0)
|
|
{
|
|
playersalive_left++;
|
|
spotinarray = players;
|
|
}
|
|
}
|
|
if(playersalive_left == 1)
|
|
{
|
|
GlobalVariables.winneranimal = playerDataAsArray[spotinarray-1].client.characterAnimal;
|
|
GlobalVariables.winnername = playerDataAsArray[spotinarray-1].client.Name;
|
|
SceneManager.LoadScene("Winner");
|
|
}
|
|
|
|
//wait until we have recieved all player input
|
|
yield return StartCoroutine(WaitForPlayerInput());
|
|
|
|
//I hate having to do this
|
|
EnvironmentBlocks = FindObjectsOfType<ActiveBlock>();
|
|
|
|
//Routine for players movement
|
|
yield return StartCoroutine(RoundRoutine()); //it's pretty long so it gets it's own coroutine;
|
|
}
|
|
//Let the gamemode know that the game is over
|
|
gameMode.GameEnd(playerDataAsArray);
|
|
}
|
|
|
|
private void removePlayer(PlayerData player)
|
|
{
|
|
player.client.ChangeScene("WaitScene");
|
|
player.isDead = true;
|
|
}
|
|
|
|
private IEnumerator RoundRoutine()
|
|
{
|
|
//Tell the gamemode that we are starting a round
|
|
gameMode.RoundStart(playerDataAsArray);
|
|
|
|
//Loop until all players have finished moving
|
|
while (playerDataAsArray.Any(p => !p.blockReader.Finished))
|
|
{
|
|
//Loop through all players
|
|
foreach (PlayerData player in playerDataAsArray)
|
|
{
|
|
yield return StartCoroutine(MoveRoutine(player));//Move Player
|
|
gameMode.PlayerMoved(player);//LetGameModeKnow
|
|
}
|
|
|
|
//Let Gamemode know all players have moved
|
|
gameMode.AllPlayersMoved(playerDataAsArray);
|
|
|
|
//I hate having to do this
|
|
EnvironmentBlocks = FindObjectsOfType<ActiveBlock>();
|
|
|
|
//Let the environment take a turn
|
|
yield return StartCoroutine(EnvironmentTurn());
|
|
gameMode.EnvironmentTurn(playerDataAsArray);
|
|
|
|
//if Game is over break out of loop
|
|
if (gameMode.isGameOver(playerDataAsArray))
|
|
break;
|
|
}
|
|
//decrease lives here!!
|
|
foreach (PlayerData player in playerDataAsArray)
|
|
{
|
|
gameMode.PlayerKilled(player);
|
|
}
|
|
|
|
//Let GameMode know that Round is Over
|
|
yield return StartCoroutine(EnvironmentEnd());
|
|
gameMode.RoundEnd(playerDataAsArray.ToArray());
|
|
|
|
playerDataAsArray.ForEach(p => p.client.SendLives()); //Update the players score
|
|
|
|
//check is anyone has 0 lives remaining
|
|
//remove them from the array
|
|
//force them back to the waiting for players screen
|
|
foreach (PlayerData player in playerDataAsArray)
|
|
{
|
|
if (player.client.Lives == 0)
|
|
{
|
|
removePlayer(player);
|
|
}
|
|
}
|
|
//spawn collectible logic blocks
|
|
spawnBlock.Spawn();
|
|
|
|
//Reset some player Data
|
|
foreach (PlayerData player in playerDataAsArray)
|
|
{
|
|
if (player.client.Lives > 0)
|
|
{
|
|
player.blockReader.Reset();
|
|
player.client.SendInventory();
|
|
}
|
|
}
|
|
|
|
playerData = playerData.OrderBy(unit => unit.Value.character.CurrentBlock.transform.position.x).ToDictionary(unit => unit.Key, unit => unit.Value);
|
|
//Dictionary<int,PlayerData> filteredPlayers = playerData.Where(p => !p.Value.isDead).ToDictionary(unit => unit.Key, unit => unit.Value);
|
|
int order = 1;
|
|
foreach (PlayerData data in playerDataAsArray)
|
|
{
|
|
data.character.runOrder = order;
|
|
order++;
|
|
}
|
|
}
|
|
|
|
private IEnumerator WaitForPlayerInput()
|
|
{
|
|
//send round length to players
|
|
//#TODO make this only happen after first input
|
|
LogicProtocols.FloatMsg RoundTime = new LogicProtocols.FloatMsg(gameMode.GetRoundTime());
|
|
|
|
foreach (PlayerData player in playerDataAsArray)
|
|
{
|
|
if (player.client.Lives == 0)
|
|
{
|
|
removePlayer(player);
|
|
}
|
|
}
|
|
|
|
//playerDataAsArray.ForEach(p => p.client.conn.Send(LogicProtocols.SendRoundTime, RoundTime));
|
|
foreach (PlayerData player in playerDataAsArray)
|
|
{
|
|
if (player.client.Lives > 0)
|
|
{
|
|
player.client.conn.Send(LogicProtocols.SendRoundTime, RoundTime);
|
|
}
|
|
}
|
|
|
|
//Send players to input Scene
|
|
//ClientList.ForEach(p => p.ChangeScene("ClientScene"));
|
|
foreach (ClientData client in ClientList)
|
|
{
|
|
if (client.Lives > 0) { client.ChangeScene("ClientScene"); }
|
|
}
|
|
|
|
//Let gamemode know clients are input-ing
|
|
gameMode.InputStart(playerDataAsArray);
|
|
|
|
//wait for all players to
|
|
yield return new WaitUntil(() => playerData.All(p => p.Value.recievedList || p.Value.isDead || p.Value.character.lives == 0));
|
|
|
|
//reset
|
|
//playerDataAsArray.ForEach(p => p.recievedList = false); //reset all players list
|
|
foreach (PlayerData player in playerDataAsArray)
|
|
{
|
|
if (player.client.Lives > 0) { player.recievedList = false; }
|
|
}
|
|
|
|
//Let gamemode know all inputs have been recieved
|
|
gameMode.InputEnd(playerDataAsArray);
|
|
}
|
|
|
|
private IEnumerator MoveRoutine(PlayerData data)
|
|
{
|
|
//If the current block hasn't already been removed, remove it from the player inventory
|
|
//We need to check if it has already been removed so loops don't eat an entire stack
|
|
if (data.blockReader.CurrentBlock != null && !data.blockReader.CurrentBlock.hasBeenRemoved)
|
|
{
|
|
data.client.Inventory.Remove(data.blockReader.CurrentBlock);
|
|
data.blockReader.CurrentBlock.hasBeenRemoved = true;
|
|
}
|
|
yield return StartCoroutine(data.blockReader.Read(data.character, AnimationTime));
|
|
}
|
|
|
|
private void SpawnCharacters()
|
|
{
|
|
playerData = new Dictionary<int, PlayerData>();
|
|
Block[] SpawnBlocks = FindObjectsOfType<Block>().Where(p => p.isSpawnable).ToArray();
|
|
|
|
//int spawnIndex = 0;
|
|
//If we have an odd number of players, then we start at spawn point 0 (in the middle)
|
|
//If we have an even number, then we skip it
|
|
//int spawnIndex = ((ClientList.Count() + 1) % 2);
|
|
int spawnIndex = 0;
|
|
|
|
foreach (ClientData client in ClientList)
|
|
{
|
|
Character newChar = Instantiate(characterPrefab);
|
|
Block startingBlock = SpawnBlocks[spawnIndex++];
|
|
newChar.Initialise(startingBlock, client.Inventory, client.characterAnimal);
|
|
newChar.transform.forward = startingBlock.SpawnDirection.ToVector();
|
|
playerData.Add(client.ID, new PlayerData(newChar, client));
|
|
newChar.ClientLink = client;
|
|
client.playerCharacter = newChar;
|
|
}
|
|
}
|
|
|
|
private IEnumerator EnvironmentTurn()
|
|
{
|
|
foreach (var InitiativeGroup in EnvironmentBlocks.GroupBy(p => p.GetInitative())){
|
|
|
|
foreach (ActiveBlock block in InitiativeGroup)
|
|
StartCoroutine(block.OnEnvironmentTurn(playerDataAsArray));
|
|
|
|
yield return new WaitUntil(() => InitiativeGroup.All(p => p.isFinished));
|
|
InitiativeGroup.ForEach(p => p.Reset());
|
|
}
|
|
}
|
|
|
|
private IEnumerator EnvironmentEnd()
|
|
{
|
|
foreach (var InitiativeGroup in EnvironmentBlocks.GroupBy(p => p.GetInitative())){
|
|
|
|
foreach (ActiveBlock block in InitiativeGroup)
|
|
StartCoroutine(block.OnRoundEnd(playerDataAsArray));
|
|
|
|
yield return new WaitUntil(() => InitiativeGroup.All(p => p.isFinished));
|
|
InitiativeGroup.ForEach(p => p.Reset());
|
|
}
|
|
}
|
|
|
|
private ActiveBlock[] GetActiveBlocksOf(int intiative)
|
|
{
|
|
return EnvironmentBlocks.Where(p => p.GetInitative() == intiative).ToArray();
|
|
}
|
|
#endregion Class Functions
|
|
|
|
#region Networking Functions
|
|
/// <summary>
|
|
/// Registers functions which should deal with incoming messages from clients
|
|
/// </summary>
|
|
private void RegisterHandlers()
|
|
{
|
|
server.RegisterHandler(LogicProtocols.SendLogicList, RecieveLogicList);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears any functions from server register which the manager would deal with
|
|
/// </summary>
|
|
private void UnRegisterHandlers()
|
|
{
|
|
server.UnregisterHandler(LogicProtocols.SendLogicList);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when server recieves moves from client
|
|
/// </summary>
|
|
/// <param name="msg">messages passed by server</param>
|
|
private void RecieveLogicList(NetworkMessage msg)
|
|
{
|
|
//try to read base message as a logic message
|
|
LogicProtocols.LogicMsg logicMsg;
|
|
if (!msg.TryRead(out logicMsg))
|
|
return;
|
|
|
|
//Update player Data with recieved list
|
|
playerData[msg.conn.Hash()].blockReader.LogicChain = new List<LogicBlock>(logicMsg.elements);
|
|
playerData[msg.conn.Hash()].recievedList = true;
|
|
}
|
|
#endregion Networking Functions
|
|
}
|
|
|
|
public class PlayerData
|
|
{
|
|
public Character character;
|
|
public BlockReader blockReader;
|
|
public ClientData client;
|
|
|
|
public bool recievedList;
|
|
public bool isDead = false;
|
|
|
|
public PlayerData(Character character, ClientData client)
|
|
{
|
|
this.character = character;
|
|
this.client = client;
|
|
blockReader = new BlockReader();
|
|
}
|
|
}
|