using System; using System.Collections.Generic; using System.Linq; using UnityEngine; public enum PlayerTypes { Player1 = 0, Player2 = 1, Player3 = 2, Player4 = 3 } [System.Serializable] public class PlayerGameData { public PlayerTypes playerType; public GameObject playerCornerEntity; public int startIndex; public int endIndex; public Transform playersParent; public List playerPawns; public int totalPawnsFinished = 0; } public class GameplayManager : MonoBehaviour, IBase, IBootLoader, IDataLoader { [SerializeField] private Transform pointerDebug; [SerializeField] private MeshRenderer pointerMeshRend; [SerializeField] private Material turnMat; [SerializeField] private Material selectMat; [SerializeField] private PlayerGameData[] playerGameDatas; private PlayerTypes currentPlayerTypeTurn; private int currentPlayerTurnIndex = 0; private List playerTypes = new List(); public List PlayerTypesCollection => playerTypes; // private Dictionary playerPawnsDict = new Dictionary>(); private Dictionary playerGameDatasDict = new Dictionary(); private TilesManager tilesManager; private int diceRolledValue; public bool CanRollDice { get; private set; } private bool CanRollDiceAgain = false; // used for when you get a 6 or when you reach the finish point public void Initialize() { InterfaceManager.Instance?.RegisterInterface(this); } public void InitializeData() { tilesManager = InterfaceManager.Instance.GetInterfaceInstance(); CanRollDice = true; InitCurrentGamePlayerInfo( new List { PlayerTypes.Player1, PlayerTypes.Player3 }); } // TODO :: Call based on 2P/3P/4P public void InitCurrentGamePlayerInfo(List playerTypes) { this.playerTypes = playerTypes; currentPlayerTypeTurn = playerTypes[currentPlayerTurnIndex]; // initialize the board based on the player types foreach (PlayerGameData playerGameData in playerGameDatas) { if (!playerTypes.Contains(playerGameData.playerType)) { playerGameData.playerCornerEntity.SetActive(false); } else { playerGameDatasDict.Add(playerGameData.playerType, playerGameData); playerGameDatasDict[playerGameData.playerType].playerPawns = new List(); foreach (Transform playerPawnChild in playerGameData.playersParent) { if (!playerPawnChild.gameObject.activeInHierarchy) continue; playerGameDatasDict[playerGameData.playerType].playerPawns.Add(playerPawnChild.GetComponent()); } } } } public void EnablePlayerSelectionStates(bool state) { foreach (PlayerPawn playerPawn in playerGameDatasDict[currentPlayerTypeTurn].playerPawns) { if (playerPawn.GetPlayerState() == PlayerState.InFinishingPath) continue; playerPawn.SetPlayerSelectionState(state); } } public void OnDiceRolled(int rolledVal) { CanRollDice = false; // add core dice logic here Debug.Log($"Tile Index :: LUDO :: rolledVal: {rolledVal} :: {currentPlayerTypeTurn}"); diceRolledValue = rolledVal; if (rolledVal == Ludo_3D_Constants.Max_Dice_Rolls) { // provide option to select a pawn from the list // also play a simple animation before selecting EnablePlayerSelectionStates(true); CanRollDiceAgain = true; bool AreAllPawnsInFinishingPath = false; foreach (var pawn in playerGameDatasDict[currentPlayerTypeTurn].playerPawns) { if (pawn.GetPlayerState() == PlayerState.InFinishingPath && pawn.GetPlayerState() != PlayerState.HasFinished) { AreAllPawnsInFinishingPath = true; continue; } AreAllPawnsInFinishingPath = false; break; } if (AreAllPawnsInFinishingPath) CanRollDice = true; pointerMeshRend.material = selectMat; // pointerMeshRend.materials[0] = selectMat; } else // if there are any other pawns that are in safe or moving state { // for player's logic IEnumerable availPlayers = playerGameDatasDict[currentPlayerTypeTurn].playerPawns.Select(pawn => pawn) .Where(pawn => pawn.GetPlayerState() == PlayerState.InSafeZone || pawn.GetPlayerState() == PlayerState.Moving || pawn.GetPlayerState() == PlayerState.InFinishingPath); int customAvailPlayers = availPlayers.Count(); foreach (PlayerPawn playerPawn in availPlayers) { Debug.Log($"## playerPawn.GetPlayerState(): {playerPawn.GetPlayerState()}"); if (playerPawn.GetPlayerState() == PlayerState.InFinishingPath) { if (diceRolledValue <= tilesManager.GetFinishingTileDataLength(currentPlayerTypeTurn) - (playerPawn.CurrentTileIndex + 1)) { playerPawn.SetPlayerSelectionState(true); } else { customAvailPlayers--; } continue; } playerPawn.SetPlayerSelectionState(true); } // if (availPlayers.Count() < 1) Debug.Log($"CustomAvailablePlayers: {customAvailPlayers}"); if (customAvailPlayers < 1) { SwitchPlayer(); CanRollDice = true; } CanRollDiceAgain = false; } } public void OnPawnSelected(PlayerPawn playerPawn) { EnablePlayerSelectionStates(false); PlayerGameData playerGameData = playerGameDatasDict[currentPlayerTypeTurn]; if (playerPawn.GetPlayerState() == PlayerState.InHome) { playerPawn.MoveToTile( tilesManager.RetrieveTileBasedOnIndex(playerGameData.startIndex).transform.position, onComplete: () => { CanRollDice = true; playerPawn.SetPlayerState(PlayerState.InSafeZone); }, playerGameData.startIndex); return; } else if (playerPawn.GetPlayerState() == PlayerState.InFinishingPath || playerPawn.CurrentTileIndex == playerGameDatasDict[currentPlayerTypeTurn].endIndex) { int finishingPathIndex = GetNextFinishingTileIndex(playerPawn); int targetIdx = finishingPathIndex + diceRolledValue > tilesManager.GetFinishingTileDataLength(currentPlayerTypeTurn) - 1 ? tilesManager.GetFinishingTileDataLength(currentPlayerTypeTurn) - 1 : finishingPathIndex + diceRolledValue; Debug.Log($"TargetIdx: {targetIdx}"); MoveThroughFinishingPath(playerPawn, finishingPathIndex, targetIdx); } else if (playerPawn.GetPlayerState() == PlayerState.InSafeZone || playerPawn.GetPlayerState() == PlayerState.Moving) { // move based on the dice value Debug.Log($"Tile Index :: currentTileIndex: {playerPawn.CurrentTileIndex}"); int nextTileIdx = GetNextGeneralTileIndex(playerPawn); int targetIdx = playerPawn.CurrentTileIndex + diceRolledValue; if (nextTileIdx == 0) targetIdx = targetIdx - playerPawn.CurrentTileIndex; Tile currentSittingTile = tilesManager.RetrieveTileBasedOnIndex(playerPawn.CurrentTileIndex); if (currentSittingTile.IsSafeZone) { currentSittingTile.UpdateSafeZonePlayerData(currentPlayerTypeTurn); } MoveThroughTiles(playerPawn, nextTileIdx, targetIndex: targetIdx); } } private int GetNextGeneralTileIndex(PlayerPawn playerPawn) { return playerPawn.CurrentTileIndex == tilesManager.GetGeneralTilesLength() - 1 ? 0 : playerPawn.CurrentTileIndex + 1; } private int GetNextFinishingTileIndex(PlayerPawn playerPawn) { return playerPawn.CurrentTileIndex > tilesManager.GetFinishingTileDataLength(currentPlayerTypeTurn) - 1 ? 0 : playerPawn.CurrentTileIndex + 1; } private void SwitchPlayer(PlayerPawn playerPawn = null) { if (!CanRollDiceAgain) { Debug.Log($"currentPlayerTurn: {currentPlayerTypeTurn}"); Debug.Log($"currentPlayerTurnIndex: {currentPlayerTurnIndex}"); if (playerTypes.Count == 1) { Debug.LogError($"GAME IS OVER"); return; } if (currentPlayerTypeTurn == playerTypes[playerTypes.Count - 1]) { currentPlayerTurnIndex = 0; currentPlayerTypeTurn = playerTypes[currentPlayerTurnIndex]; } else { currentPlayerTurnIndex++; currentPlayerTypeTurn = playerTypes[currentPlayerTurnIndex]; } } var tempPos = playerGameDatasDict[currentPlayerTypeTurn].playerCornerEntity.transform.position; pointerDebug.position = new Vector3(tempPos.x, 3f, tempPos.z); pointerMeshRend.material = turnMat; // pointerMeshRend.materials[0] = turnMat; if (playerPawn) playerPawn.SetPlayerState(tilesManager.RetrieveTileBasedOnIndex(playerPawn.CurrentTileIndex).IsSafeZone ? PlayerState.InSafeZone : PlayerState.Moving); } private void MoveThroughTiles(PlayerPawn playerPawn, int index, int targetIndex) { Debug.Log($"Tile Index :: nextIndex: {index}, targetIndex: {targetIndex}"); Tile nextTile = tilesManager.RetrieveTileBasedOnIndex(index); Vector3 targetPosition = tilesManager.RetrieveTileBasedOnIndex(index).CenterPlacementPosition; if (index == targetIndex) // is next index the targetIndex { Tile targetTile = tilesManager.RetrieveTileBasedOnIndex(targetIndex); if (targetTile.IsSafeZone) { if (targetTile.PlayerTypesCount == 1) { if (!targetTile.ContainsPlayerType(currentPlayerTypeTurn)) // means it is a new player type, the second one { PlayerTypes initialPlayerType = targetTile.GetFirstPlayerType(); var playerPawnsCount = targetTile.GetPlayerPawnsCountInTile(initialPlayerType); // rearrange already existing player from center position to it's saved transform for (int idx = 0; idx < playerPawnsCount; idx++) { targetTile.IterateAndGetPlayerPawn(initialPlayerType, idx).MoveToTileSubPosition(targetTile.GetPlacementPoint(initialPlayerType).position); } targetTile.InitPlayerPawn(playerPawn, currentPlayerTypeTurn); targetPosition = targetTile.GetPlacementPoint(currentPlayerTypeTurn).position; } else { targetPosition = targetTile.CenterPlacementPosition; } } else { // TODO :: Check the data if it's consistent if (!targetTile.ContainsPlayerType(currentPlayerTypeTurn)) { targetTile.InitPlayerPawn(playerPawn, currentPlayerTypeTurn); targetPosition = targetTile.GetPlacementPoint(currentPlayerTypeTurn).position; } else { targetPosition = targetTile.GetPlacementPoint(currentPlayerTypeTurn).position; } } // TODO :: Introduce a swapping mechanism } else { // TODO :: Move code to onComplete callback if (targetTile.PlayerPawn != null) // TODO :: Improve this logic, use a collection { // TODO :: send this pawn back to base } } } playerPawn.MoveToTile( targetPosition, onComplete: () => { diceRolledValue--; if (diceRolledValue > 0) { int nextTileIndex = GetNextGeneralTileIndex(playerPawn); if (playerPawn.GetPlayerState() == PlayerState.InFinishingPath || index == playerGameDatasDict[currentPlayerTypeTurn].endIndex) { // MoveThroughTiles(playerPawn, index, targetIndex); Debug.Log($"TargetIdx: {targetIndex - index}"); MoveThroughFinishingPath(playerPawn, 0, targetIndex - index); } else if (nextTileIndex <= targetIndex) { if (nextTileIndex == 0) targetIndex = targetIndex - playerPawn.CurrentTileIndex; MoveThroughTiles(playerPawn, nextTileIndex, targetIndex); } } else { nextTile.InitPlayerPawn(playerPawn, currentPlayerTypeTurn); SwitchPlayer(playerPawn); CanRollDice = true; } }, index); } private void MoveThroughFinishingPath(PlayerPawn playerPawn, int index, int targetIndex) { playerPawn.SetPlayerState(PlayerState.InFinishingPath); playerPawn.MoveToTile( tilesManager.RetrievePositionForFinishingTile(currentPlayerTypeTurn, index).position, onComplete: () => { diceRolledValue--; Debug.Log($"DiceRolledValue: {diceRolledValue}"); if (diceRolledValue > 0) { int tileIndex = GetNextFinishingTileIndex(playerPawn); Debug.Log($"tileIndex: {tileIndex}, targetIndex: {targetIndex}"); if (tileIndex <= targetIndex) { // MoveThroughTiles(playerPawn, index, targetIndex); MoveThroughFinishingPath(playerPawn, tileIndex, targetIndex); } } else { if (playerPawn.CurrentTileIndex == tilesManager.GetFinishingTileDataLength(currentPlayerTypeTurn) - 1) { playerPawn.SetPlayerState(PlayerState.HasFinished); playerGameDatasDict[currentPlayerTypeTurn].totalPawnsFinished++; if (playerGameDatasDict[currentPlayerTypeTurn].totalPawnsFinished == playerGameDatasDict[currentPlayerTypeTurn].playerPawns.Count) { CanRollDiceAgain = false; SwitchPlayer(); if (playerTypes.Contains(currentPlayerTypeTurn)) playerTypes.Remove(currentPlayerTypeTurn); Debug.Log($"PlayerTypes: {playerTypes.Count}"); } else { CanRollDiceAgain = true; } } else { SwitchPlayer(); } CanRollDice = true; } }, index); } }