public class CustomTrainAI : TrainAI { // correct would be to inherit from VehicleAI (in order to keep the correct references to `base`) public void TrafficManagerSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) { if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) { byte pathFindFlags = Singleton <PathManager> .instance.m_pathUnits.m_buffer[(int)((UIntPtr)vehicleData.m_path)].m_pathFindFlags; #if USEPATHWAITCOUNTER if ((pathFindFlags & (PathUnit.FLAG_READY | PathUnit.FLAG_FAILED)) != 0) { VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleId); state.PathWaitCounter = 0; // NON-STOCK CODE } #endif if ((pathFindFlags & PathUnit.FLAG_READY) != 0) { try { this.PathFindReady(vehicleId, ref vehicleData); } catch (Exception e) { Log.Warning($"TrainAI.PathFindReady({vehicleId}) for vehicle {vehicleData.Info?.m_class?.name} threw an exception: {e.ToString()}"); vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; Singleton <PathManager> .instance.ReleasePath(vehicleData.m_path); vehicleData.m_path = 0u; vehicleData.Unspawn(vehicleId); return; } } else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) { vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; Singleton <PathManager> .instance.ReleasePath(vehicleData.m_path); vehicleData.m_path = 0u; vehicleData.Unspawn(vehicleId); return; } #if USEPATHWAITCOUNTER else { VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleId); state.PathWaitCounter = (ushort)Math.Min(ushort.MaxValue, (int)state.PathWaitCounter + 1); // NON-STOCK CODE } #endif } else { if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) { this.TrySpawn(vehicleId, ref vehicleData); } } bool reversed = (vehicleData.m_flags & Vehicle.Flags.Reversed) != 0; ushort frontVehicleId; if (reversed) { frontVehicleId = vehicleData.GetLastVehicle(vehicleId); } else { frontVehicleId = vehicleId; } /// NON-STOCK CODE START /// VehicleStateManager vehStateManager = VehicleStateManager.Instance(); if (Options.prioritySignsEnabled || Options.timedLightsEnabled) { try { vehStateManager.UpdateVehiclePos(frontVehicleId, ref Singleton <VehicleManager> .instance.m_vehicles.m_buffer[frontVehicleId]); } catch (Exception e) { Log.Error("TrainAI TrafficManagerSimulationStep (2) Error: " + e.ToString()); } } if (!Options.isStockLaneChangerUsed()) { try { //Log._Debug($"HandleVehicle for trams. vehicleId={vehicleId} frontVehicleId={frontVehicleId}"); vehStateManager.LogTraffic(frontVehicleId, ref Singleton <VehicleManager> .instance.m_vehicles.m_buffer[frontVehicleId], true); } catch (Exception e) { Log.Error("TrainAI TrafficManagerSimulationStep (1) Error: " + e.ToString()); } } /// NON-STOCK CODE END /// VehicleManager instance = Singleton <VehicleManager> .instance; VehicleInfo info = instance.m_vehicles.m_buffer[(int)frontVehicleId].Info; info.m_vehicleAI.SimulationStep(frontVehicleId, ref instance.m_vehicles.m_buffer[(int)frontVehicleId], vehicleId, ref vehicleData, 0); if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) { return; } bool flag2 = (vehicleData.m_flags & Vehicle.Flags.Reversed) != 0; if (flag2 != reversed) { reversed = flag2; if (reversed) { frontVehicleId = vehicleData.GetLastVehicle(vehicleId); } else { frontVehicleId = vehicleId; } info = instance.m_vehicles.m_buffer[(int)frontVehicleId].Info; info.m_vehicleAI.SimulationStep(frontVehicleId, ref instance.m_vehicles.m_buffer[(int)frontVehicleId], vehicleId, ref vehicleData, 0); if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) { return; } flag2 = ((vehicleData.m_flags & Vehicle.Flags.Reversed) != 0); if (flag2 != reversed) { Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId); return; } } if (reversed) { frontVehicleId = instance.m_vehicles.m_buffer[(int)frontVehicleId].m_leadingVehicle; int num2 = 0; while (frontVehicleId != 0) { info = instance.m_vehicles.m_buffer[(int)frontVehicleId].Info; info.m_vehicleAI.SimulationStep(frontVehicleId, ref instance.m_vehicles.m_buffer[(int)frontVehicleId], vehicleId, ref vehicleData, 0); if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) { return; } frontVehicleId = instance.m_vehicles.m_buffer[(int)frontVehicleId].m_leadingVehicle; if (++num2 > 16384) { CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace); break; } } } else { frontVehicleId = instance.m_vehicles.m_buffer[(int)frontVehicleId].m_trailingVehicle; int num3 = 0; while (frontVehicleId != 0) { info = instance.m_vehicles.m_buffer[(int)frontVehicleId].Info; info.m_vehicleAI.SimulationStep(frontVehicleId, ref instance.m_vehicles.m_buffer[(int)frontVehicleId], vehicleId, ref vehicleData, 0); if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) { return; } frontVehicleId = instance.m_vehicles.m_buffer[(int)frontVehicleId].m_trailingVehicle; if (++num3 > 16384) { CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace); break; } } } if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace | Vehicle.Flags.WaitingCargo)) == 0 || (vehicleData.m_blockCounter == 255 /*&& Options.enableDespawning*/)) { Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId); } }
/// <summary> /// Checks for traffic lights and priority signs when changing segments (for road & rail vehicles). /// Sets the maximum allowed speed <paramref name="maxSpeed"/> if segment change is not allowed (otherwise <paramref name="maxSpeed"/> has to be set by the calling method). /// </summary> /// <param name="vehicleId">vehicle id</param> /// <param name="vehicleData">vehicle data</param> /// <param name="lastFrameData">last frame data of vehicle</param> /// <param name="isRecklessDriver">if true, this vehicle ignores red traffic lights and priority signs</param> /// <param name="prevPos">previous path position</param> /// <param name="prevTargetNodeId">previous target node</param> /// <param name="prevLaneID">previous lane</param> /// <param name="position">current path position</param> /// <param name="targetNodeId">transit node</param> /// <param name="laneID">current lane</param> /// <param name="nextPosition">next path position</param> /// <param name="nextTargetNodeId">next target node</param> /// <param name="maxSpeed">maximum allowed speed (only valid if method returns false)</param> /// <returns>true, if the vehicle may change segments, false otherwise.</returns> internal static bool MayChangeSegment(ushort vehicleId, ref Vehicle vehicleData, ref Vehicle.Frame lastFrameData, bool isRecklessDriver, ref PathUnit.Position prevPos, ushort prevTargetNodeId, uint prevLaneID, ref PathUnit.Position position, ushort targetNodeId, uint laneID, ref PathUnit.Position nextPosition, ushort nextTargetNodeId, out float maxSpeed, bool debug = false) { debug = false; if (prevTargetNodeId != targetNodeId) { // method should only be called if targetNodeId == prevTargetNode maxSpeed = 0f; return(true); } bool forceUpdatePos = false; VehicleState vehicleState = null; try { vehicleState = VehicleStateManager.GetVehicleState(vehicleId); if (vehicleState == null) { VehicleStateManager.OnPathFindReady(vehicleId, ref vehicleData); vehicleState = VehicleStateManager.GetVehicleState(vehicleId); if (vehicleState == null) { #if DEBUG Log._Debug($"Could not get vehicle state of {vehicleId}!"); #endif } else { forceUpdatePos = true; } } } catch (Exception e) { Log.Error("VehicleAI MayChangeSegment vehicle state error: " + e.ToString()); } if (forceUpdatePos || Options.simAccuracy >= 2) { try { VehicleStateManager.UpdateVehiclePos(vehicleId, ref vehicleData, ref prevPos, ref position); } catch (Exception e) { Log.Error("VehicleAI MayChangeSegment Error: " + e.ToString()); } } var netManager = Singleton <NetManager> .instance; uint currentFrameIndex = Singleton <SimulationManager> .instance.m_currentFrameIndex; uint prevTargetNodeLower8Bits = (uint)((prevTargetNodeId << 8) / 32768); uint random = currentFrameIndex - prevTargetNodeLower8Bits & 255u; bool isRailVehicle = (vehicleData.Info.m_vehicleType & (VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Metro)) != VehicleInfo.VehicleType.None; NetNode.Flags targetNodeFlags = netManager.m_nodes.m_buffer[targetNodeId].m_flags; bool hasTrafficLight = (targetNodeFlags & NetNode.Flags.TrafficLights) != NetNode.Flags.None; bool checkTrafficLights = false; if (!isRailVehicle) { // check if to check space #if DEBUG if (debug) { Log._Debug($"CustomVehicleAI.MayChangeSegment: Vehicle {vehicleId} is not a train."); } #endif var prevLaneFlags = (NetLane.Flags)netManager.m_lanes.m_buffer[(int)((UIntPtr)prevLaneID)].m_flags; var hasCrossing = (targetNodeFlags & NetNode.Flags.LevelCrossing) != NetNode.Flags.None; var isJoinedJunction = (prevLaneFlags & NetLane.Flags.JoinedJunction) != NetLane.Flags.None; bool checkSpace = !Flags.getEnterWhenBlockedAllowed(prevPos.m_segment, netManager.m_segments.m_buffer[prevPos.m_segment].m_startNode == targetNodeId) && !isRecklessDriver; //TrafficLightSimulation nodeSim = TrafficLightSimulation.GetNodeSimulation(destinationNodeId); //if (timedNode != null && timedNode.vehiclesMayEnterBlockedJunctions) { // checkSpace = false; //} if (checkSpace) { // check if there is enough space if ((targetNodeFlags & (NetNode.Flags.Junction | NetNode.Flags.OneWayOut | NetNode.Flags.OneWayIn)) == NetNode.Flags.Junction && netManager.m_nodes.m_buffer[targetNodeId].CountSegments() != 2) { var len = vehicleData.CalculateTotalLength(vehicleId) + 2f; if (!netManager.m_lanes.m_buffer[(int)((UIntPtr)laneID)].CheckSpace(len)) { var sufficientSpace = false; if (nextPosition.m_segment != 0 && netManager.m_lanes.m_buffer[(int)((UIntPtr)laneID)].m_length < 30f) { NetNode.Flags nextTargetNodeFlags = netManager.m_nodes.m_buffer[nextTargetNodeId].m_flags; if ((nextTargetNodeFlags & (NetNode.Flags.Junction | NetNode.Flags.OneWayOut | NetNode.Flags.OneWayIn)) != NetNode.Flags.Junction || netManager.m_nodes.m_buffer[nextTargetNodeId].CountSegments() == 2) { uint nextLaneId = PathManager.GetLaneID(nextPosition); if (nextLaneId != 0u) { sufficientSpace = netManager.m_lanes.m_buffer[(int)((UIntPtr)nextLaneId)].CheckSpace(len); } } } if (!sufficientSpace) { maxSpeed = 0f; try { if (vehicleState != null) { #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState to BLOCKED"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Blocked; } } catch (Exception e) { Log.Error("VehicleAI MayChangeSegment error while setting junction state to BLOCKED: " + e.ToString()); } return(false); } } } } checkTrafficLights = (!isJoinedJunction || hasCrossing); } else { #if DEBUG if (debug) { Log._Debug($"CustomVehicleAI.MayChangeSegment: Vehicle {vehicleId} is a train."); } #endif checkTrafficLights = true; } try { if (vehicleState != null && vehicleState.JunctionTransitState == VehicleJunctionTransitState.Blocked) { #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState from BLOCKED to ENTER"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Enter; } if ((vehicleData.m_flags & Vehicle.Flags.Emergency2) == 0) { if (hasTrafficLight && checkTrafficLights) { #if DEBUG if (debug) { Log._Debug($"CustomVehicleAI.MayChangeSegment: Node {targetNodeId} has a traffic light."); } #endif var destinationInfo = netManager.m_nodes.m_buffer[targetNodeId].Info; if (vehicleState != null && vehicleState.JunctionTransitState == VehicleJunctionTransitState.None) { #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState to ENTER (1)"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Enter; } RoadBaseAI.TrafficLightState vehicleLightState; RoadBaseAI.TrafficLightState pedestrianLightState; bool vehicles; bool pedestrians; CustomRoadAI.GetTrafficLightState(vehicleId, ref vehicleData, targetNodeId, prevPos.m_segment, prevPos.m_lane, position.m_segment, ref netManager.m_segments.m_buffer[prevPos.m_segment], currentFrameIndex - prevTargetNodeLower8Bits, out vehicleLightState, out pedestrianLightState, out vehicles, out pedestrians); if (vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Car && isRecklessDriver) // no reckless driving at railroad crossings { vehicleLightState = RoadBaseAI.TrafficLightState.Green; } #if DEBUG if (debug) { Log._Debug($"CustomVehicleAI.MayChangeSegment: Vehicle {vehicleId} has {vehicleLightState} at node {targetNodeId}"); } #endif if (!vehicles && random >= 196u) { vehicles = true; RoadBaseAI.SetTrafficLightState(targetNodeId, ref netManager.m_segments.m_buffer[prevPos.m_segment], currentFrameIndex - prevTargetNodeLower8Bits, vehicleLightState, pedestrianLightState, vehicles, pedestrians); } var stopCar = false; switch (vehicleLightState) { case RoadBaseAI.TrafficLightState.RedToGreen: if (random < 60u) { stopCar = true; } else { #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState to LEAVE (RedToGreen)"); } #endif if (vehicleState != null) { vehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave; } } break; case RoadBaseAI.TrafficLightState.Red: stopCar = true; break; case RoadBaseAI.TrafficLightState.GreenToRed: if (random >= 30u) { stopCar = true; } else if (vehicleState != null) { #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState to LEAVE (GreenToRed)"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave; } break; } /*if ((vehicleLightState == RoadBaseAI.TrafficLightState.Green || vehicleLightState == RoadBaseAI.TrafficLightState.RedToGreen) && !Flags.getEnterWhenBlockedAllowed(prevPos.m_segment, netManager.m_segments.m_buffer[prevPos.m_segment].m_startNode == targetNodeId)) { * var hasIncomingCars = TrafficPriority.HasIncomingVehiclesWithHigherPriority(vehicleId, targetNodeId); * * if (hasIncomingCars) { * // green light but other cars are incoming and they have priority: stop * stopCar = true; * } * }*/ if (stopCar) { if (vehicleState != null) { #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState to STOP"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop; } maxSpeed = 0f; return(false); } } else if (vehicleState != null) { #if DEBUG //bool debug = destinationNodeId == 10864; //bool debug = destinationNodeId == 13531; //bool debug = false;// targetNodeId == 5027; #endif //bool debug = false; #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId} is arriving @ seg. {prevPos.m_segment} ({position.m_segment}, {nextPosition.m_segment}), node {targetNodeId} which is not a traffic light."); } #endif var prioritySegment = TrafficPriority.GetPrioritySegment(targetNodeId, prevPos.m_segment); if (prioritySegment != null) { #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId} is arriving @ seg. {prevPos.m_segment} ({position.m_segment}, {nextPosition.m_segment}), node {targetNodeId} which is not a traffic light and is a priority segment."); } #endif //if (prioritySegment.HasVehicle(vehicleId)) { #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: segment target position found"); } #endif #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: global target position found. carState = {vehicleState.JunctionTransitState.ToString()}"); } #endif var currentFrameIndex2 = Singleton <SimulationManager> .instance.m_currentFrameIndex; var frame = currentFrameIndex2 >> 4; float speed = lastFrameData.m_velocity.magnitude; if (vehicleState.JunctionTransitState == VehicleJunctionTransitState.None) { #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState to ENTER (prio)"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Enter; } if (vehicleState.JunctionTransitState != VehicleJunctionTransitState.Leave) { bool hasIncomingCars; switch (prioritySegment.Type) { case SegmentEnd.PriorityType.Stop: #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: STOP sign. waittime={vehicleState.WaitTime}, vel={speed}"); } #endif if (Options.simAccuracy <= 2 || (Options.simAccuracy >= 3 && vehicleState.WaitTime < MaxPriorityWaitTime)) { #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState to STOP (wait)"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop; if (speed <= TrafficPriority.maxStopVelocity) { vehicleState.WaitTime++; float minStopWaitTime = UnityEngine.Random.Range(0f, 3f); if (vehicleState.WaitTime >= minStopWaitTime) { if (Options.simAccuracy >= 4) { vehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave; } else { hasIncomingCars = TrafficPriority.HasIncomingVehiclesWithHigherPriority(vehicleId, ref vehicleData, ref prevPos, ref position); #if DEBUG if (debug) { Log._Debug($"hasIncomingCars: {hasIncomingCars}"); } #endif if (hasIncomingCars) { maxSpeed = 0f; return(false); } #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState to LEAVE (min wait timeout)"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave; } } else { maxSpeed = 0; return(false); } } else { vehicleState.WaitTime = 0; maxSpeed = 0f; return(false); } } else { #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState to LEAVE (max wait timeout)"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave; } break; case SegmentEnd.PriorityType.Yield: #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: YIELD sign. waittime={vehicleState.WaitTime}"); } #endif if (Options.simAccuracy <= 2 || (Options.simAccuracy >= 3 && vehicleState.WaitTime < MaxPriorityWaitTime)) { vehicleState.WaitTime++; #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState to STOP (wait)"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop; if (speed <= TrafficPriority.maxYieldVelocity || Options.simAccuracy <= 2) { if (Options.simAccuracy >= 4) { vehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave; } else { hasIncomingCars = TrafficPriority.HasIncomingVehiclesWithHigherPriority(vehicleId, ref vehicleData, ref prevPos, ref position); #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: hasIncomingCars: {hasIncomingCars}"); } #endif if (hasIncomingCars) { maxSpeed = 0f; return(false); } else { #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState to LEAVE (no incoming cars)"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave; } } } else { #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Vehicle has not yet reached yield speed (reduce {speed} by {vehicleState.ReduceSpeedByValueToYield})"); } #endif // vehicle has not yet reached yield speed maxSpeed = TrafficPriority.maxYieldVelocity; return(false); } } else { #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState to LEAVE (max wait timeout)"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave; } break; case SegmentEnd.PriorityType.Main: case SegmentEnd.PriorityType.None: #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: MAIN sign. waittime={vehicleState.WaitTime}"); } #endif maxSpeed = 0f; if (Options.simAccuracy == 4) { return(true); } if (Options.simAccuracy <= 2 || (Options.simAccuracy == 3 && vehicleState.WaitTime < MaxPriorityWaitTime)) { vehicleState.WaitTime++; #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState to STOP (wait)"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop; hasIncomingCars = TrafficPriority.HasIncomingVehiclesWithHigherPriority(vehicleId, ref vehicleData, ref prevPos, ref position); #if DEBUG if (debug) { Log._Debug($"hasIncomingCars: {hasIncomingCars}"); } #endif if (hasIncomingCars) { return(false); } #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState to LEAVE (no conflicting car)"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave; } return(true); } } else if (speed <= TrafficPriority.maxStopVelocity) { // vehicle is not moving. reset allowance to leave junction #if DEBUG if (debug) { Log._Debug($"Vehicle {vehicleId}: Setting JunctionTransitState from LEAVE to BLOCKED (speed to low)"); } #endif vehicleState.JunctionTransitState = VehicleJunctionTransitState.Blocked; maxSpeed = 0f; return(false); } } } } } catch (Exception e) { Log.Error($"Error occured in MayChangeSegment: {e.ToString()}"); } maxSpeed = 0f; // maxSpeed should be set by caller return(true); }
/// <summary> /// Lightweight simulation step method. /// This method is occasionally being called for different cars. /// </summary> /// <param name="vehicleId"></param> /// <param name="vehicleData"></param> /// <param name="physicsLodRefPos"></param> public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) { PathManager pathMan = Singleton <PathManager> .instance; #if DEBUG /*if (!GlobalConfig.Instance.DebugSwitches[0]) { * Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}) called. flags: {vehicleData.m_flags} pfFlags: {pathMan.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags}"); * }*/ #endif // NON-STOCK CODE START VehicleState state = null; ExtCitizenInstance driverExtInstance = null; bool prohibitPocketCars = Options.prohibitPocketCars; if (prohibitPocketCars) { // check for valid driver and update return path state state = VehicleStateManager.Instance._GetVehicleState(vehicleData.GetFirstVehicle(vehicleId)); if (state.VehicleType == ExtVehicleType.PassengerCar) { driverExtInstance = state.GetDriverExtInstance(); if (driverExtInstance == null) { prohibitPocketCars = false; } else { driverExtInstance.UpdateReturnPathState(); } } else { prohibitPocketCars = false; } } // NON-STOCK CODE END if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0 && (!prohibitPocketCars || driverExtInstance.ReturnPathState != ExtCitizenInstance.ExtPathState.Calculating)) // NON-STOCK CODE: Parking AI: wait for the return path to be calculated { PathManager pathManager = Singleton <PathManager> .instance; byte pathFindFlags = pathManager.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags; bool pathFindFailed = (pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0; // path == 0: non-stock code! bool pathFindSucceeded = (pathFindFlags & PathUnit.FLAG_READY) != 0; #if USEPATHWAITCOUNTER if ((pathFindFlags & (PathUnit.FLAG_READY | PathUnit.FLAG_FAILED)) != 0) { VehicleState state = VehicleStateManager.Instance._GetVehicleState(vehicleId); state.PathWaitCounter = 0; // NON-STOCK CODE } #endif if (prohibitPocketCars) { if (driverExtInstance.ReturnPathState == ExtPathState.Failed) { // no walking path from parking position to target found. flag main path as 'failed'. #if DEBUG if (GlobalConfig.Instance.DebugSwitches[2]) { Log._Debug($"CustomCarAI.CustomSimulationStep: Return path {driverExtInstance.ReturnPathId} FAILED. Forcing path-finding to fail."); } #endif pathFindSucceeded = false; pathFindFailed = true; } driverExtInstance.ReleaseReturnPath(); if (pathFindSucceeded) { CustomPassengerCarAI.OnPathFindSuccess(vehicleId, ref vehicleData, driverExtInstance); } else if (pathFindFailed) { CustomPassengerCarAI.OnPathFindFailure(driverExtInstance, vehicleId); } } if (pathFindSucceeded) { vehicleData.m_pathPositionIndex = 255; vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; vehicleData.m_flags &= ~Vehicle.Flags.Arriving; this.PathfindSuccess(vehicleId, ref vehicleData); this.TrySpawn(vehicleId, ref vehicleData); } else if (pathFindFailed) { vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; Singleton <PathManager> .instance.ReleasePath(vehicleData.m_path); vehicleData.m_path = 0u; this.PathfindFailure(vehicleId, ref vehicleData); return; } #if USEPATHWAITCOUNTER else { VehicleState state = VehicleStateManager.Instance._GetVehicleState(vehicleId); state.PathWaitCounter = (ushort)Math.Min(ushort.MaxValue, (int)state.PathWaitCounter + 1); // NON-STOCK CODE } #endif } else { if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) { this.TrySpawn(vehicleId, ref vehicleData); } } /// NON-STOCK CODE START /// VehicleStateManager vehStateManager = VehicleStateManager.Instance; if (Options.prioritySignsEnabled || Options.timedLightsEnabled) { // update vehicle position for timed traffic lights and priority signs try { vehStateManager.UpdateVehiclePos(vehicleId, ref vehicleData); } catch (Exception e) { Log.Error("CarAI CustomSimulationStep Error: " + e.ToString()); } } if (!Options.isStockLaneChangerUsed()) { // Advanced AI traffic measurement try { vehStateManager.LogTraffic(vehicleId, ref vehicleData, true); } catch (Exception e) { Log.Error("CarAI CustomSimulationStep Error: " + e.ToString()); } } /// NON-STOCK CODE END /// Vector3 lastFramePosition = vehicleData.GetLastFramePosition(); int lodPhysics; if (Vector3.SqrMagnitude(physicsLodRefPos - lastFramePosition) >= 1210000f) { lodPhysics = 2; } else if (Vector3.SqrMagnitude(Singleton <SimulationManager> .instance.m_simulationView.m_position - lastFramePosition) >= 250000f) { lodPhysics = 1; } else { lodPhysics = 0; } this.SimulationStep(vehicleId, ref vehicleData, vehicleId, ref vehicleData, lodPhysics); if (vehicleData.m_leadingVehicle == 0 && vehicleData.m_trailingVehicle != 0) { VehicleManager vehManager = Singleton <VehicleManager> .instance; ushort num = vehicleData.m_trailingVehicle; int num2 = 0; while (num != 0) { ushort trailingVehicle = vehManager.m_vehicles.m_buffer[(int)num].m_trailingVehicle; VehicleInfo info = vehManager.m_vehicles.m_buffer[(int)num].Info; info.m_vehicleAI.SimulationStep(num, ref vehManager.m_vehicles.m_buffer[(int)num], vehicleId, ref vehicleData, lodPhysics); num = trailingVehicle; if (++num2 > 16384) { CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace); break; } } } #if PATHRECALC ushort recalcSegmentId = 0; #endif int privateServiceIndex = ItemClass.GetPrivateServiceIndex(this.m_info.m_class.m_service); int maxBlockCounter = (privateServiceIndex == -1) ? 150 : 100; if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace)) == 0 && vehicleData.m_cargoParent == 0) { Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId); } else if ((int)vehicleData.m_blockCounter >= maxBlockCounter && Options.enableDespawning) { Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId); } #if PATHRECALC else if (vehicleData.m_leadingVehicle == 0 && CustomVehicleAI.ShouldRecalculatePath(vehicleId, ref vehicleData, maxBlockCounter, out recalcSegmentId)) { CustomVehicleAI.MarkPathRecalculation(vehicleId, recalcSegmentId); InvalidPath(vehicleId, ref vehicleData, vehicleId, ref vehicleData); } #endif }
/// <summary> /// Lightweight simulation step method. /// This method is occasionally being called for different cars. /// </summary> /// <param name="vehicleId"></param> /// <param name="vehicleData"></param> /// <param name="physicsLodRefPos"></param> public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) { PathManager pathMan = Singleton <PathManager> .instance; #if DEBUG /*if (!Options.disableSomething1) { * Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}) called. flags: {vehicleData.m_flags} pfFlags: {pathMan.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags}"); * }*/ #endif if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) { PathManager pathManager = Singleton <PathManager> .instance; byte pathFindFlags = pathManager.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags; #if USEPATHWAITCOUNTER if ((pathFindFlags & (PathUnit.FLAG_READY | PathUnit.FLAG_FAILED)) != 0) { VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleId); state.PathWaitCounter = 0; // NON-STOCK CODE } #endif if ((pathFindFlags & PathUnit.FLAG_READY) != 0) { vehicleData.m_pathPositionIndex = 255; vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; vehicleData.m_flags &= ~Vehicle.Flags.Arriving; this.PathfindSuccess(vehicleId, ref vehicleData); this.TrySpawn(vehicleId, ref vehicleData); } else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) // path == 0: non-stock code! { vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; Singleton <PathManager> .instance.ReleasePath(vehicleData.m_path); vehicleData.m_path = 0u; this.PathfindFailure(vehicleId, ref vehicleData); return; } #if USEPATHWAITCOUNTER else { VehicleState state = VehicleStateManager.Instance()._GetVehicleState(vehicleId); state.PathWaitCounter = (ushort)Math.Min(ushort.MaxValue, (int)state.PathWaitCounter + 1); // NON-STOCK CODE } #endif } else { if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) { this.TrySpawn(vehicleId, ref vehicleData); } } /// NON-STOCK CODE START /// VehicleStateManager vehStateManager = VehicleStateManager.Instance(); if (Options.prioritySignsEnabled || Options.timedLightsEnabled) { try { vehStateManager.UpdateVehiclePos(vehicleId, ref vehicleData); } catch (Exception e) { Log.Error("CarAI CustomSimulationStep Error: " + e.ToString()); } } if (!Options.isStockLaneChangerUsed()) { try { vehStateManager.LogTraffic(vehicleId, ref vehicleData, true); } catch (Exception e) { Log.Error("CarAI CustomSimulationStep Error: " + e.ToString()); } } /// NON-STOCK CODE END /// Vector3 lastFramePosition = vehicleData.GetLastFramePosition(); int lodPhysics; if (Vector3.SqrMagnitude(physicsLodRefPos - lastFramePosition) >= 1210000f) { lodPhysics = 2; } else if (Vector3.SqrMagnitude(Singleton <SimulationManager> .instance.m_simulationView.m_position - lastFramePosition) >= 250000f) { lodPhysics = 1; } else { lodPhysics = 0; } this.SimulationStep(vehicleId, ref vehicleData, vehicleId, ref vehicleData, lodPhysics); if (vehicleData.m_leadingVehicle == 0 && vehicleData.m_trailingVehicle != 0) { VehicleManager instance2 = Singleton <VehicleManager> .instance; ushort num = vehicleData.m_trailingVehicle; int num2 = 0; while (num != 0) { ushort trailingVehicle = instance2.m_vehicles.m_buffer[(int)num].m_trailingVehicle; VehicleInfo info = instance2.m_vehicles.m_buffer[(int)num].Info; info.m_vehicleAI.SimulationStep(num, ref instance2.m_vehicles.m_buffer[(int)num], vehicleId, ref vehicleData, lodPhysics); num = trailingVehicle; if (++num2 > 16384) { CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace); break; } } } #if PATHRECALC ushort recalcSegmentId = 0; #endif int privateServiceIndex = ItemClass.GetPrivateServiceIndex(this.m_info.m_class.m_service); int maxBlockCounter = (privateServiceIndex == -1) ? 150 : 100; if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace)) == 0 && vehicleData.m_cargoParent == 0) { Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId); } else if ((int)vehicleData.m_blockCounter >= maxBlockCounter && Options.enableDespawning) { Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId); } #if PATHRECALC else if (vehicleData.m_leadingVehicle == 0 && CustomVehicleAI.ShouldRecalculatePath(vehicleId, ref vehicleData, maxBlockCounter, out recalcSegmentId)) { CustomVehicleAI.MarkPathRecalculation(vehicleId, recalcSegmentId); InvalidPath(vehicleId, ref vehicleData, vehicleId, ref vehicleData); } #endif }
public void CustomCheckNextLane(ushort vehicleID, ref Vehicle vehicleData, ref float maxSpeed, PathUnit.Position position, uint laneID, byte offset, PathUnit.Position prevPos, uint prevLaneID, byte prevOffset, Bezier3 bezier) { if (Options.simAccuracy <= 1) { try { VehicleStateManager.UpdateVehiclePos(vehicleID, ref vehicleData, ref prevPos, ref position); } catch (Exception e) { Log.Error("TrainAI CustomCheckNextLane Error: " + e.ToString()); } } NetManager instance = Singleton <NetManager> .instance; Vehicle.Frame lastFrameData = vehicleData.GetLastFrameData(); Vector3 a = lastFrameData.m_position; Vector3 a2 = lastFrameData.m_position; Vector3 b = lastFrameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f); a += b; a2 -= b; float num = 0.5f * lastFrameData.m_velocity.sqrMagnitude / this.m_info.m_braking; float a3 = Vector3.Distance(a, bezier.a); float b2 = Vector3.Distance(a2, bezier.a); if (Mathf.Min(a3, b2) >= num - 5f) { if (!instance.m_lanes.m_buffer[(int)((UIntPtr)laneID)].CheckSpace(1000f, vehicleID)) { maxSpeed = 0f; return; } Vector3 vector = bezier.Position(0.5f); Segment3 segment; if (Vector3.SqrMagnitude(vehicleData.m_segment.a - vector) < Vector3.SqrMagnitude(bezier.a - vector)) { segment = new Segment3(vehicleData.m_segment.a, vector); } else { segment = new Segment3(bezier.a, vector); } if (segment.LengthSqr() >= 3f) { segment.a += (segment.b - segment.a).normalized * 2.5f; if (CustomTrainAI.CheckOverlap(vehicleID, ref vehicleData, segment, vehicleID)) { maxSpeed = 0f; return; } } segment = new Segment3(vector, bezier.d); if (segment.LengthSqr() >= 1f && CustomTrainAI.CheckOverlap(vehicleID, ref vehicleData, segment, vehicleID)) { maxSpeed = 0f; return; } ushort targetNodeId; if (offset < position.m_offset) { targetNodeId = instance.m_segments.m_buffer[(int)position.m_segment].m_startNode; } else { targetNodeId = instance.m_segments.m_buffer[(int)position.m_segment].m_endNode; } ushort prevTargetNodeId; if (prevOffset == 0) { prevTargetNodeId = instance.m_segments.m_buffer[(int)prevPos.m_segment].m_startNode; } else { prevTargetNodeId = instance.m_segments.m_buffer[(int)prevPos.m_segment].m_endNode; } if (targetNodeId == prevTargetNodeId) { float oldMaxSpeed = maxSpeed; #if DEBUG bool debug = false; // targetNodeId == 14527 || targetNodeId == 15048; if (debug) { Log._Debug($"Train {vehicleID} wants to change segment. seg. {prevPos.m_segment} -> node {targetNodeId} -> seg. {position.m_segment}"); } #else bool debug = false; #endif bool mayChange = CustomVehicleAI.MayChangeSegment(vehicleID, ref vehicleData, ref lastFrameData, false, ref prevPos, prevTargetNodeId, prevLaneID, ref position, targetNodeId, laneID, out maxSpeed, debug); if (!mayChange) { return; } maxSpeed = oldMaxSpeed; } } }
/// <summary> /// Lightweight simulation step method. /// This method is occasionally being called for different cars. /// </summary> /// <param name="vehicleId"></param> /// <param name="vehicleData"></param> /// <param name="physicsLodRefPos"></param> public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) { #if USEPATHWAITCOUNTER VehicleState state = VehicleStateManager._GetVehicleState(vehicleId); #endif if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) { PathManager instance = Singleton <PathManager> .instance; byte pathFindFlags = instance.m_pathUnits.m_buffer[(int)((UIntPtr)vehicleData.m_path)].m_pathFindFlags; if ((pathFindFlags & PathUnit.FLAG_READY) != 0) { #if USEPATHWAITCOUNTER state.PathWaitCounter = 0; // NON-STOCK CODE #endif vehicleData.m_pathPositionIndex = 255; vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; vehicleData.m_flags &= ~Vehicle.Flags.Arriving; this.PathfindSuccess(vehicleId, ref vehicleData); this.TrySpawn(vehicleId, ref vehicleData); VehicleStateManager.OnPathFindReady(vehicleId, ref vehicleData); // NON-STOCK CODE } else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0 #if USEPATHWAITCOUNTER || ((pathFindFlags & PathUnit.FLAG_CREATED) != 0 && state.PathWaitCounter == ushort.MaxValue) #endif ) // NON-STOCK CODE { #if USEPATHWAITCOUNTER state.PathWaitCounter = 0; // NON-STOCK CODE #endif vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; Singleton <PathManager> .instance.ReleasePath(vehicleData.m_path); vehicleData.m_path = 0u; this.PathfindFailure(vehicleId, ref vehicleData); return; } #if USEPATHWAITCOUNTER else { state.PathWaitCounter = (ushort)Math.Min(ushort.MaxValue, (int)state.PathWaitCounter + 1); // NON-STOCK CODE } #endif } else { #if USEPATHWAITCOUNTER state.PathWaitCounter = 0; // NON-STOCK CODE #endif if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) { this.TrySpawn(vehicleId, ref vehicleData); } } try { VehicleStateManager.LogTraffic(vehicleId, ref vehicleData, true); } catch (Exception e) { Log.Error("CarAI CustomSimulationStep Error: " + e.ToString()); } try { VehicleStateManager.UpdateVehiclePos(vehicleId, ref vehicleData); } catch (Exception e) { Log.Error("CarAI CustomSimulationStep Error: " + e.ToString()); } Vector3 lastFramePosition = vehicleData.GetLastFramePosition(); int lodPhysics; if (Vector3.SqrMagnitude(physicsLodRefPos - lastFramePosition) >= 1210000f) { lodPhysics = 2; } else if (Vector3.SqrMagnitude(Singleton <SimulationManager> .instance.m_simulationView.m_position - lastFramePosition) >= 250000f) { lodPhysics = 1; } else { lodPhysics = 0; } this.SimulationStep(vehicleId, ref vehicleData, vehicleId, ref vehicleData, lodPhysics); if (vehicleData.m_leadingVehicle == 0 && vehicleData.m_trailingVehicle != 0) { VehicleManager instance2 = Singleton <VehicleManager> .instance; ushort num = vehicleData.m_trailingVehicle; int num2 = 0; while (num != 0) { ushort trailingVehicle = instance2.m_vehicles.m_buffer[(int)num].m_trailingVehicle; VehicleInfo info = instance2.m_vehicles.m_buffer[(int)num].Info; info.m_vehicleAI.SimulationStep(num, ref instance2.m_vehicles.m_buffer[(int)num], vehicleId, ref vehicleData, lodPhysics); num = trailingVehicle; if (++num2 > 16384) { CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace); break; } } } #if PATHRECALC ushort recalcSegmentId = 0; #endif int privateServiceIndex = ItemClass.GetPrivateServiceIndex(this.m_info.m_class.m_service); int maxBlockCounter = (privateServiceIndex == -1) ? 150 : 100; if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace)) == 0 && vehicleData.m_cargoParent == 0) { Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId); } else if ((int)vehicleData.m_blockCounter == maxBlockCounter && Options.enableDespawning) { Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId); } #if PATHRECALC else if (vehicleData.m_leadingVehicle == 0 && CustomVehicleAI.ShouldRecalculatePath(vehicleId, ref vehicleData, maxBlockCounter, out recalcSegmentId)) { CustomVehicleAI.MarkPathRecalculation(vehicleId, recalcSegmentId); InvalidPath(vehicleId, ref vehicleData, vehicleId, ref vehicleData); } #endif }
public void CustomCalculateSegmentPosition(ushort vehicleId, ref Vehicle vehicleData, PathUnit.Position nextPosition, PathUnit.Position position, uint laneID, byte offset, PathUnit.Position prevPos, uint prevLaneID, byte prevOffset, int index, out Vector3 pos, out Vector3 dir, out float maxSpeed) { if (Options.simAccuracy <= 1) { try { VehicleStateManager.UpdateVehiclePos(vehicleId, ref vehicleData, ref prevPos, ref position); } catch (Exception e) { Log.Error("CarAI CustomCalculateSegmentPosition Error: " + e.ToString()); } } var netManager = Singleton <NetManager> .instance; //var vehicleManager = Singleton<VehicleManager>.instance; netManager.m_lanes.m_buffer[(int)((UIntPtr)laneID)].CalculatePositionAndDirection(offset * 0.003921569f, out pos, out dir); Vehicle.Frame lastFrameData = vehicleData.GetLastFrameData(); Vector3 lastFrameVehiclePos = lastFrameData.m_position; var camPos = Camera.main.transform.position; #if DEBUG //bool isEmergency = VehicleStateManager._GetVehicleState(vehicleId).VehicleType == ExtVehicleType.Emergency; #endif // I think this is supposed to be the lane position? // [VN, 12/23/2015] It's the 3D car position on the Bezier curve of the lane. // This crazy 0.003921569f equals to 1f/255 and prevOffset is the byte value (0..255) of the car position. var vehiclePosOnBezier = netManager.m_lanes.m_buffer[(int)((UIntPtr)prevLaneID)].CalculatePosition(prevOffset * 0.003921569f); //ushort currentSegmentId = netManager.m_lanes.m_buffer[(int)((UIntPtr)prevLaneID)].m_segment; ushort targetNodeId; ushort nextTargetNodeId; if (offset < position.m_offset) { targetNodeId = netManager.m_segments.m_buffer[position.m_segment].m_startNode; nextTargetNodeId = netManager.m_segments.m_buffer[position.m_segment].m_endNode; } else { targetNodeId = netManager.m_segments.m_buffer[position.m_segment].m_endNode; nextTargetNodeId = netManager.m_segments.m_buffer[position.m_segment].m_startNode; } var prevTargetNodeId = prevOffset == 0 ? netManager.m_segments.m_buffer[prevPos.m_segment].m_startNode : netManager.m_segments.m_buffer[prevPos.m_segment].m_endNode; // this seems to be like the required braking force in order to stop the vehicle within its half length. var crazyValue = 0.5f * lastFrameData.m_velocity.sqrMagnitude / m_info.m_braking + m_info.m_generatedInfo.m_size.z * 0.5f; /*try { * VehicleStateManager.UpdateVehiclePos(vehicleId, ref vehicleData); * } catch (Exception e) { * Log.Error("CarAI TmCalculateSegmentPosition Error: " + e.ToString()); * }*/ bool isRecklessDriver = IsRecklessDriver(vehicleId, ref vehicleData); if (targetNodeId == prevTargetNodeId) { if (Vector3.Distance(lastFrameVehiclePos, vehiclePosOnBezier) >= crazyValue - 1f) { if (!CustomVehicleAI.MayChangeSegment(vehicleId, ref vehicleData, ref lastFrameData, isRecklessDriver, ref prevPos, prevTargetNodeId, prevLaneID, ref position, targetNodeId, laneID, ref nextPosition, nextTargetNodeId, out maxSpeed)) { return; } } } var info2 = netManager.m_segments.m_buffer[position.m_segment].Info; if (info2.m_lanes != null && info2.m_lanes.Length > position.m_lane) { var laneSpeedLimit = SpeedLimitManager.GetLockFreeGameSpeedLimit(position.m_segment, position.m_lane, laneID, info2.m_lanes[position.m_lane]); // info2.m_lanes[position.m_lane].m_speedLimit; #if DEBUG /*if (position.m_segment == 275) { * Log._Debug($"Applying lane speed limit of {laneSpeedLimit} to lane {laneID} @ seg. {position.m_segment}"); * }*/ #endif /*if (TrafficRoadRestrictions.IsSegment(position.m_segment)) { * var restrictionSegment = TrafficRoadRestrictions.GetSegment(position.m_segment); * * if (restrictionSegment.SpeedLimits[position.m_lane] > 0.1f) { * laneSpeedLimit = restrictionSegment.SpeedLimits[position.m_lane]; * } * }*/ maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, laneSpeedLimit, netManager.m_lanes.m_buffer[(int)((UIntPtr)laneID)].m_curve); } else { maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, 1f, 0f); } maxSpeed = CalcMaxSpeed(vehicleId, ref vehicleData, position, pos, maxSpeed, isRecklessDriver); }
public void CustomCalculateSegmentPosition(ushort vehicleId, ref Vehicle vehicleData, PathUnit.Position nextPosition, PathUnit.Position position, uint laneID, byte offset, PathUnit.Position prevPos, uint prevLaneID, byte prevOffset, int index, out Vector3 pos, out Vector3 dir, out float maxSpeed) { if (Options.simAccuracy <= 1) { try { VehicleStateManager.UpdateVehiclePos(vehicleId, ref vehicleData, ref prevPos, ref position); } catch (Exception e) { Log.Error("TramAI CustomCalculateSegmentPosition Error: " + e.ToString()); } } NetManager netManager = Singleton <NetManager> .instance; netManager.m_lanes.m_buffer[(int)((UIntPtr)laneID)].CalculatePositionAndDirection((float)offset * 0.003921569f, out pos, out dir); Vector3 b = netManager.m_lanes.m_buffer[(int)((UIntPtr)prevLaneID)].CalculatePosition((float)prevOffset * 0.003921569f); Vehicle.Frame lastFrameData = vehicleData.GetLastFrameData(); Vector3 a = lastFrameData.m_position; Vector3 a2 = lastFrameData.m_position; Vector3 b2 = lastFrameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f); a += b2; a2 -= b2; float crazyValue = 0.5f * lastFrameData.m_velocity.sqrMagnitude / this.m_info.m_braking; float a3 = Vector3.Distance(a, b); float b3 = Vector3.Distance(a2, b); if (Mathf.Min(a3, b3) >= crazyValue - 1f) { Segment3 segment; segment.a = pos; ushort targetNodeId; ushort nextTargetNodeId; if (offset < position.m_offset) { segment.b = pos + dir.normalized * this.m_info.m_generatedInfo.m_size.z; targetNodeId = netManager.m_segments.m_buffer[(int)position.m_segment].m_startNode; nextTargetNodeId = netManager.m_segments.m_buffer[(int)position.m_segment].m_endNode; } else { segment.b = pos - dir.normalized * this.m_info.m_generatedInfo.m_size.z; targetNodeId = netManager.m_segments.m_buffer[(int)position.m_segment].m_endNode; nextTargetNodeId = netManager.m_segments.m_buffer[(int)position.m_segment].m_startNode; } ushort prevTargetNodeId; if (prevOffset == 0) { prevTargetNodeId = netManager.m_segments.m_buffer[(int)prevPos.m_segment].m_startNode; } else { prevTargetNodeId = netManager.m_segments.m_buffer[(int)prevPos.m_segment].m_endNode; } if (targetNodeId == prevTargetNodeId) { if (!CustomVehicleAI.MayChangeSegment(vehicleId, ref vehicleData, ref lastFrameData, false, ref prevPos, prevTargetNodeId, prevLaneID, ref position, targetNodeId, laneID, ref nextPosition, nextTargetNodeId, out maxSpeed)) { return; } } } NetInfo info = netManager.m_segments.m_buffer[(int)position.m_segment].Info; if (info.m_lanes != null && info.m_lanes.Length > (int)position.m_lane) { //maxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, info.m_lanes[(int)position.m_lane].m_speedLimit, instance.m_lanes.m_buffer[(int)((UIntPtr)laneID)].m_curve); maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, SpeedLimitManager.GetLockFreeGameSpeedLimit(position.m_segment, position.m_lane, laneID, info.m_lanes[position.m_lane]), netManager.m_lanes.m_buffer[laneID].m_curve); } else { maxSpeed = this.CalculateTargetSpeed(vehicleId, ref vehicleData, 1f, 0f); } }
public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) { #if USEPATHWAITCOUNTER VehicleState state = VehicleStateManager._GetVehicleState(vehicleId); #endif if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) { byte pathFindFlags = Singleton <PathManager> .instance.m_pathUnits.m_buffer[(int)((UIntPtr)vehicleData.m_path)].m_pathFindFlags; if ((pathFindFlags & PathUnit.FLAG_READY) != 0) { #if USEPATHWAITCOUNTER state.PathWaitCounter = 0; // NON-STOCK CODE #endif try { this.PathfindSuccess(vehicleId, ref vehicleData); this.PathFindReady(vehicleId, ref vehicleData); VehicleStateManager.OnPathFindReady(vehicleId, ref vehicleData); // NON-STOCK CODE } catch (Exception e) { Log.Warning($"TramBaseAI.PathFindSuccess/PathFindReady({vehicleId}) threw an exception: {e.ToString()}"); vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; Singleton <PathManager> .instance.ReleasePath(vehicleData.m_path); vehicleData.m_path = 0u; this.PathfindFailure(vehicleId, ref vehicleData); return; } } else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0 #if USEPATHWAITCOUNTER || ((pathFindFlags & PathUnit.FLAG_CREATED) != 0 && state.PathWaitCounter == ushort.MaxValue) #endif ) // NON-STOCK CODE { #if USEPATHWAITCOUNTER state.PathWaitCounter = 0; // NON-STOCK CODE #endif vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath; Singleton <PathManager> .instance.ReleasePath(vehicleData.m_path); vehicleData.m_path = 0u; this.PathfindFailure(vehicleId, ref vehicleData); return; } #if USEPATHWAITCOUNTER else { state.PathWaitCounter = (ushort)Math.Min(ushort.MaxValue, (int)state.PathWaitCounter + 1); // NON-STOCK CODE } #endif } else { #if USEPATHWAITCOUNTER state.PathWaitCounter = 0; // NON-STOCK CODE #endif if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) { this.TrySpawn(vehicleId, ref vehicleData); } } /// NON-STOCK CODE START /// bool reversed = (vehicleData.m_flags & Vehicle.Flags.Reversed) != 0; ushort frontVehicleId; if (reversed) { frontVehicleId = vehicleData.GetLastVehicle(vehicleId); } else { frontVehicleId = vehicleId; } try { //Log._Debug($"HandleVehicle for trams. vehicleId={vehicleId} frontVehicleId={frontVehicleId}"); VehicleStateManager.LogTraffic(frontVehicleId, ref Singleton <VehicleManager> .instance.m_vehicles.m_buffer[frontVehicleId], true); } catch (Exception e) { Log.Error("TramAI CustomSimulationStep (1) Error: " + e.ToString()); } try { VehicleStateManager.UpdateVehiclePos(frontVehicleId, ref Singleton <VehicleManager> .instance.m_vehicles.m_buffer[frontVehicleId]); } catch (Exception e) { Log.Error("TramAI CustomSimulationStep (2) Error: " + e.ToString()); } /// NON-STOCK CODE END /// VehicleManager instance = Singleton <VehicleManager> .instance; VehicleInfo info = instance.m_vehicles.m_buffer[(int)vehicleId].Info; info.m_vehicleAI.SimulationStep(vehicleId, ref instance.m_vehicles.m_buffer[(int)vehicleId], vehicleId, ref vehicleData, 0); if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) { return; } ushort trailingVehicle = instance.m_vehicles.m_buffer[(int)vehicleId].m_trailingVehicle; int num = 0; while (trailingVehicle != 0) { info = instance.m_vehicles.m_buffer[(int)trailingVehicle].Info; info.m_vehicleAI.SimulationStep(trailingVehicle, ref instance.m_vehicles.m_buffer[(int)trailingVehicle], vehicleId, ref vehicleData, 0); if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) { return; } trailingVehicle = instance.m_vehicles.m_buffer[(int)trailingVehicle].m_trailingVehicle; if (++num > 16384) { CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace); break; } } if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace | Vehicle.Flags.WaitingCargo)) == 0 || (vehicleData.m_blockCounter == 255 && Options.enableDespawning)) { Singleton <VehicleManager> .instance.ReleaseVehicle(vehicleId); } }