VE Lua Documentation

Press F to search!

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})')