using Gley.UrbanSystem.Internal; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Gley.TrafficSystem.Internal { /// /// Controls the traffic light intersections. /// internal class TrafficLightsIntersection : GenericIntersection { private readonly TrafficLightsIntersectionData _trafficLightsIntersectionData; private readonly TrafficLightsColor[] _intersectionState; private readonly float[] _roadGreenLightTime; private TrafficLightsBehaviour _trafficLightsBehaviour; private float _currentTime; private int _nrOfRoads; private int _currentRoad; private bool _yellowLight; private bool _stopUpdate; private bool _hasPedestrians; /// /// Constructor used for conversion from editor intersection type /// /// /// /// /// internal TrafficLightsIntersection(TrafficLightsIntersectionData trafficLightsIntersectionData, TrafficWaypointsDataHandler trafficWaypointsDataHandler, IPedestrianWaypointsDataHandler pedestrianWaypointsDataHandler, TrafficLightsBehaviour trafficLightsBehaviour, float greenLightTime, float yellowLightTime) { _trafficLightsIntersectionData = trafficLightsIntersectionData; SetTrafficLightsBehaviour(trafficLightsBehaviour); _nrOfRoads = _trafficLightsIntersectionData.StopWaypoints.Length; GetPedestrianRoads(pedestrianWaypointsDataHandler); _roadGreenLightTime = new float[_nrOfRoads]; for (int i = 0; i < _trafficLightsIntersectionData.StopWaypoints.Length; i++) { _roadGreenLightTime[i] = _trafficLightsIntersectionData.StopWaypoints[i].greenLightTime; } SetPedestrianGreenLightTime(); if (_nrOfRoads == 0) { Debug.LogWarning("Intersection " + _trafficLightsIntersectionData.Name + " has some unassigned references"); return; } _carsInIntersection = new List(); for (int i = 0; i < _trafficLightsIntersectionData.ExitWaypoints.Length; i++) { trafficWaypointsDataHandler.SetIntersection(_trafficLightsIntersectionData.ExitWaypoints[i], this, false, false, false, true); } for (int i = 0; i < _trafficLightsIntersectionData.StopWaypoints.Length; i++) { for (int j = 0; j < _trafficLightsIntersectionData.StopWaypoints[i].roadWaypoints.Length; j++) { trafficWaypointsDataHandler.SetIntersection(_trafficLightsIntersectionData.StopWaypoints[i].roadWaypoints[j], this, false, true, true, false); } } _intersectionState = new TrafficLightsColor[_nrOfRoads]; _currentRoad = Random.Range(0, _nrOfRoads); ChangeCurrentRoadColors(_currentRoad, TrafficLightsColor.Green); ChangeAllRoadsExceptSelectd(_currentRoad, TrafficLightsColor.Red); ApplyColorChanges(); _currentTime = 0; if (greenLightTime >= 0) { for (int i = 0; i < _roadGreenLightTime.Length; i++) { _roadGreenLightTime[i] = greenLightTime; } } if (yellowLightTime >= 0) { _trafficLightsIntersectionData.YellowLightTime = yellowLightTime; } for (int i = 0; i < _roadGreenLightTime.Length; i++) { if (_roadGreenLightTime[i] == 0) { _roadGreenLightTime[i] = _trafficLightsIntersectionData.GreenLightTime; } } } public override void PedestrianPassed(int pedestrianIndex) { } public override bool IsPathFree(int waypointIndex) { return false; } internal override string GetName() { return _trafficLightsIntersectionData.Name; } internal override int[] GetPedStopWaypoint() { return _trafficLightsIntersectionData.PedestrianWaypoints; } /// /// Change traffic lights color /// internal override void UpdateIntersection(float realtimeSinceStartup) { if (_stopUpdate) return; if (_yellowLight == false) { if (realtimeSinceStartup - _currentTime > _roadGreenLightTime[_currentRoad]) { ChangeCurrentRoadColors(_currentRoad, TrafficLightsColor.YellowGreen); ChangeCurrentRoadColors(GetValidValue(_currentRoad + 1), TrafficLightsColor.YellowRed); ApplyColorChanges(); _yellowLight = true; _currentTime = realtimeSinceStartup; } } else { if (realtimeSinceStartup - _currentTime > _trafficLightsIntersectionData.YellowLightTime) { if (_carsInIntersection.Count == 0 || _trafficLightsIntersectionData.ExitWaypoints.Length == 0) { ChangeCurrentRoadColors(_currentRoad, TrafficLightsColor.Red); _currentRoad++; _currentRoad = GetValidValue(_currentRoad); ChangeCurrentRoadColors(_currentRoad, TrafficLightsColor.Green); _yellowLight = false; _currentTime = realtimeSinceStartup; ApplyColorChanges(); } } } } internal override List GetStopWaypoints() { var result = new List(); for (int i = 0; i < _trafficLightsIntersectionData.StopWaypoints.Length; i++) { result.AddRange(_trafficLightsIntersectionData.StopWaypoints[i].roadWaypoints); } return result; } /// /// Used to set up custom behavior for traffic lights /// /// internal void SetTrafficLightsBehaviour(TrafficLightsBehaviour trafficLightsBehaviour) { _trafficLightsBehaviour = trafficLightsBehaviour; } internal void SetGreenRoad(int roadIndex, bool doNotChangeAgain) { _stopUpdate = doNotChangeAgain; ChangeCurrentRoadColors(roadIndex, TrafficLightsColor.Green); ChangeAllRoadsExceptSelectd(roadIndex, TrafficLightsColor.Red); ApplyColorChanges(); } /// /// After all intersection changes have been made this method apply them to the waypoint system and traffic lights /// private void ApplyColorChanges() { for (int i = 0; i < _intersectionState.Length; i++) { //change waypoint color UpdateCurrentIntersectionWaypoints(i, _intersectionState[i] != TrafficLightsColor.Green); if (i < _trafficLightsIntersectionData.StopWaypoints.Length) { //change traffic lights color _trafficLightsBehaviour?.Invoke(_intersectionState[i], _trafficLightsIntersectionData.StopWaypoints[i].redLightObjects, _trafficLightsIntersectionData.StopWaypoints[i].yellowLightObjects, _trafficLightsIntersectionData.StopWaypoints[i].greenLightObjects, _trafficLightsIntersectionData.Name); } } } /// /// Trigger state changes for specified waypoints /// /// /// private void UpdateCurrentIntersectionWaypoints(int road, bool stop) { if (_hasPedestrians && road >= _trafficLightsIntersectionData.StopWaypoints.Length) { TriggerPedestrianWaypointsUpdate(stop); return; } for (int j = 0; j < _trafficLightsIntersectionData.StopWaypoints[road].roadWaypoints.Length; j++) { WaypointEvents.TriggerTrafficLightChangedEvent(_trafficLightsIntersectionData.StopWaypoints[road].roadWaypoints[j], stop); } } /// /// Change color for specified road /// /// /// private void ChangeCurrentRoadColors(int currentRoad, TrafficLightsColor newColor) { if (currentRoad < _intersectionState.Length) { _intersectionState[currentRoad] = newColor; } else { Debug.LogError(currentRoad + "is grated than the max number of roads for intersection " + _trafficLightsIntersectionData.Name); } } /// /// Change color for all roads except the specified one /// /// /// private void ChangeAllRoadsExceptSelectd(int currentRoad, TrafficLightsColor newColor) { for (int i = 0; i < _intersectionState.Length; i++) { if (i != currentRoad) { _intersectionState[i] = newColor; } } } /// /// Correctly increment the road number /// /// /// private int GetValidValue(int roadNumber) { if (roadNumber >= _nrOfRoads) { roadNumber = roadNumber % _nrOfRoads; } if (roadNumber < 0) { roadNumber = _nrOfRoads + roadNumber; } return roadNumber; } private void GetPedestrianRoads(IPedestrianWaypointsDataHandler pedestrianWaypointsDataHandler) { if (_trafficLightsIntersectionData.PedestrianWaypoints.Length > 0) { _hasPedestrians = true; _nrOfRoads += 1; pedestrianWaypointsDataHandler.SetIntersection(_trafficLightsIntersectionData.PedestrianWaypoints, this); } } private void SetPedestrianGreenLightTime() { if (_hasPedestrians) { _roadGreenLightTime[_roadGreenLightTime.Length - 1] = _trafficLightsIntersectionData.PedestrianGreenLightTime; } } private void TriggerPedestrianWaypointsUpdate(bool stop) { #if GLEY_PEDESTRIAN_SYSTEM if (!PedestrianSystem.API.IsInitialized()) { CoroutineManager.StartStaticCoroutine(WaitForInitialization(stop)); return; } for (int i = 0; i < _trafficLightsIntersectionData.RedLightObjects.Length; i++) { if (_trafficLightsIntersectionData.RedLightObjects[i].activeSelf != stop) { _trafficLightsIntersectionData.RedLightObjects[i].SetActive(stop); } } for (int i = 0; i < _trafficLightsIntersectionData.GreenLightObjects.Length; i++) { if (_trafficLightsIntersectionData.GreenLightObjects[i].activeSelf != !stop) { _trafficLightsIntersectionData.GreenLightObjects[i].SetActive(!stop); } } for (int i = 0; i < _trafficLightsIntersectionData.PedestrianWaypoints.Length; i++) { PedestrianSystem.Events.TriggerStopStateChangedEvent(_trafficLightsIntersectionData.PedestrianWaypoints[i], stop); } #endif } #if GLEY_PEDESTRIAN_SYSTEM IEnumerator WaitForInitialization(bool stop) { if (!Gley.PedestrianSystem.Internal.PedestrianManager.Exists) { yield break; } while (!PedestrianSystem.API.IsInitialized()) { yield return null; } TriggerPedestrianWaypointsUpdate(stop); } #endif } }