using System; using System.Collections.Generic; using System.Linq; using TMPro; using UnityEngine; public enum PlayerType { Player1 = 0, Player2 = 1, Player3 = 2, Player4 = 3, Bot } [System.Serializable] public class PlayerGameData { public PlayerType playerType; public int startIndex; public int endIndex; public Transform playersParent; public List playerPawnsDict; public int totalPawnsFinished = 0; } [System.Serializable] public enum MatchType { LAN, Bot } public class GameplayManager : MonoBehaviour, IBase, IBootLoader, IDataLoader { [SerializeField] private TextMeshProUGUI diceText; [SerializeField] private Transform pointerDebug; [SerializeField] private MeshRenderer pointerMeshRend; [SerializeField] private Material turnMat; [SerializeField] private Material selectMat; [SerializeField] private PlayerGameData[] playerGameDatas; [SerializeField] private PlayerBaseHandler playerBaseHandler; private PlayerType 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; // initialize the player list from UI. InitPlayerTypesForLAN(null); } // TODO :: Call when the UI selection is made and game starts public void InitPlayerTypesForLAN(List types) { // TODO :: 2P, 3P, 4P playerTypes = new List { PlayerType.Player1, PlayerType.Player2, PlayerType.Player3, PlayerType.Player4 }; playerBaseHandler.InitPlayerTypes(playerTypes); InitCurrentGamePlayerInfo(); } public void InitPlayerTypesForBotMatch(PlayerType selectedPlayerType, int botCount) { // test data if (botCount == 1) { var playerType = (PlayerType)((int)selectedPlayerType + 2 < Enum.GetValues(typeof(PlayerType)).Length ? (int)selectedPlayerType + 2 : (int)selectedPlayerType - 2); } else { } } // TODO :: Call based on 2P/3P/4P public void InitCurrentGamePlayerInfo() { currentPlayerTypeTurn = playerTypes[currentPlayerTurnIndex]; // initialize the board based on the player types foreach (PlayerGameData playerGameData in playerGameDatas) { if (!playerTypes.Contains(playerGameData.playerType)) continue; playerGameDatasDict.Add(playerGameData.playerType, playerGameData); playerGameDatasDict[playerGameData.playerType].playerPawnsDict = new List(); foreach (Transform playerPawnChild in playerGameData.playersParent) { if (!playerPawnChild.gameObject.activeInHierarchy) continue; playerGameDatasDict[playerGameData.playerType].playerPawnsDict.Add(playerPawnChild.GetComponent()); } } } 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; diceText.text = $"{diceRolledValue}"; 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]; Debug.Log($"playerPawn.GetPlayerState(): {playerPawn.GetPlayerState()}"); 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) { PlayerType playerType = safeTile.GetFirstPlayerType(); var playerPawns = safeTile.GetPlayerPawns(playerType); foreach (var pawn in playerPawns) pawn.MoveToCustomTilePosition(safeTile.CenterPlacementPosition); } } else { currentSittingTile.ResetPlayerPawn(); } 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]; } diceText.text = $"{0}"; } Debug.Log($"CurrentPlayerTurn: {currentPlayerTypeTurn}"); var tempPos = playerBaseHandler.GetPlayerBase(currentPlayerTypeTurn).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 Debug.Log($"nextTile.IsSafeZone: {nextTile.IsSafeZone}"); if (!nextTile.IsSafeZone) { Debug.Log($"nextTile.PlayerPawn: {nextTile.PlayerPawn}, {nextTile.transform.name}"); if (nextTile.PlayerPawn != null) { // play animation for moving back to base. // TODO :: Send existing pawn back to base. // means there's already a pawn there, move him back to the base. var playerBasePos = playerBaseHandler.GetPlayerBase(nextTile.PlayerPawn.PlayerType) .GetBasePlacementDataPosition(nextTile.PlayerPawn.PlayerId - 1).position; Debug.Log($"playerBasePos: {playerBasePos}"); nextTile.PlayerPawn.transform.position = playerBasePos; nextTile.PlayerPawn.SetPlayerState(PlayerState.InHome); } 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 { PlayerType 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); } }