Created
November 2, 2025 13:17
-
-
Save dreasgrech/8ee4ddfaa024535882a56765f8fe6788 to your computer and use it in GitHub Desktop.
A hotfix for Assetto Corsa Realistic Trackday 0.9.1 for older CSP version which didn't yet have ac.getCarMaxSpeedWithGear. Replace file: assettocorsa\apps\lua\AssettoCorsaRealisticTrackday\CarManager.lua
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| local CarManager = {} | |
| -- Andreas: used while still writing the accident system | |
| local DISABLE_ACCIDENTCOLLISION_DETECTION = true | |
| local CAR_SPEEDS_BUFFER_SIZE = 600 | |
| ---@type table<integer,boolean> | |
| CarManager.cars_initialized = {} | |
| ---@type table<integer,number> | |
| CarManager.cars_MAXTOPSPEED = {} -- do not reset this | |
| ---@type table<integer,number> | |
| CarManager.cars_currentSplineOffset = {} | |
| ---@type table<integer,number> | |
| CarManager.cars_targetSplineOffset = {} | |
| ---@type table<integer,number> | |
| CarManager.cars_maxSideMargin = {} | |
| ---@type table<integer,number> | |
| CarManager.cars_currentNormalizedTrackProgress = {} | |
| ---@type table<integer,integer> | |
| CarManager.cars_reasonWhyCantYield_NAME = {} | |
| ---@type table<integer,integer> | |
| CarManager.cars_reasonWhyCantOvertake_NAME = {} | |
| ---@type table<integer,table<integer,integer>> | |
| CarManager.cars_statesExitReason_NAME = {} | |
| -- CarManager.cars_currentTurningLights = {} | |
| -- CarManager.cars_indLeft = {} | |
| -- CarManager.cars_indRight = {} | |
| -- CarManager.cars_indPhase = {} | |
| -- CarManager.cars_hasTL = {} | |
| -- CarManager.cars_evacuating = {} | |
| -- CarManager.cars_anchorPoints = {} | |
| ---@type table<integer,integer> | |
| CarManager.cars_totalSideBlockRaysData = {} -- {[carIndex] = 1, [carIndex] = 0, ...} | |
| ---@type table<integer,table> | |
| CarManager.cars_sideBlockRaysData = {} -- Example: one ray=> {pos,dir,len}. two rays: {pos,dir,len,pos,dir,len} | |
| ---@type table<integer,number> | |
| CarManager.cars_throttleLimit = {} | |
| ---@type table<integer,integer> | |
| CarManager.cars_aiCaution = {} | |
| ---@type table<integer,number> | |
| CarManager.cars_aiTopSpeed = {} | |
| ---@type table<integer,number> | |
| CarManager.cars_aiStopCounter = {} | |
| ---@type table<integer,boolean> | |
| CarManager.cars_gentleStop = {} | |
| ---@type table<integer,integer> | |
| CarManager.cars_currentlyOvertakingCarIndex = {} -- car index of the car we're currently overtaking | |
| ---@type table<integer,integer> | |
| CarManager.cars_currentlyYieldingCarToIndex = {} -- car index of the car we're currently yielding to | |
| ---@type table<integer,number> | |
| CarManager.cars_timeInCurrentState = {} -- time spent in the current state (seconds) | |
| ---@type table<integer,table<integer,number>> | |
| CarManager.cars_speedBuffer = {} | |
| ---@type table<integer,integer> | |
| CarManager.cars_speedBufferIndex = {} | |
| ---@type table<integer,number> | |
| CarManager.cars_speedBufferTotal = {} | |
| ---@type table<integer,number> | |
| CarManager.cars_averageSpeedKmh = {} | |
| ---@type table<integer,number> | |
| CarManager.cars_closingSpeed = {} | |
| ---@type table<integer,number> | |
| CarManager.cars_timeToCollision = {} | |
| -- CarManager.cars_involvedInAccidents = {} | |
| -- CarManager.cars_totalAccidentsInvolvedIn = {} | |
| ---@type table<integer,integer> | |
| CarManager.cars_culpritInAccidentIndex = {} | |
| ---@type table<integer,integer> | |
| CarManager.cars_navigatingAroundAccidentIndex = {} | |
| ---@type table<integer,integer> | |
| CarManager.cars_navigatingAroundCarIndex = {} | |
| ---@type table<integer,boolean> | |
| CarManager.cars_justTeleportedDueToCustomAIFlood = {} | |
| ---@type table<integer,number> | |
| CarManager.cars_grip = {} | |
| ---@type table<integer,vec3> | |
| CarManager.cars_AABBSIZE = {} | |
| ---@type table<integer,vec3> | |
| CarManager.cars_HALF_AABSIZE = {} | |
| ---@type table<integer,ac.StateCar> | |
| CarManager.currentSortedCarsList = {} | |
| ---@type table<number,number> | |
| CarManager.sortedCarList_carIndexToSortedIndex = {} -- [carIndex] = sortedListIndex | |
| -- -- evacuate state so we don’t re-trigger while a car is already evacuating | |
| -- local evacuating = {} | |
| -- -- calculate the max top speeds of each car | |
| -- for i, car in ac.iterateCars() do | |
| -- local carIndex = car.index | |
| -- CarManager.cars_MAXTOPSPEED[carIndex] = CarOperations.calculateMaxTopSpeed(carIndex) | |
| --end | |
| ---@enum CarManager.AICautionValues | |
| ---Holds the different AI Caution levels used in different situations | |
| CarManager.AICautionValues = { | |
| OVERTAKING_WITH_NO_OBSTACLE_INFRONT = 0, | |
| OVERTAKING_WITH_OBSTACLE_INFRONT = 1, | |
| OVERTAKING_WHILE_INCORNER = 2, | |
| YIELDING = 4, | |
| AFTER_ACCIDENT = 16 | |
| } | |
| ---@enum CarManager.GripValues | |
| CarManager.GripValues = { | |
| NORMAL = 1, -- todo: physics.setExtraAIGrip says that the default value is 1 but also says that AI cars have 120% grip | |
| DRIVING_TO_THE_SIDE = 1.3 | |
| } | |
| ---Sets all the default values for a car | |
| ---@param carIndex number | |
| CarManager.setInitializedDefaults = function(carIndex) | |
| CarManager.cars_initialized[carIndex] = true | |
| CarManager.cars_currentSplineOffset[carIndex] = 0 | |
| CarManager.cars_targetSplineOffset[carIndex] = 0 | |
| CarManager.cars_maxSideMargin[carIndex] = 0 | |
| CarManager.cars_currentNormalizedTrackProgress[carIndex] = -1 | |
| CarManager.cars_reasonWhyCantYield_NAME[carIndex] = Strings.StringNames[Strings.StringCategories.ReasonWhyCantYield].None | |
| CarManager.cars_reasonWhyCantOvertake_NAME[carIndex] = Strings.StringNames[Strings.StringCategories.ReasonWhyCantOvertake].None | |
| -- CarManager.cars_currentTurningLights[carIndex] = nil | |
| -- CarManager.cars_indLeft[carIndex] = false | |
| -- CarManager.cars_indRight[carIndex] = false | |
| -- CarManager.cars_indPhase[carIndex] = false | |
| -- CarManager.cars_hasTL[carIndex] = false | |
| -- CarManager.cars_evacuating[carIndex] = false | |
| -- CarManager.cars_anchorPoints[carIndex] = nil | |
| CarManager.cars_totalSideBlockRaysData[carIndex] = 0 | |
| CarManager.cars_sideBlockRaysData[carIndex] = {} -- since this is used as a list, initialize to empty list | |
| CarManager.cars_currentlyOvertakingCarIndex[carIndex] = nil | |
| CarManager.cars_currentlyYieldingCarToIndex[carIndex] = nil | |
| CarManager.cars_timeInCurrentState[carIndex] = 0 | |
| CarManager.cars_statesExitReason_NAME[carIndex] = {} | |
| CarManager.cars_speedBuffer[carIndex] = {} | |
| CarManager.cars_speedBufferIndex[carIndex] = 0 | |
| CarManager.cars_speedBufferTotal[carIndex] = 0 | |
| CarManager.cars_averageSpeedKmh[carIndex] = 0 | |
| CarManager.cars_closingSpeed[carIndex] = 0 | |
| CarManager.cars_timeToCollision[carIndex] = math.huge | |
| -- CarManager.cars_involvedInAccidents[carIndex] = {} | |
| CarManager.cars_culpritInAccidentIndex[carIndex] = 0 | |
| -- CarManager.cars_navigatingAroundAccidentIndex[carIndex] = nil | |
| -- CarManager.cars_navigatingAroundCarIndex[carIndex] = nil | |
| CarManager.sortedCarList_carIndexToSortedIndex[carIndex] = nil | |
| CarManager.cars_justTeleportedDueToCustomAIFlood[carIndex] = false | |
| AccidentManager.setCarNavigatingAroundAccident(carIndex, nil, nil) | |
| CarStateMachine.initializeCarInStateMachine(carIndex) | |
| -- remove speed limitations which could have occured during an accident | |
| CarOperations.resetAIThrottleLimit(carIndex) | |
| CarOperations.removeAITopSpeed(carIndex) | |
| CarOperations.setAIStopCounter(carIndex, 0) | |
| CarOperations.setGentleStop(carIndex, false) | |
| CarOperations.removeAICaution(carIndex) | |
| CarOperations.setDefaultAIGrip(carIndex) | |
| -- reset any pedal positions we may have set | |
| CarOperations.resetPedalPosition(carIndex, CarOperations.CarPedals.Brake) | |
| CarOperations.resetPedalPosition(carIndex, CarOperations.CarPedals.Gas) | |
| CarOperations.resetPedalPosition(carIndex, CarOperations.CarPedals.Clutch ) | |
| local car = ac.getCar(carIndex) | |
| if car then | |
| CarManager.cars_AABBSIZE[carIndex] = car.aabbSize | |
| CarManager.cars_HALF_AABSIZE[carIndex] = car.aabbSize * 0.5 | |
| -- Turn off any turning lights | |
| ac.setTargetCar(carIndex) | |
| ac.setTurningLights(ac.TurningLights.None) | |
| end | |
| end | |
| ---If the car hasn't been initialized yet, initializes it to default values | |
| ---@param carIndex number | |
| function CarManager.ensureDefaults(carIndex) | |
| if CarManager.cars_initialized[carIndex] then | |
| return | |
| end | |
| CarManager.setInitializedDefaults(carIndex) | |
| end | |
| --- returns the calculated spline offset of the car, which is the one we use when easing driving to the side | |
| ---@param carIndex any | |
| ---@return unknown | |
| function CarManager.getCalculatedTrackLateralOffset(carIndex) | |
| return CarManager.cars_currentSplineOffset[carIndex] | |
| end | |
| -- function CarManager.getActualTrackLateralOffset(carIndex) | |
| -- local car = ac.getCar(carIndex) | |
| -- if not car then | |
| -- return 0 | |
| -- end | |
| --- returns the actual spline offset of the car, which may be different from the one set via physics.setAISplineOffset due to physics corrections | |
| ---@param carPosition vec3 | |
| ---@return number | |
| function CarManager.getActualTrackLateralOffset(carPosition) | |
| local carTrackCoordinates = ac.worldCoordinateToTrack(carPosition) | |
| return carTrackCoordinates.x | |
| end | |
| --- returns a boolean value indicating whether the car is on the overtaking lane | |
| ---@param carIndex number | |
| ---@param trackSide any | |
| ---@return boolean | |
| function CarManager.isCarDrivingOnSide(carIndex, trackSide) | |
| local car = ac.getCar(carIndex) | |
| if not car then | |
| return false | |
| end | |
| local carPosition = car.position | |
| local carTrackCoordinatesX = CarManager.getActualTrackLateralOffset(carPosition) | |
| if trackSide == RaceTrackManager.TrackSide.LEFT then | |
| return carTrackCoordinatesX <= -0.1 | |
| end | |
| return carTrackCoordinatesX >= 0.1 | |
| end | |
| ---used in sorting | |
| ---@param carA ac.StateCar | |
| ---@param carB ac.StateCar | |
| ---@return boolean | |
| local function isFirstCarSplinePositionGreater(carA, carB) | |
| return carA.splinePosition > carB.splinePosition | |
| end | |
| -- function CarManager.getCarListSortedByTrackPosition() | |
| -- local sortedCarsList = {} | |
| -- for i, car in ac.iterateCars() do | |
| -- sortedCarsList[#sortedCarsList + 1] = car | |
| -- end | |
| -- -- table.sort(sortedCarsList, function (carA, carB) | |
| -- -- return carA.splinePosition > carB.splinePosition | |
| -- -- end) | |
| -- table.sort(sortedCarsList, isFirstCarSplinePositionGreater) | |
| -- return sortedCarsList | |
| -- end | |
| ---Sorts the given car list by track position, with the car furthest ahead first | |
| ---@param carList table<integer,ac.StateCar> | |
| ---@return table<integer,ac.StateCar> carList | |
| function CarManager.sortCarListByTrackPosition(carList) | |
| table.sort(carList, isFirstCarSplinePositionGreater) | |
| return carList | |
| end | |
| ---Returns a boolean value indicating whether the car is mid-corner and the distance to the upcoming turn (0 if mid-corner) | |
| ---@param carIndex number | |
| ---@return boolean | |
| ---@return number | |
| function CarManager.isCarMidCorner(carIndex) | |
| local trackUpcomingTurn = ac.getTrackUpcomingTurn(carIndex) | |
| local distanceToUpcomingTurn = trackUpcomingTurn.x | |
| -- local turnAngle = trackUpcomingTurn.y | |
| local isMidCorner = distanceToUpcomingTurn == 0 | |
| return isMidCorner, distanceToUpcomingTurn | |
| end | |
| -- ---Returns a boolean value indicating whether the car is off track (more than 1.5 lanes away from center) | |
| -- ---@param carIndex number | |
| -- ---@return boolean | |
| -- function CarManager.isCarOffTrack(carIndex) | |
| -- local car = ac.getCar(carIndex) | |
| -- if not car then | |
| -- return false | |
| -- end | |
| -- local carActualTrackLateralOffset = CarManager.getActualTrackLateralOffset(car.position) | |
| -- return math.abs(carActualTrackLateralOffset) > 1.5 | |
| -- end | |
| ---The indices of the car wheels as used in car.wheels[] | |
| ---Andreas: I found these indexes by drawing a debugBox at each wheel position with different colors | |
| ---@enum CarManager.CAR_WHEELS_INDEX | |
| CarManager.CAR_WHEELS_INDEX = { | |
| FRONT_LEFT = 0, | |
| FRONT_RIGHT = 1, | |
| REAR_LEFT = 2, | |
| REAR_RIGHT = 3 | |
| } | |
| ---Returns a boolean value indicating whether the car is off track depending on the given side (if any wheel on that side is off track) | |
| ---@param car ac.StateCar | |
| ---@param side RaceTrackManager.TrackSide | |
| ---@return boolean | |
| function CarManager.isCarOffTrack(car, side) | |
| if not car then | |
| return false | |
| end | |
| local carWheels = car.wheels | |
| -- Andreas: the following loop assumes that the wheel indices are always 0,1,2,3 for FL,FR,RL,RR | |
| -- First I determine which index to start the loop from (FRONT_LEFT or FRONT_RIGHT) | |
| -- then the loop iterates over both wheels on the given side by incrementing by 2 to skip the tyre in between | |
| local loopStart | |
| if side == RaceTrackManager.TrackSide.LEFT then | |
| loopStart = CarManager.CAR_WHEELS_INDEX.FRONT_LEFT | |
| else | |
| loopStart = CarManager.CAR_WHEELS_INDEX.FRONT_RIGHT | |
| end | |
| -- this loop won't run more than twice since there are only two wheels on each side | |
| local loopEnd = loopStart + 2 | |
| for i = loopStart, loopEnd, 2 do | |
| local wheel = carWheels[i] | |
| if wheel then -- Andreas: is this wheel check neccessary? can there be vehicles with less than 4 wheels? | |
| local wheelOffTrack = not wheel.surfaceValidTrack | |
| if wheelOffTrack then | |
| -- todo: should we check both wheels on the side or just one? | |
| return true | |
| end | |
| end | |
| end | |
| return false | |
| -- for i = 0, 1 do | |
| -- local wheelIndex | |
| -- if side == RaceTrackManager.TrackSide.LEFT then | |
| -- wheelIndex = i * 2 -- 0,2 | |
| -- else | |
| -- wheelIndex = i * 2 + 1 -- 1,3 | |
| -- end | |
| -- local wheel = carWheels[wheelIndex] | |
| -- end | |
| end | |
| ---comment | |
| ---@param car ac.StateCar | |
| function CarManager.saveCarSpeed(car) | |
| local carIndex = car.index | |
| local currentSpeedKmh = car.speedKmh | |
| -- don't record the speed if the car is barely moving or stopped | |
| local carStopped = currentSpeedKmh < 1 | |
| if carStopped then | |
| return | |
| end | |
| local currentSpeedBufferIndex = CarManager.cars_speedBufferIndex[carIndex] | |
| local speedThatWillBeReplaced = CarManager.cars_speedBuffer[carIndex][currentSpeedBufferIndex] or 0 | |
| local speedBufferTotal = CarManager.cars_speedBufferTotal[carIndex] | |
| speedBufferTotal = speedBufferTotal - speedThatWillBeReplaced + currentSpeedKmh | |
| CarManager.cars_speedBufferTotal[carIndex] = speedBufferTotal | |
| CarManager.cars_speedBuffer[carIndex][currentSpeedBufferIndex] = currentSpeedKmh | |
| CarManager.cars_speedBufferIndex[carIndex] = (currentSpeedBufferIndex + 1) % CAR_SPEEDS_BUFFER_SIZE | |
| CarManager.cars_averageSpeedKmh[carIndex] = speedBufferTotal / CAR_SPEEDS_BUFFER_SIZE | |
| -- log the entire speed buffer | |
| -- Logger.log(string.format("Car %d speed buffer: %s, average: %.2f", carIndex, table.concat(CarManager.cars_speedBuffer[carIndex], ", "), CarManager.cars_averageSpeedKmh[carIndex] or 0)) | |
| end | |
| ---Returns the closing speed (m/s) and time to collision (s) between two cars | |
| ---If the closing speed is negative, then the cars are moving apart (not closing) | |
| ---@param car ac.StateCar | |
| ---@param carFront ac.StateCar | |
| ---@return number closingSpeed_ms | |
| ---@return number TTC | |
| CarManager.getClosingSpeed = function(car, carFront) | |
| if not car or not carFront then return -1,-1 end | |
| -- positions | |
| local deltaPositions = carFront.position - car.position | |
| local distanceBetweenPositions = deltaPositions:length() | |
| if distanceBetweenPositions < 1e-3 then return -1, -1 end | |
| local rhat = deltaPositions / distanceBetweenPositions | |
| -- closing speed in m/s (positive = closing, negative = opening) | |
| local deltaVelocities = carFront.velocity - car.velocity | |
| local closingSpeed_ms = -math.dot(deltaVelocities, rhat) | |
| -- -- (optional) smooth a bit to reduce noise | |
| -- local previousClosingSpeed = CarManager.cars_closingSpeed[car.index] or 0 | |
| -- local alpha = const(0.2) | |
| -- closing_ms = previousClosingSpeed and (previousClosingSpeed*(1-alpha) + closing_ms*alpha) or closing_ms | |
| -- time-to-collision if still closing and paths roughly align | |
| local timeToCollision = (closingSpeed_ms > 0.1) and (distanceBetweenPositions / closingSpeed_ms) or math.huge | |
| -- local carBehindIndex = car.index | |
| -- CarManager.cars_closingSpeed[carBehindIndex] = closing_ms | |
| -- CarManager.cars_timeToCollision[carBehindIndex] = TTC | |
| return closingSpeed_ms, timeToCollision | |
| end | |
| -- -- Utility: compute world right-vector at a given progress on the AI spline | |
| -- local function trackRightAt(progress) | |
| -- -- sample two points along the spline to get forward dir | |
| -- local p0 = ac.trackProgressToWorldCoordinate(progress) | |
| -- local p1 = ac.trackProgressToWorldCoordinate((progress + 0.0008) % 1.0) | |
| -- local fwd = (p1 - p0):normalize() | |
| -- -- Y-up world, so right = up × fwd | |
| -- local up = vec3(0,1,0) | |
| -- local right = up:cross(fwd):normalize() | |
| -- return right | |
| -- end | |
| -- -- Physics shove for ~1.2 s (applied at physics rate) to push the car onto grass | |
| -- local function shoveCarSideways(carIndex, towardsRight, strengthN, seconds) | |
| -- CarManager.cars_reason[carIndex] = "starting to shove car sideways to evacuate" | |
| -- -- optional safety bubble: disable car-car collisions during the shove | |
| -- physics.disableCarCollisions(carIndex, true) -- re-enable later | |
| -- -- cap pace while evacuating | |
| -- physics.setAITopSpeed(carIndex, 15) -- 15 km/h crawl | |
| -- CarOperations.setAIThrottleLimit(carIndex, 0.25) | |
| -- -- Let tyres-out be OK globally in modes; prevents penalties while off track (optional) | |
| -- physics.setAllowedTyresOut(-1) | |
| -- -- launch short physics worker to add lateral force each physics tick | |
| -- local startedAt = os.clock() | |
| -- physics.startPhysicsWorker([[ | |
| -- local idx, dirSign, forceN = __input.idx, __input.sign, __input.forceN | |
| -- function script.update(dt) | |
| -- -- grab current progress and compute world right | |
| -- local car = ac.getCar(idx) | |
| -- if not car then return end | |
| -- local prog = math.max(0, math.min(1, ac.worldCoordinateToTrack(car.position).z)) | |
| -- local p0 = ac.trackProgressToWorldCoordinate(prog) | |
| -- local p1 = ac.trackProgressToWorldCoordinate((prog + 0.001) % 1.0) | |
| -- local fwd = (p1 - p0):normalize() | |
| -- local right= vec3(0,1,0):cross(fwd):normalize() | |
| -- local sideways = right * dirSign | |
| -- -- apply a gentle, ground-level push near CG (world coords) | |
| -- local applyPos = car.position + vec3(0, 0.2, 0) | |
| -- physics.addForce(idx, applyPos, false, sideways * forceN * dt, false, -1) | |
| -- ac.log("lateral log") | |
| -- end | |
| -- ]], { idx = carIndex, sign = (towardsRight and 1 or -1), forceN = strengthN }, function(err) end) | |
| -- -- CarManager.cars_reason[carIndex] = "applying lateral shove to evacuate in loop" | |
| -- -- stop the shove and restore things later | |
| -- setTimeout(function() | |
| -- CarManager.cars_reason[carIndex] = "Stopping shove to restore things later" | |
| -- physics.disableCarCollisions(carIndex, false) -- restore collisions | |
| -- physics.setAITopSpeed(carIndex, math.huge) | |
| -- CarOperations.setAIThrottleLimit(carIndex, 1) | |
| -- end, seconds or 1.2) | |
| -- end | |
| --- -- Monitor collisions | |
| --- ac.onCarCollision(-1, function (carIndex) | |
| --- if DISABLE_ACCIDENTCOLLISION_DETECTION then return end | |
| --- local car = ac.getCar(carIndex) | |
| --- if not car or CarManager.cars_evacuating[carIndex] then return end | |
| --- -- hazard lights | |
| --- ac.setTargetCar(carIndex) | |
| --- if car.hasTurningLights then | |
| --- ac.setTurningLights(ac.TurningLights.Hazards) | |
| --- end | |
| --- -- pick nearer side and immediately bias AI to that edge (cheap hint) | |
| --- local tcoords = ac.worldCoordinateToTrack(car.position) -- X∈[-1..1], Z∈[0..1] | |
| --- local prog = tcoords.z | |
| --- local sides = ac.getTrackAISplineSides(prog) -- vec2(leftDistM, rightDistM) | |
| --- local goRight = (sides.y <= sides.x) | |
| --- local edgeOffsetNorm = goRight and 0.98 or -0.98 -- hug the boundary | |
| --- physics.setAISplineOffset(carIndex, edgeOffsetNorm, true) -- override AI awareness | |
| --- -- brief settle, then push onto grass if still near racing line | |
| --- CarManager.cars_evacuating[carIndex] = true | |
| --- physics.setAIStopCounter(carIndex, 0.4) -- momentary pause | |
| --- physics.setGentleStop(carIndex, true) -- smooth decel | |
| --- CarManager.cars_reason[carIndex] = ("Just collided. Evacuating %s side at spline=%.3f") | |
| --- :format(goRight and "RIGHT" or "LEFT", car.splinePosition) | |
| --- CarManager.cars_currentlyYielding[carIndex] = false | |
| --- setTimeout(function() | |
| --- physics.setAIStopCounter(carIndex, 0) | |
| --- physics.setGentleStop(carIndex, false) | |
| --- CarManager.cars_reason[carIndex] = "Starting to shove car off track to the " .. (goRight and "RIGHT" or "LEFT") | |
| --- -- crawl and steer bias remain; now physically nudge off the tarmac | |
| --- shoveCarSideways(carIndex, goRight, 9000, 1.2) -- ~9 kN lateral shove ~1.2 s | |
| --- -- if you want them to then head to pits once clear: | |
| --- -- physics.setAIPitStopRequest(carIndex, true) -- optional | |
| --- Logger.log(("Car #%d (%s) evacuating %s side at spline=%.3f") :format(carIndex, car.name, goRight and "RIGHT" or "LEFT", car.splinePosition)) | |
| --- --[=====[ | |
| --- -- after a few seconds, clear state & lights so AI can recover | |
| --- setTimeout(function() | |
| --- physics.setAISplineOffset(carIndex, 0, true) | |
| --- physics.setAITopSpeed(carIndex, math.huge) | |
| --- CarOperations.setAIThrottleLimit(carIndex, 1) | |
| --- if car.hasTurningLights then ac.setTurningLights(ac.TurningLights.None) end | |
| --- CarManager.cars_evacuating[carIndex] = nil | |
| --- end, 6.0) | |
| --- --]=====] | |
| --- end, 0.6) | |
| --- end) | |
| return CarManager |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment