using System.Linq.Expressions; using System.Collections; using System.Collections.Generic; using System.Runtime.InteropServices; using DG.Tweening; using UnityEngine; using UnityEngine.InputSystem.Processors; public class AIPathManager : MonoBehaviour, IBase, IBootLoader, IDataLoader { [SerializeField] private Transform[] lanes; [SerializeField] private int totalLanes = 3; [SerializeField] private float pathTimerMinLimit = 6f; [SerializeField] private float pathTimerMaxLimit = 10f; // private float safeDistance = 0f; private float timer = 0; private float pathTimerLimit = 5f; // private float extraDelayTime = 2f; private bool isInitialSpawn = true; private int currentTrackLaneIdx = -1; private int safeAreaIdx = 0; private float nonMovableDifficultyVal = 0; private float movableDifficultyVal = 0; private Vector3 globalEndPointPosMax = new Vector3(Mathf.Infinity, Mathf.Infinity, Mathf.Infinity); private List laneIndexes = new List(); private Vector3 laneSpawnStartPos; private TimerSystem timerSystem; private PlayerCarController playerCarController; private AIController aiController; private WorldSpawnManager worldSpawnManager; private ObstaclesManager obstaclesManager; private CollectiblesManager collectiblesManager; private DifficultyEvaluator difficultyEvaluator; private GameManager gameManager; private ObstacleBase lastEncounteredObstacle = null; private AnimationCurve nonMoveableObstaclesDiffCurve; private AnimationCurve moveableTrainDiffCurve; private Dictionary lastSpawnedObstaclesInLane = new Dictionary(); // convert to a list if required public void Initialize() { InterfaceManager.Instance?.RegisterInterface(this); } public void InitializeData() { playerCarController = InterfaceManager.Instance?.GetInterfaceInstance(); aiController = InterfaceManager.Instance?.GetInterfaceInstance(); obstaclesManager = InterfaceManager.Instance?.GetInterfaceInstance(); collectiblesManager = InterfaceManager.Instance?.GetInterfaceInstance(); worldSpawnManager = InterfaceManager.Instance?.GetInterfaceInstance(); difficultyEvaluator = InterfaceManager.Instance?.GetInterfaceInstance(); gameManager = InterfaceManager.Instance?.GetInterfaceInstance(); InitDifficultyCurves(); InitializeTimerSystem(); } private void InitDifficultyCurves() { Debug.Log($"### InitDifficultyCurves"); moveableTrainDiffCurve = difficultyEvaluator.DifficultyCurveSO.GetDifficultyCurve(DifficultyCurveType.MovableTrainSpeed); nonMoveableObstaclesDiffCurve = difficultyEvaluator.DifficultyCurveSO.GetDifficultyCurve(DifficultyCurveType.NonMovableObstacleSpeed); } public void SpawnObstacles() { int closerEndpointLaneIdx = 0; if (isInitialSpawn) { isInitialSpawn = false; float distZ = 0, extraOffsetDist = 0; safeAreaIdx = Random.Range(0, totalLanes); Debug.Log($"Updated safeAreaIdx on initial instance {safeAreaIdx}"); SetObstaclePositionData(); closerEndpointLaneIdx = FindCloserEndpointLaneIdx(safeAreaIdx); } else { laneIndexes.Clear(); laneIndexes.Add(safeAreaIdx); if (safeAreaIdx != 0 && safeAreaIdx != totalLanes - 1) { Debug.Log($"safeAreaIdx: {safeAreaIdx}"); if (lastSpawnedObstaclesInLane.ContainsKey(safeAreaIdx - 1) && lastSpawnedObstaclesInLane[safeAreaIdx - 1].HasAIPassed) laneIndexes.Add(safeAreaIdx - 1); if (lastSpawnedObstaclesInLane.ContainsKey(safeAreaIdx + 1) && lastSpawnedObstaclesInLane[safeAreaIdx + 1].HasAIPassed) laneIndexes.Add(safeAreaIdx + 1); } else if (safeAreaIdx == 0) { if (lastSpawnedObstaclesInLane.ContainsKey(safeAreaIdx + 1) && lastSpawnedObstaclesInLane[safeAreaIdx + 1].HasAIPassed) laneIndexes.Add(safeAreaIdx + 1); } else { if (lastSpawnedObstaclesInLane.ContainsKey(safeAreaIdx - 1) && lastSpawnedObstaclesInLane[safeAreaIdx - 1].HasAIPassed) laneIndexes.Add(safeAreaIdx - 1); } Debug.Log($":: safeAreaIdx: {safeAreaIdx}"); // Debug.Log($":: laneIndexes: {laneIndexes[0]}, {laneIndexes[1]}"); safeAreaIdx = laneIndexes[Random.Range(0, laneIndexes.Count)]; Debug.Log($"Updated safeAreaIdx on second instance {safeAreaIdx}"); SetObstaclePositionData(); Transform emptyLane = GetEmptyLane(safeAreaIdx); aiController.ChangeLane(emptyLane.position); closerEndpointLaneIdx = FindCloserEndpointLaneIdx(safeAreaIdx); } Debug.Log($"closerEndpointLaneIdx: {closerEndpointLaneIdx}"); lastEncounteredObstacle = lastSpawnedObstaclesInLane[closerEndpointLaneIdx]; currentTrackLaneIdx = closerEndpointLaneIdx; } private ObstaclesPathData obstaclesPathData = null; public void StartCreatingObstacleElements() { timer = 0; InitObstaclesPathData(); Debug.Log($"### obstaclesManager: {obstaclesManager.CurrentTrackObstacleType}"); Debug.Log($"### movableDifficultyCurve: {moveableTrainDiffCurve.Evaluate(playerCarController.CurrentCoveredDistance01)}"); Debug.Log($"### nonMovableObstaclesDifficultyCurve: {nonMoveableObstaclesDiffCurve.Evaluate(playerCarController.CurrentCoveredDistance01)}"); var difficultyVal = obstaclesManager.CurrentTrackObstacleType == TrackObstacleType.MovableTrain ? moveableTrainDiffCurve.Evaluate(playerCarController.CurrentCoveredDistance01) : nonMoveableObstaclesDiffCurve.Evaluate(playerCarController.CurrentCoveredDistance01); var startBoundPathTimerMax = obstaclesManager.ObstaclesPathSO.GetStartBoundObstaclesPathData(obstaclesManager.CurrentTrackObstacleType).pathTimerMaxLimit; var endBoundPathTimerMax = obstaclesManager.ObstaclesPathSO.GetEndBoundObstaclesPathData(obstaclesManager.CurrentTrackObstacleType).pathTimerMaxLimit; obstaclesPathData.pathTimerMaxLimit = worldSpawnManager.GetResultBasedOnDifficultyProgressiveFormula(startVal: startBoundPathTimerMax, endVal: endBoundPathTimerMax, fraction: difficultyVal); var startBoundSafeDistance = obstaclesManager.ObstaclesPathSO.GetStartBoundObstaclesPathData(obstaclesManager.CurrentTrackObstacleType).safeDistance; var endBoundSafeDistance = obstaclesManager.ObstaclesPathSO.GetEndBoundObstaclesPathData(obstaclesManager.CurrentTrackObstacleType).safeDistance; obstaclesPathData.safeDistance = worldSpawnManager.GetResultBasedOnDifficultyProgressiveFormula(startVal: startBoundSafeDistance, endVal: endBoundSafeDistance, fraction: difficultyVal); Debug.Log($"## obstaclesPathData.safeDistance: {obstaclesPathData.safeDistance}"); Debug.Log($"## obstaclesPathData.pathTimerMaxLimit: {obstaclesPathData.pathTimerMaxLimit}"); pathTimerLimit = Random.Range(obstaclesPathData.pathTimerMinLimit, obstaclesPathData.pathTimerMaxLimit); if (obstaclesPathData.extraDelay > 0) { InitializePathTimerWithExtraDelay(); } else { InitializePathTimer(); } Debug.Log($"StartCreatingPathElements"); } private void InitObstaclesPathData() { obstaclesManager.SetObstaclesType(isInitialSpawn); var newData = obstaclesManager.ObstaclesPathSO.GetStartBoundObstaclesPathData(obstaclesManager.CurrentTrackObstacleType); if (obstaclesPathData == null) { obstaclesPathData = new ObstaclesPathData(); } obstaclesPathData.trackObstacleType = newData.trackObstacleType; obstaclesPathData.extraDelay = newData.extraDelay; obstaclesPathData.pathTimerMinLimit = newData.pathTimerMinLimit; obstaclesPathData.pathTimerMaxLimit = newData.pathTimerMaxLimit; obstaclesPathData.safeDistance = newData.safeDistance; obstaclesPathData.extraOffsetMinDist = newData.extraOffsetMinDist; obstaclesPathData.extraOffsetMaxDist = newData.extraOffsetMaxDist; } public void CreateCollectibleElements() { // TODO :: create two different positions int index1 = Random.Range(0, lanes.Length); int index2 = Random.Range(0, lanes.Length); Vector3 lanePos1 = new Vector3(lanes[index1].position.x, 0.5f, aiController.transform.position.z); collectiblesManager.SpawnCollectible(lanePos1); if (index1 != index2) { Vector3 lanePos2 = new Vector3(lanes[index2].position.x, 0.5f, aiController.transform.position.z); collectiblesManager.SpawnCollectible(lanePos2); } } public void InitializeTimerSystem() { isInitialSpawn = true; timerSystem = new TimerSystem(); } private void InitializePathTimer() { timerSystem.Init(pathTimerLimit, onComplete: () => { StartCreatingObstacleElements(); }, inProgress: () => { CheckIfAICrossedLaneTrainEndPoint(); }); } private void InitializePathTimerWithExtraDelay() { timerSystem.Init(obstaclesPathData.extraDelay, onComplete: () => { timerSystem.Init(pathTimerLimit, onComplete: () => { StartCreatingObstacleElements(); }, inProgress: () => { CheckIfAICrossedLaneTrainEndPoint(); }); }); } private void Update() { if (!gameManager || !gameManager.IsGameInProgress) return; timerSystem?.UpdateTimer(Time.deltaTime); } private void CheckIfAICrossedLaneTrainEndPoint() { if (lastEncounteredObstacle) Debug.Log($":: CheckIfAICrossedLaneTrainEndPoint :: {aiController.transform.position.z} > {lastEncounteredObstacle.EndPoint.position.z}"); if (isInitialSpawn || (lastEncounteredObstacle != null && aiController.transform.position.z > lastEncounteredObstacle.EndPoint.position.z)) { lastEncounteredObstacle = null; if (lastSpawnedObstaclesInLane.ContainsKey(currentTrackLaneIdx)) lastSpawnedObstaclesInLane[currentTrackLaneIdx].SetAIPassedState(true); Debug.Log($"Spawn Obstacles"); SpawnObstacles(); } } private void SetObstaclePositionData() { float extraOffsetDist = 0, distZ = 0; for (int i = 0; i < totalLanes; i++) { if (i == safeAreaIdx) { continue; } extraOffsetDist = Random.Range(obstaclesManager.ObstaclesPathSO.GetStartBoundObstaclesPathData(obstaclesManager.CurrentTrackObstacleType).extraOffsetMinDist, obstaclesManager.ObstaclesPathSO.GetStartBoundObstaclesPathData(obstaclesManager.CurrentTrackObstacleType).extraOffsetMaxDist); // TODO :: tune values distZ = obstaclesPathData.safeDistance + extraOffsetDist; laneSpawnStartPos = new Vector3(lanes[i].position.x, lanes[i].position.y, aiController.transform.position.z + distZ); Debug.Log($"LaneSpawnStartPos: {laneSpawnStartPos}"); obstaclesManager.SpawnObstacle(laneSpawnStartPos, out ObstacleBase obstacleBase); if (lastSpawnedObstaclesInLane.ContainsKey(i)) lastSpawnedObstaclesInLane[i] = obstacleBase; else lastSpawnedObstaclesInLane.Add(i, obstacleBase); } } private Transform GetEmptyLane(int safeAreaIdx) { var emptyLane = lanes[safeAreaIdx]; emptyLane.position = new Vector3(emptyLane.position.x, emptyLane.position.y, aiController.transform.position.z); return emptyLane; } private int FindCloserEndpointLaneIdx(int safeAreaIdx) { int closerEndpointIdx; var n1 = -1; var n2 = -1; if (safeAreaIdx != lanes.Length && safeAreaIdx != 0) { n1 = safeAreaIdx - 1; n2 = safeAreaIdx + 1; } else if (safeAreaIdx == 0) { n1 = safeAreaIdx + 1; } else { n1 = safeAreaIdx - 1; } Vector3 pos1 = Vector3.zero, pos2 = Vector3.zero; if (n1 != -1 && lastSpawnedObstaclesInLane.ContainsKey(n1)) { pos1 = lastSpawnedObstaclesInLane[n1].EndPoint.position; } if (n2 != -1 && lastSpawnedObstaclesInLane.ContainsKey(n2)) { pos2 = lastSpawnedObstaclesInLane[n2].EndPoint.position; } if (pos1 != Vector3.zero && pos2 != Vector3.zero) { closerEndpointIdx = pos1.z < pos2.z ? n1 : n2; } else// if (pos1 != Vector3.zero) { closerEndpointIdx = pos1 != Vector3.zero ? n1 : n2; } return closerEndpointIdx; } }