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 playerPawnsDict; 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.Player2, PlayerTypes.Player3, PlayerTypes.Player4 }); } // 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].playerPawnsDict = new List(); PlayerPawn playerPawn = null; int indexer = 0; foreach (Transform playerPawnChild in playerGameData.playersParent) { if (!playerPawnChild.gameObject.activeInHierarchy) continue; playerPawn = playerPawnChild.GetComponent(); indexer++; playerPawn.InitId(indexer); playerGameDatasDict[playerGameData.playerType].playerPawnsDict.Add(playerPawn); } } } } public void EnablePlayerSelectionStates(bool state) { foreach (var playerPawn in playerGameDatasDict[currentPlayerTypeTurn].playerPawnsDict) { 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].playerPawnsDict) { 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].playerPawnsDict.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) { Tile targetTile = tilesManager.RetrieveTileBasedOnIndex(playerGameData.startIndex); playerPawn.MoveToTile( GetAndInitPositionInsideSafeZone(playerPawn, targetTile), 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) { SafeTile safeTile = (SafeTile)currentSittingTile; safeTile.UpdateSafeZonePlayerData(currentPlayerTypeTurn, playerPawn); if (safeTile.PlayerTypesCount == 1) { PlayerTypes playerType = safeTile.GetFirstPlayerType(); var playerPawns = safeTile.GetPlayerPawns(playerType); foreach (var pawn in playerPawns) pawn.MoveToCustomTilePosition(safeTile.CenterPlacementPosition); } } 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) { Tile nextTile = tilesManager.RetrieveTileBasedOnIndex(index); Vector3 targetPosition = nextTile.CenterPlacementPosition; Debug.Log($"Tile Index :: nextIndex: {index}, targetIndex: {targetIndex}, nextTileName: {nextTile.name}"); if (index == targetIndex) // if the target index is the safe zone only then apply the logic for rearranging pawns { Tile targetTile = tilesManager.RetrieveTileBasedOnIndex(targetIndex); if (targetTile.IsSafeZone) { targetPosition = GetAndInitPositionInsideSafeZone(playerPawn, targetTile); } } Debug.Log($"tile targetPosition: {targetPosition}"); playerPawn.MoveToTile( targetPosition, onComplete: () => { diceRolledValue--; Debug.Log($"DiceRolledValue: {diceRolledValue}"); if (diceRolledValue > 0) { int nextTileIndex = GetNextGeneralTileIndex(playerPawn); Debug.Log($"currentTileIndex: {playerPawn.CurrentTileIndex}, nextTileIndex: {nextTileIndex}, targetIndex: {targetIndex}"); 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) - 1; MoveThroughTiles(playerPawn, nextTileIndex, targetIndex); } } else { // TODO :: Improve this logic, use a collection if (!nextTile.IsSafeZone) { if (nextTile.PlayerPawn != null) { // TODO :: Send existing pawn back to base. // means there's already a pawn there, move him back to the base. } nextTile.InitPlayerPawn(playerPawn, currentPlayerTypeTurn); } SwitchPlayer(playerPawn); CanRollDice = true; } }, index); } private Vector3 GetAndInitPositionInsideSafeZone(PlayerPawn playerPawn, Tile targetTile) { Vector3 targetPosition; SafeTile targetSafeTile = (SafeTile)targetTile; Debug.Log($"targetSafeTile.PlayerTypesCount: {targetSafeTile.PlayerTypesCount}"); if (targetSafeTile.PlayerTypesCount == 1) { Debug.Log($"targetSafeTile.ContainsPlayerType(currentPlayerTypeTurn): {targetSafeTile.ContainsPlayerType(currentPlayerTypeTurn)}"); if (!targetSafeTile.ContainsPlayerType(currentPlayerTypeTurn)) // means it is a new player type, the second one { PlayerTypes initialPlayerType = targetSafeTile.GetFirstPlayerType(); // rearrange already existing player from center position to it's saved transform var playerPawns = targetSafeTile.GetPlayerPawns(initialPlayerType); foreach (var pawn in playerPawns) { var placementPoint = targetSafeTile.GetPlacementPoint(initialPlayerType); pawn.MoveToCustomTilePosition(placementPoint.position); } targetSafeTile.InitPlayerPawn(playerPawn, currentPlayerTypeTurn); targetPosition = targetSafeTile.GetPlacementPoint(currentPlayerTypeTurn).position; } else { targetSafeTile.InitPlayerPawn(playerPawn, currentPlayerTypeTurn); targetPosition = targetTile.CenterPlacementPosition; } } else { // TODO :: Check the data if it's consistent Debug.Log($"targetSafeTile.ContainsPlayerType(currentPlayerTypeTurn): {targetSafeTile.ContainsPlayerType(currentPlayerTypeTurn)}"); if (!targetSafeTile.ContainsPlayerType(currentPlayerTypeTurn)) { Debug.Log($"targetSafeTile.PlayerTypesCount: {targetSafeTile.PlayerTypesCount}"); if (targetSafeTile.PlayerTypesCount < 1) // he is the only player that is being added to the safe zone { targetSafeTile.InitPlayerPawn(playerPawn, currentPlayerTypeTurn); targetPosition = targetTile.CenterPlacementPosition; } else { targetSafeTile.InitPlayerPawn(playerPawn, currentPlayerTypeTurn); targetPosition = targetSafeTile.GetPlacementPoint(currentPlayerTypeTurn).position; } } else { targetSafeTile.InitPlayerPawn(playerPawn, currentPlayerTypeTurn); targetPosition = targetSafeTile.GetPlacementPoint(currentPlayerTypeTurn).position; } } return targetPosition; } 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].playerPawnsDict.Count) { CanRollDiceAgain = false; SwitchPlayer(); if (playerTypes.Contains(currentPlayerTypeTurn)) playerTypes.Remove(currentPlayerTypeTurn); Debug.Log($"PlayerTypes: {playerTypes.Count}"); } else { CanRollDiceAgain = true; } } else { SwitchPlayer(); } CanRollDice = true; } }, index); } }