Newer
Older
CGTrack / Assets / Oculus / Platform / Samples / VrBoardGame / Scripts / GameController.cs
namespace Oculus.Platform.Samples.VrBoardGame
{
	using UnityEngine;
	using UnityEngine.UI;

	// This is the primary class that implements the game logic.
	public class GameController : MonoBehaviour
	{
		// instance of the object interfacing with the matchmaking service
		[SerializeField] private MatchmakingManager m_matchmaking = null;

		[SerializeField] private GameBoard m_board = null;
		[SerializeField] private GamePiece m_pieceA = null;
		[SerializeField] private GamePiece m_pieceB = null;
		[SerializeField] private GamePiece m_powerPiece = null;

		// colors for the various states of the selectable games pieces
		[SerializeField] private Color m_unusableColor = Color.white;
		[SerializeField] private Color m_unselectedColor = Color.white;
		[SerializeField] private Color m_selectedColor = Color.white;
		[SerializeField] private Color m_highlightedColor = Color.white;

		[SerializeField] private Text m_ballCountText = null;
		[SerializeField] private Text m_player0Text = null;
		[SerializeField] private Text m_player1Text = null;

		private enum GameState {
			None,
			PracticingMyTurn, PracticingAiTurn,
			OnlineMatchMyTurn, OnlineMatchRemoteTurn
		}

		private GameState m_state;

		// the game piece the player is currently looking at
		private GamePiece m_interestedPiece;

		// the piece the player selected with the Fire button
		private GamePiece m_selectedPiece;

		// the piece that would be placed if the player pressed the Fire button
		private GamePiece m_proposedPiece;

		// how many IAP power-balls the user has
		private uint m_powerBallcount;

		// the name of the current opponent
		private string m_opponentName;

		void Start()
		{
			TransitionToState(GameState.None);
			UpdateScores();
		}

		void Update()
		{
			PerFrameStateUpdate();
		}

		#region Game State

		private void TransitionToState(GameState state)
		{
			m_state = state;

			UpdateGamePieceColors();
		}

		private void TransitionToNextState()
		{
			if (!m_board.IsFull())
			{
				switch (m_state)
				{
					case GameState.PracticingAiTurn:
						TransitionToState(GameState.PracticingMyTurn);
						break;
					case GameState.PracticingMyTurn:
						TransitionToState(GameState.PracticingAiTurn);
						break;
					case GameState.OnlineMatchRemoteTurn:
						TransitionToState(GameState.OnlineMatchMyTurn);
						break;
					case GameState.OnlineMatchMyTurn:
						TransitionToState(GameState.OnlineMatchRemoteTurn);
						break;
				}
			}
			else
			{
				switch (m_state)
				{
					case GameState.OnlineMatchRemoteTurn:
					case GameState.OnlineMatchMyTurn:
						m_matchmaking.EndMatch(m_board.GetPlayerScore(0), m_board.GetPlayerScore(1));
						break;
				}
				TransitionToState(GameState.None);
			}
		}

		private void PerFrameStateUpdate()
		{
			switch (m_state)
			{
				case GameState.PracticingAiTurn:
					// don't move immediately to give the AI time to 'think'
					if (Random.Range(1, 100) < 3)
					{
						MakeAIMove(1);
					}
					break;

				case GameState.PracticingMyTurn:
				case GameState.OnlineMatchMyTurn:
					if (Input.GetButton("Fire1"))
					{
						TrySelectPiece();
						TryPlacePiece();
					}
					break;
			}
		}

		#endregion

		#region Practicing with an AI Player

		public void PracticeButtonPressed()
		{
			m_opponentName = "* AI *";

			switch (m_state)
			{
				case GameState.OnlineMatchMyTurn:
				case GameState.OnlineMatchRemoteTurn:
					m_matchmaking.EndMatch(m_board.GetPlayerScore(0), m_board.GetPlayerScore(1));
					break;
			}
			m_board.Reset();

			// randomly decised whether the player or AI goes first
			if (Random.Range(0, 2) == 1)
			{
				TransitionToState(GameState.PracticingMyTurn);
			}
			else
			{
				TransitionToState(GameState.PracticingAiTurn);
			}

			UpdateScores();
		}

		private void MakeAIMove(int player)
		{
			bool moved = false;

			// pick a random search start position
			int rx = Random.Range(0, GameBoard.LENGTH_X - 1);
			int ry = Random.Range(0, GameBoard.LENGTH_Y - 1);

			// from (rx,ry) search of an available move
			for (int i = 0; i < GameBoard.LENGTH_X && !moved; i++)
			{
				for (int j = 0; j < GameBoard.LENGTH_Y && !moved; j++)
				{
					int x = (rx + i) % GameBoard.LENGTH_X;
					int y = (ry + j) % GameBoard.LENGTH_Y;

					// first try to place a piece on the current position
					if (m_board.CanPlayerMoveToPostion(x, y))
					{
						GamePiece p = Random.Range(0, 2) == 0 ? m_pieceA : m_pieceB;
						m_board.AddPiece(player, p.Prefab, x, y);
						moved = true;
					}
					// a random percentage of the time, try to powerup this position
					else if (m_board.CanPlayerPowerUpPosition(x, y) && Random.Range(0, 8) < 2)
					{
						m_board.AddPowerPiece(player, m_powerPiece.Prefab, x, y);
						moved = true;
					}
				}
			}

			if (moved)
			{
				UpdateScores();
				TransitionToNextState();
			}
		}

		#endregion

		#region Playing Online Match

		// called from the MatchmakingManager was a successly online match is made
		public void StartOnlineMatch (string opponentName, bool localUserGoesFirst)
		{
			m_board.Reset();
			m_opponentName = opponentName;

			if (localUserGoesFirst)
			{
				TransitionToState(GameState.OnlineMatchMyTurn);
			}
			else
			{
				TransitionToState(GameState.OnlineMatchRemoteTurn);
			}

			UpdateScores();
		}

		// called from the Matchmaking Manager when the remote users their next move
		public void MakeRemoteMove(GamePiece.Piece piece, int x, int y)
		{
			GameObject prefab = m_pieceA.PrefabFor(piece);

			if (piece == GamePiece.Piece.PowerBall)
			{
				m_board.AddPowerPiece(1, prefab, x, y);
			}
			else
			{
				m_board.AddPiece(1, prefab, x, y);
			}

			UpdateScores();
		}

		// called from the MatchmakingManager when the local user becomes the room
		// owner and thus it's safe for the local user to make their move
		public void MarkRemoteTurnComplete()
		{
			if (m_state == GameState.OnlineMatchRemoteTurn)
			{
				TransitionToNextState();
			}
		}

		// the match ended from a player leaving before the board was complete
		public void RemoteMatchEnded()
		{
			m_matchmaking.EndMatch(m_board.GetPlayerScore(0), m_board.GetPlayerScore(1));
		}

		#endregion

		#region Selecting and Placing a Game Place

		public void StartedLookingAtPiece(GamePiece piece)
		{
			m_interestedPiece = piece;
			UpdateGamePieceColors();
		}

		public void StoppedLookingAtPiece()
		{
			m_interestedPiece = null;
			UpdateGamePieceColors();
		}

		// This method is used to display an example piece where the player is looking
		// so they know what to expect when they press the Fire button.
		public void StartedLookingAtPosition(BoardPosition position)
		{
			if (m_state != GameState.OnlineMatchMyTurn && m_state != GameState.PracticingMyTurn)
				return;

			GamePiece newPiece = null;

			if ((m_selectedPiece == m_pieceA || m_selectedPiece == m_pieceB) &&
				m_board.CanPlayerMoveToPostion(position.x, position.y))
			{
				newPiece = m_board.AddProposedPiece(m_selectedPiece.Prefab, position);
			}
			else if (m_selectedPiece == m_powerPiece &&
				m_board.CanPlayerPowerUpPosition(position.x, position.y))
			{
				newPiece = m_board.AddProposedPowerPiece(m_selectedPiece.Prefab, position);
			}

			if (newPiece != null)
			{
				if (m_proposedPiece != null)
				{
					Destroy(m_proposedPiece.gameObject);
				}
				m_proposedPiece = newPiece;
			}
		}

		public void ClearProposedMove()
		{
			if (m_proposedPiece != null)
			{
				Destroy(m_proposedPiece.gameObject);
			}
		}

		public void TrySelectPiece()
		{
			if (m_interestedPiece == m_pieceA || m_interestedPiece == m_pieceB)
			{
				m_selectedPiece = m_interestedPiece;
			}
			else if (m_interestedPiece == m_powerPiece &&
					(m_powerBallcount > 0 || m_state == GameState.PracticingMyTurn))
			{
				m_selectedPiece = m_interestedPiece;
			}
			UpdateGamePieceColors();
		}

		public void TryPlacePiece()
		{
			if (m_proposedPiece == null)
				return;

			var position = m_proposedPiece.Position;
			switch(m_proposedPiece.Type)
			{
				case GamePiece.Piece.A:
				case GamePiece.Piece.B:
					m_board.AddPiece(0, m_proposedPiece.Prefab, position.x, position.y);
					break;
				case GamePiece.Piece.PowerBall:
					m_board.AddPowerPiece(0, m_proposedPiece.Prefab, position.x, position.y);
					break;
			}
			Destroy(m_proposedPiece.gameObject);

			if (m_state == GameState.OnlineMatchMyTurn)
			{
				m_matchmaking.SendLocalMove(m_proposedPiece.Type, position.x, position.y);
			}

			UpdateScores();
			TransitionToNextState();
		}

		#endregion

		#region UI

		public void QuitButtonPressed()
		{
			UnityEngine.Application.Quit();
		}

		public void AddPowerballs(uint count)
		{
			m_powerBallcount += count;
			m_ballCountText.text = "x" + m_powerBallcount.ToString();
		}

		private void UpdateScores()
		{
			m_player0Text.text = string.Format("{0}\n\n{1}",
				PlatformManager.MyOculusID, m_board.GetPlayerScore(0));

			m_player1Text.text = string.Format("{0}\n\n{1}",
				m_opponentName, m_board.GetPlayerScore(1));
		}

		private void UpdateGamePieceColors()
		{
			switch (m_state)
			{
				case GameState.None:
				case GameState.PracticingAiTurn:
				case GameState.OnlineMatchRemoteTurn:
					m_pieceA.GetComponent<Renderer>().material.color = m_unusableColor;
					m_pieceB.GetComponent<Renderer>().material.color = m_unusableColor;
					m_powerPiece.GetComponent<Renderer>().material.color = m_unusableColor;
					if (m_proposedPiece != null)
					{
						Destroy(m_proposedPiece.gameObject);
					}
					break;

				case GameState.PracticingMyTurn:
				case GameState.OnlineMatchMyTurn:
					m_pieceA.GetComponent<Renderer>().material.color = m_unselectedColor;
					m_pieceB.GetComponent<Renderer>().material.color = m_unselectedColor;
					m_powerPiece.GetComponent<Renderer>().material.color = m_unselectedColor;
					if (m_interestedPiece == m_pieceA || m_interestedPiece == m_pieceB ||
						m_interestedPiece == m_powerPiece)
					{
						m_interestedPiece.GetComponent<Renderer>().material.color = m_highlightedColor;
					}
					if (m_selectedPiece != null)
					{
						m_selectedPiece.GetComponent<Renderer>().material.color = m_selectedColor;
					}
					break;
			}
		}

		#endregion
	}
}