driveUsingPath
Definition
-- @/lua/vehicle/ai.lua:6702
local function driveUsingPath(arg)
--[[ At least one argument of either path or wpTargetList or script must be specified. All other arguments are optional.
* path: A sequence of waypoint names that form a path by themselves to be followed in the order provided.
* wpTargetList: A sequence of waypoint names to be used as succesive targets ex. wpTargetList = {'wp1', 'wp2'}.
Between any two consequitive waypoints a shortest path route will be followed.
* script: A sequence of positions from a user-defined trajectory. For each node/position we have
* mandatory properties:
* x, y, z global coordinates of the corresponding node
* optional properties:
* r, the width of the path built using the scripted trajectory at the corresponding node
* vl, roadSpeedLimit for the corresponding node. If this value is specified and routeSpeedMode is set to 'legal', vl will act as an upper bound for the speed profile
* v, speed value for the corresponding node. If this value is specified the ai is forced to reach it at the corresponding node, skipping awareness and routeSpeedLimit (if they are enabled)
-- Optional Arguments --
* wpSpeeds: Type: (key/value pairs, key: "node_name", value: speed, number in m/s)
Define target speeds for individual waypoints. The ai will try to meet this speed when at the given waypoint.
* noOfLaps: Type: number. Default value: nil
The number of laps if the path is a loop. If not defined, the ai will just follow the succesion of waypoints once.
* routeSpeed: A speed in m/s. To be used in tandem with "routeSpeedMode".
Type: number
* routeSpeedMode: Values: 'limit': the ai will not go above the 'routeSpeed' defined by routeSpeed.
'set': the ai will try to always go at the speed defined by "routeSpeed".
* driveInLane: Values: 'on' (anything else is considered off/inactive)
When 'on' the ai will keep on the correct side of the road on two way roads.
This also affects pathFinding in that when this option is active ai paths will traverse roads in the legal direction if posibble.
Default: inactive
* aggression: Value: 0.3 - 1. The aggression value with which the ai will drive the route.
At 1 the ai will drive at the limit of traction. A value of 0.3 would be considered normal every day driving, going shopping etc.
Default: 0.3
* avoidCars: Values: 'on' / 'off'. When 'on' the ai will be aware of (avoid crashing into) other vehicles on the map. Default is 'off'
* examples:
ai.driveUsingPath{ wpTargetList = {'wp1', 'wp10'}, driveInLane = 'on', avoidCars = 'on', routeSpeed = 35, routeSpeedMode = 'limit', wpSpeeds = {wp1 = 10, wp2 = 40}, aggression = 0.3}
In the above example the speeds set for wp1 and wp2 will take precedence over "routeSpeed" for the specified nodes.
--]]
if (arg.wpTargetList == nil and arg.path == nil and arg.script == nil) or
(type(arg.wpTargetList) ~= 'table' and type(arg.path) ~= 'table' and type(arg.script) ~= 'table') or
(arg.wpSpeeds ~= nil and type(arg.wpSpeeds) ~= 'table') or
(arg.noOfLaps ~= nil and type(arg.noOfLaps) ~= 'number') or
(arg.routeSpeed ~= nil and type(arg.routeSpeed) ~= 'number') or
(arg.routeSpeedMode ~= nil and type(arg.routeSpeedMode) ~= 'string') or
(arg.driveInLane ~= nil and type(arg.driveInLane) ~= 'string') or
(arg.aggression ~= nil and type(arg.aggression) ~= 'number')
then
return
end
if arg.script then
-- Set vehicle position and orientation at the start of the path
-- Get initial position and orientation of vehicle at start of path (possibly time offset and/or time delayed)
local script = arg.script
local dir, up, pos
if script[1].dir then
-- vehicle initial orientation vectors exist
dir = vec3(script[1].dir)
up = vec3(script[1].up or mapmgr.surfaceNormalBelow(vec3(script[1])))
local frontPosRelOrig = obj:getOriginalFrontPositionRelative() -- original relative front position in the vehicle coordinate system (left, back, up)
local vx = dir * -frontPosRelOrig.y
local vz = up * frontPosRelOrig.z
local vy = dir:cross(up) * -frontPosRelOrig.x
pos = vec3(script[1]) - vx - vz - vy
local dH = require('scriptai').wheelToGroundDist(pos, dir, up)
pos:setAdd(dH * up)
else
-- vehicle initial orientation vectors don't exist
-- estimate vehicle orientation vectors from path and ground normal
local p1 = vec3(script[1])
local p1z0 = p1:z0()
local scriptPosi = vec3()
local k
for i = 2, #script do
scriptPosi:set(script[i].x, script[i].y, 0)
if p1z0:squaredDistance(scriptPosi) > 0.2 * 0.2 then
k = i
break
end
end
if k then
local p2 = vec3(script[k])
dir = p2 - p1; dir:normalize()
up = mapmgr.surfaceNormalBelow(p1)
local frontPosRelOrig = obj:getOriginalFrontPositionRelative() -- original relative front position in the vehicle coordinate system (left, back, up)
local vx = dir * -frontPosRelOrig.y
local vz = up * frontPosRelOrig.z
local vy = dir:cross(up) * -frontPosRelOrig.x
pos = p1 - vx - vz - vy
local dH = require('scriptai').wheelToGroundDist(pos, dir, up)
pos:setAdd(dH * up)
end
end
if dir then
local rot = quatFromDir(dir:cross(up):cross(up), up)
obj:queueGameEngineLua(
"getObjectByID(" .. objectId .. "):resetBrokenFlexMesh();" ..
"vehicleSetPositionRotation(" .. objectId .. "," .. pos.x .. "," .. pos.y .. "," .. pos.z .. "," .. rot.x .. "," .. rot.y .. "," .. rot.z .. "," .. rot.w .. ")"
)
mapmgr.setCustomMap() -- nils mapmgr.mapData
M.mode = 'manual'
stateChanged()
noOfLaps = max(arg.noOfLaps or 1, 1)
local pathMap = require('graphpath').newGraphpath() -- create a dummy graph
local scrCount = #arg.script
if noOfLaps > 1 and arg.script[scrCount].x == arg.script[1].x and arg.script[scrCount].y == arg.script[1].y and arg.script[scrCount].z == arg.script[1].z then
loopPath = true
end
local path = table.new(scrCount, 0)
local speedProfile = table.new(0, scrCount)
local radius = obj:getInitialWidth()
-- set the graph node positions and widths and create the path array and speed profile data
for i = 1, scrCount - (loopPath and 1 or 0) do -- avoid adding the last point if the scripts loops
local node = 'wp_'..tostring(i)
pathMap:setPointPositionRadius(node, vec3(arg.script[i].x, arg.script[i].y, arg.script[i].z), arg.script[i].r or radius)
path[i] = node
speedProfile[node] = arg.script[i].v -- TODO: This is a problem with a looping path for first/last node speed setting
end
if loopPath then
table.insert(path, path[1])
end
-- add the nodes and edges to the dummy graph
for i = 1, scrCount - 1 do
local n1id, n2id = path[i], path[i+1]
pathMap:uniEdge(n1id, n2id, pathMap.positions[n1id]:distance(pathMap.positions[n2id]), 1, arg.script[i].vl or 100, nil, false)
end
scriptData = deepcopy(arg)
scriptData.mapData = pathMap
scriptData.path = path
scriptData.speedProfile = speedProfile
scriptData.noOfLaps = noOfLaps
scriptData.loopPath = loopPath
end
else
setState({mode = 'manual'})
setParameters({ -- setParameters calls tableMerge so the nil guards are not really needed
driveStyle = arg.driveStyle or 'default',
staticFrictionCoefMult = arg.staticFrictionCoefMult,
lookAheadKv = max(0.1, arg.lookAheadKv or parameters.lookAheadKv),
understeerThrottleControl = arg.understeerThrottleControl,
oversteerThrottleControl = arg.oversteerThrottleControl,
throttleTcs = arg.throttleTcs,
abBrakeControl = arg.abBrakeControl,
underSteerBrakeControl = arg.underSteerBrakeControl,
throttleKp = arg.throttleKp,
targetSpeedSmootherRate = arg.targetSpeedSmootherRate,
turnForceCoef = arg.turnForceCoef,
springForceIntegratorDispLim = arg.springForceIntegratorDispLim
})
noOfLaps = max(arg.noOfLaps or 1, 1)
wpList = arg.wpTargetList
manualPath = arg.path
validateInput = validateUserInput
opt.avoidCars = arg.avoidCars or 'off'
if noOfLaps > 1 and ((wpList and wpList[2] and wpList[1] == wpList[#wpList]) or (manualPath and manualPath[2] and manualPath[1] == manualPath[#manualPath])) then
loopPath = true
end
speedProfile = arg.wpSpeeds or {}
setSpeed(arg.routeSpeed)
setSpeedMode(arg.routeSpeedMode)
driveInLane(arg.driveInLane)
setAggressionExternal(arg.aggression)
stateChanged()
end
end
Callers
@/lua/ge/extensions/gameplay/skidpadTest.lua
-- Make the AI drive the route
vehicle:queueLuaCommand('ai.driveUsingPath(' .. serialize(config) .. ')')
return true
@/gameplay/missionTypes/chase/customNodes/suspectAiNode.lua
if race.aiPath and race.aiPath[1] then
veh:queueLuaCommand('ai.driveUsingPath({wpTargetList = '..serialize(race.aiPath)..'})')
end
@/lua/ge/extensions/gameplay/garageMode.lua
core_vehicleBridge.executeAction(playerVeh, 'setFreeze', false)
playerVeh:queueLuaCommand('ai.driveUsingPath({wpTargetList = {"garageExit"}})')
job.sleep(2)
@/lua/ge/extensions/scenario/scenariohelper.lua
local function setAiRoute(vehicleName, waypoints)
queueLuaCommandByName(vehicleName, 'ai.driveUsingPath({wpTargetList = '..serialize(waypoints)..'})')
if scenario_scenarios then
local resetLearning = arg.resetLearning and 'true' or 'false'
queueLuaCommandByName(vehicleName, 'ai.driveUsingPath({wpTargetList = '..serialize(waypoints)..', routeSpeed = '..routeSpeed..', routeSpeedMode = "'..routeSpeedMode..'", driveInLane = "'..driveInLane..'", wpSpeeds = '..serialize(speeds)..', noOfLaps = '..lapCount..', aggression = '..aggression..', aggressionMode = "'..aggressionMode..'", resetLearning = '..resetLearning..', avoidCars = "'..tostring(avoidCars)..'"})')
@/lua/vehicle/ai.lua
if script[1] and script[1].v then
driveUsingPath({script = script})
else
@/lua/vehicle/extensions/tech/techCore.lua
}
ai.driveUsingPath(arg)
ai.stateChanged()
args.avoidCars = request['avoidCars'] == true and 'on' or 'off'
ai.driveUsingPath(args)
request:sendACK('DriveUsingPath')
@/lua/ge/extensions/career/modules/vehiclePerformance.lua
-- Make the AI drive the route
vehicle:queueLuaCommand('ai.driveUsingPath(' .. serialize(config) .. ')')
core_camera.setByName(0, 'orbit')
@/lua/ge/extensions/gameplay/taxi.lua
plVeh:queueLuaCommand('ai.driveUsingPath('..routeStr..')')
@/lua/ge/extensions/gameplay/race/race.lua
getObjectByID(id):queueLuaCommand('controller.setFreeze(false)')
getObjectByID(id):queueLuaCommand('ai.driveUsingPath('..str..')') -- this should ignore one-way roads
end
@/lua/ge/extensions/editor/aiTests.lua
obj:queueLuaCommand("ai.driveUsingPath({wpTargetList = "..serialize(path)..", avoidCars = 'off'})")
obj:queueLuaCommand("ai.setAggression("..aiParams.aggression..")")
@/lua/ge/extensions/core/hotlapping.lua
veh:queueLuaCommand('ai.driveUsingPath({wpTargetList = '..serialize(route)..', noOfLaps = 1000, avoidCars = "on"})')
veh:queueLuaCommand('ai.setParameters({turnForceCoef = 4, awarenessForceCoef = 0.15})') -- slightly improves racing
@/lua/ge/extensions/core/quickAccess.lua
affectedVehicleCount = affectedVehicleCount + 1
veh:queueLuaCommand('ai.driveUsingPath('..str..')')
::continue::
local veh = be:getObject(i)
veh:queueLuaCommand('ai.driveUsingPath('..str..')')
veh:queueLuaCommand('ai.setRacing(true)')
@/lua/ge/extensions/flowgraph/nodes/vehicle/ai/followWaypoints.lua
veh:queueLuaCommand('ai.driveUsingPath({wpTargetList = ' .. serialize(aiPath) .. ', wpSpeeds = ' .. serialize(wpSpeeds) .. ', noOfLaps = ' .. self.lapCount .. ', aggression = 1})')