You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

370 lines
12 KiB

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>();
//Start Game
StartCoroutine(GameRoutine());
}
private void Update()
{
//This is required so that the server can continue to recieve client messages
//(it is a unity thing)
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();
//gets list of items to check spawn locations
spawnBlock.wakeup();
//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))
{
//wait until we have recieved all player input
yield return StartCoroutine(WaitForPlayerInput());
//I hate having to do this
EnvironmentBlocks = FindObjectsOfType<ActiveBlock>();
////Debug.Log("Active blocks found: " + EnvironmentBlocks.Length);
//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);
//playerDataAsArray.OrderByDescending(unit => unit.character.transform.position.x);
//playerDataAsArray = playerDataAsArray.OrderBy(unit => unit.character.CurrentBlock.transform.position.x).ToArray();
playerData = playerData.OrderBy(unit => unit.Value.character.CurrentBlock.transform.position.x).ToDictionary(unit => unit.Key, unit => unit.Value);
//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);
//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);
}
playerDataAsArray.ForEach(p => p.client.SendLives()); //Update the players score
//Let GameMode know that Round is Over
yield return StartCoroutine(EnvironmentEnd());
gameMode.RoundEnd(playerDataAsArray.ToArray());
//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)
{
//Debug.Log("Remove: " + player.client.characterAnimal);
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();
}
}
}
private IEnumerator WaitForPlayerInput()
{
//send round length to players
//#TODO make this only happen after first input
LogicProtocols.FloatMsg RoundTime = new LogicProtocols.FloatMsg(gameMode.GetRoundTime());
//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));
//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;
}
//Process the move
//blockFinished = data.blockReader.Read(data.character, AnimationTime, out waitTime);
//Wait for the animation to finish
//yield return new WaitForSeconds(waitTime);
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 blockIndex = 0;
foreach (Block block in SpawnBlocks)
{
////Debug.Log("Block #" + blockIndex++ + " (" + block.transform.position.x + ", " + block.transform.position.y + ", " + block.transform.position.z + ")");
}
//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)
{
////Debug.Log("spawnIndex = " + spawnIndex);
Character newChar = Instantiate(characterPrefab);
//Block startingBlock = SpawnBlocks[(spawnIndex++ % ClientList.ConnectedClients.Count)];
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.server.RegisterHandler(LogicProtocols.SendLogicList, RecieveLogicList);
}
/// <summary>
/// Clears any functions from server register which the manager would deal with
/// </summary>
private void UnRegisterHandlers()
{
server.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;
////Debug that we have recieved it
////Debug.Log("Recieved function from " + ClientList[msg.conn.connectionId].Name);
//Update player Data with recieved list
playerData[msg.conn.connectionId].blockReader.LogicChain = new List<LogicBlock>(logicMsg.elements);
playerData[msg.conn.connectionId].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();
}
}