mirror of
https://github.com/jimathy/jim_bridge.git
synced 2026-11-10 18:33:55 +00:00
1486 lines
58 KiB
Lua
1486 lines
58 KiB
Lua
--[[
|
|
Ped Task & AI Control Module
|
|
--------------------------------
|
|
Wraps the GTA V "Task*" natives so scripted peds can be given orders
|
|
without having to remember each native's argument list.
|
|
|
|
Covers:
|
|
• Movement - goTo, goToEntity, follow, wander, flee, face
|
|
• Combat - combat, melee, shootAt, aimAt, accuracy, firingPattern
|
|
• Vehicles - enterVehicle, leaveVehicle, openDoor, closeDoor, driveTo, park, chaseVehicle
|
|
• Animation - playAnim, scenario, lookAt, ragdoll, standStill
|
|
• Behaviour - combat/flee attributes, combat range & movement, ped state, config flags
|
|
• Tracking - status, isRunning, cancel, clear, wait, active
|
|
|
|
Design mirrors 'make/ropeControl.lua':
|
|
• Every task is registered against its ped so it can be queried and cancelled.
|
|
• Finite tasks accept an `onComplete` callback and an optional `timeout`.
|
|
• All functions return `false, "reason"` when they cannot run.
|
|
• Inputs accept an entity handle or a vector3/vector4/table of coordinates.
|
|
|
|
All natives used here were verified against the FiveM native reference.
|
|
GTA has no close-door task and no "sit still" task, so those use the
|
|
closest available natives instead.
|
|
|
|
Usage:
|
|
local ped = makePed({model = `a_m_y_business_03`}, vec4(450.0, -980.0, 30.0, 100.0))
|
|
pedTaskGoTo(ped, vec3(455.0, -975.0, 30.0), {
|
|
run = true,
|
|
onComplete = function(p) print("arrived") end,
|
|
})
|
|
]]
|
|
|
|
if not isServer() then
|
|
-------------------------------------------------------------
|
|
-- State
|
|
-------------------------------------------------------------
|
|
|
|
-- Tracks the tasks currently assigned to each ped.
|
|
-- PedTasks[ped] = { current = "goTo", tasks = { ["goTo"] = true } }
|
|
local PedTasks = {}
|
|
-- Generation counter per ped/task, used to invalidate stale completion watchers.
|
|
local TaskGen = {}
|
|
|
|
-------------------------------------------------------------
|
|
-- Native constants
|
|
-------------------------------------------------------------
|
|
|
|
-- SET_PED_COMBAT_ATTRIBUTES takes a single attribute index, not a bitmask.
|
|
-- Indexes taken from the game's eCombatAttribute enum.
|
|
local combatAttributes = {
|
|
useCover = 0,
|
|
useVehicle = 1,
|
|
doDrivebys = 2,
|
|
leaveVehicles = 3,
|
|
canUseDynamicStrafeDecisions = 4,
|
|
alwaysFight = 5,
|
|
fleeWhilstInVehicle = 6,
|
|
justFollowVehicle = 7,
|
|
playReactionAnims = 8,
|
|
willScanForDeadPeds = 9,
|
|
isAGuard = 10,
|
|
justSeekCover = 11,
|
|
blindFireInCover = 12,
|
|
aggressive = 13,
|
|
canInvestigate = 14,
|
|
canUseRadio = 15,
|
|
canCaptureEnemyPeds = 16,
|
|
alwaysFlee = 17,
|
|
canTauntInVehicle = 20,
|
|
canChaseTargetOnFoot = 21,
|
|
willDragInjuredPedsToSafety = 22,
|
|
requiresLosToShoot = 23,
|
|
useProximityFiringRate = 24,
|
|
disableSecondaryTarget = 25,
|
|
disableEntryReactions = 26,
|
|
perfectAccuracy = 27,
|
|
canUseFrustratedAdvance = 28,
|
|
moveToLocationBeforeCoverSearch = 29,
|
|
canShootWithoutLos = 30,
|
|
maintainMinDistanceToTarget = 31,
|
|
canUsePeekingVariations = 34,
|
|
disablePinnedDown = 35,
|
|
disablePinDownOthers = 36,
|
|
openCombatWhenDefensiveAreaReached = 37,
|
|
disableBulletReactions = 38,
|
|
canBust = 39,
|
|
ignoredByOtherPedsWhenWanted = 40,
|
|
canCommandeerVehicles = 41,
|
|
canFlank = 42,
|
|
switchToAdvanceIfCantFindCover = 43,
|
|
switchToDefensiveIfInCover = 44,
|
|
clearPrimaryDefensiveAreaReached = 45,
|
|
canFightArmedPedsWhenNotArmed = 46,
|
|
enableTacticalPointsWhenDefensive = 47,
|
|
disableCoverArcAdjustments = 48,
|
|
useEnemyAccuracyScaling = 49,
|
|
canCharge = 50,
|
|
removeAreaSetWillAdvanceWhenReached = 51,
|
|
useVehicleAttack = 52,
|
|
useVehicleAttackIfHasMountedGuns = 53,
|
|
alwaysEquipBestWeapon = 54,
|
|
canSeeUnderwaterPeds = 55,
|
|
disableAimAtAiTargetsInHelis = 56,
|
|
disableSeekDueToLineOfSight = 57,
|
|
disableFleeFromCombat = 58,
|
|
disableTargetChangesDuringPursuit = 59,
|
|
canThrowSmokeGrenade = 60,
|
|
clearAreaSetDefensiveIfUnreachable = 62,
|
|
disableBlockFromPursueDuringChase = 64,
|
|
disableSpinOutDuringVehicleChase = 65,
|
|
disableCruiseInFrontDuringChase = 66,
|
|
canIgnoreBlockedLosWeighting = 67,
|
|
disableReactToBuddyShot = 68,
|
|
preferNavmeshDuringVehicleChase = 69,
|
|
allowedToAvoidOffroadDuringChase = 70,
|
|
permitChargeBeyondDefensiveArea = 71,
|
|
useRocketsAgainstVehiclesOnly = 72,
|
|
disableTacticalPointsWithoutLos = 73,
|
|
disablePullAlongsideDuringChase = 74,
|
|
disableAllRandomsFlee = 78,
|
|
generateDeadPedSeenScriptEvents = 79,
|
|
useMaxSenseRangeOnEvents = 80,
|
|
restrictInVehicleAimingToSide = 81,
|
|
useDefaultBlockedLosPosition = 82,
|
|
requiresLosToAim = 83,
|
|
canCruiseAndBlockInVehicle = 84,
|
|
preferAirCombatWhenInAircraft = 85,
|
|
allowDogFighting = 86,
|
|
preferNonAircraftTargets = 87,
|
|
preferKnownTargetsWhenClosest = 88,
|
|
forceCheckAttackAngleForMountedGun = 89,
|
|
blockFireForPassengerMountedGuns = 90,
|
|
}
|
|
|
|
-- SET_PED_COMBAT_RANGE values
|
|
local combatRanges = { melee = 0, preferred = 1, far = 2 }
|
|
-- SET_PED_COMBAT_MOVEMENT values
|
|
local combatMovements = { hold = 0, strafe = 1, advance = 2 }
|
|
|
|
-- Vehicle seat indexes, these also match the door indexes used by the door natives
|
|
local vehicleSeats = {
|
|
any = -1,
|
|
driver = 0,
|
|
passenger = 1,
|
|
rearLeft = 2,
|
|
rearRight = 3,
|
|
}
|
|
|
|
-- TASK_PLAY_ANIM flags
|
|
local animFlags = {
|
|
looping = 1,
|
|
holdLastFrame = 2,
|
|
repositionWhenFinished = 4,
|
|
notInterruptable = 8,
|
|
upperBody = 16,
|
|
}
|
|
|
|
-- TASK_ENTER_VEHICLE flags
|
|
local enterVehicleFlags = {
|
|
normal = 1,
|
|
teleportToVehicle = 3,
|
|
carjackFromSeat = 8,
|
|
teleportDirectly = 16,
|
|
}
|
|
|
|
-- TASK_LEAVE_VEHICLE flags
|
|
local leaveVehicleFlags = {
|
|
normal = 0,
|
|
normalSlow = 64,
|
|
keepDoorOpen = 256,
|
|
teleportOutside = 16,
|
|
throwOutOfVehicle = 4160,
|
|
moveToPassengerFirst = 262144,
|
|
}
|
|
|
|
-- TASK_VEHICLE_PARK modes
|
|
local parkModes = { ignoreHeading = 0, forward = 1, reverse = 2 }
|
|
|
|
-- The task index used by GetIsTaskActive to report a wandering ped
|
|
local TASK_INDEX_WANDER = 222
|
|
|
|
-------------------------------------------------------------
|
|
-- Internal helpers
|
|
-------------------------------------------------------------
|
|
|
|
--- Checks the ped handle is a living, existing entity.
|
|
--- @param ped number The ped handle to test.
|
|
--- @return boolean True if the ped can be given a task.
|
|
local function isValidPed(ped)
|
|
return type(ped) == "number" and ped ~= 0 and DoesEntityExist(ped) and not IsEntityDead(ped)
|
|
end
|
|
|
|
--- Normalises a coordinate input into a vector3.
|
|
--- @param target number|vector3|vector4|table An entity handle or a set of coordinates.
|
|
--- @return vector3|nil The resolved coordinates, or nil if they could not be resolved.
|
|
local function resolveCoords(target)
|
|
if type(target) == "number" then
|
|
if not DoesEntityExist(target) then return nil end
|
|
return GetEntityCoords(target, false)
|
|
end
|
|
if type(target) == "vector3" then return target end
|
|
if type(target) == "vector4" then return target.xyz end
|
|
if type(target) == "table" and target.x and target.y and target.z then
|
|
return vec3(target.x, target.y, target.z)
|
|
end
|
|
return nil
|
|
end
|
|
|
|
--- Resolves a seat or door index from a name or number.
|
|
--- @param seat string|number The seat name ("driver") or index.
|
|
--- @return number The seat index, defaulting to -1 (any seat).
|
|
local function resolveSeat(seat)
|
|
if type(seat) == "string" then return vehicleSeats[seat] or -1 end
|
|
if type(seat) == "number" then return seat end
|
|
return -1
|
|
end
|
|
|
|
--- Runs a native inside pcall so a bad argument list cannot break the calling script.
|
|
--- @param label string The name used in the debug output if the call fails.
|
|
--- @param fn function The native to call.
|
|
--- @return boolean True if the call succeeded.
|
|
local function safeCall(label, fn, ...)
|
|
local ok, err = pcall(fn, ...)
|
|
if not ok then
|
|
debugPrint("^6Bridge^7: ^1ERROR^7 in ^3"..label.."^7: "..tostring(err))
|
|
end
|
|
return ok
|
|
end
|
|
|
|
--- Registers a task against a ped and returns its generation token.
|
|
--- @param ped number The ped the task belongs to.
|
|
--- @param name string The name of the task.
|
|
--- @return number The generation token for this assignment.
|
|
local function registerTask(ped, name)
|
|
if not PedTasks[ped] then PedTasks[ped] = { current = nil, tasks = {} } end
|
|
PedTasks[ped].current = name
|
|
PedTasks[ped].tasks[name] = true
|
|
|
|
TaskGen[ped] = TaskGen[ped] or {}
|
|
TaskGen[ped][name] = (TaskGen[ped][name] or 0) + 1
|
|
|
|
debugPrint("^6Bridge^7: ^3pedTask^7: '^6"..name.."' ^2assigned to ped^7 '^6"..ped.."'")
|
|
return TaskGen[ped][name]
|
|
end
|
|
|
|
--- Removes a task from a ped's registry.
|
|
--- @param ped number The ped the task belongs to.
|
|
--- @param name string The name of the task.
|
|
local function unregisterTask(ped, name)
|
|
if not PedTasks[ped] then return end
|
|
if PedTasks[ped].current == name then PedTasks[ped].current = nil end
|
|
PedTasks[ped].tasks[name] = nil
|
|
end
|
|
|
|
--- Watches a task until its condition is met, the timeout expires, or the ped is gone.
|
|
--- @param ped number The ped running the task.
|
|
--- @param name string The name of the task being watched.
|
|
--- @param gen number The generation token from registerTask.
|
|
--- @param check function A function returning true when the task is complete.
|
|
--- @param opts table Optional callbacks and the timeout in milliseconds.
|
|
local function watchTask(ped, name, gen, check, opts)
|
|
local timeout = opts.timeout or 15000
|
|
local start = GetGameTimer()
|
|
|
|
CreateThread(function()
|
|
while DoesEntityExist(ped) and (GetGameTimer() - start) < timeout do
|
|
local ok, result = pcall(check, ped)
|
|
if ok and result then
|
|
if TaskGen[ped] and TaskGen[ped][name] == gen then
|
|
unregisterTask(ped, name)
|
|
end
|
|
if opts.onComplete then opts.onComplete(ped) end
|
|
return
|
|
end
|
|
Wait(100)
|
|
end
|
|
|
|
if TaskGen[ped] and TaskGen[ped][name] == gen then
|
|
unregisterTask(ped, name)
|
|
end
|
|
if opts.onTimeout then opts.onTimeout(ped) end
|
|
end)
|
|
end
|
|
|
|
-------------------------------------------------------------
|
|
-- Movement
|
|
-------------------------------------------------------------
|
|
|
|
--- Sends a ped to walk or run to a set of coordinates.
|
|
---
|
|
--- The task completes once the ped is within `opts.radius` of the destination.
|
|
---
|
|
---@param ped number The ped to move.
|
|
---@param coords vector3|vector4|table|number The destination, or an entity to walk to.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **run** `boolean`: Run instead of walk. Default `false`.
|
|
--- - **speed** `number`: Raw move speed, `1.0` walks and `3.0` runs. Default depends on `run`.
|
|
--- - **radius** `number`: Arrival distance in game units. Default `2.0`.
|
|
--- - **heading** `number`: Target heading in degrees, `-1` keeps current. Default `-1`.
|
|
--- - **slide** `number`: Distance the ped may slide to a stop. Default `0.0`.
|
|
--- - **nativeTimeout** `number`: Milliseconds the native itself allows. Default `-1`.
|
|
--- - **timeout** `number`: Milliseconds before giving up. Default `15000`.
|
|
--- - **onComplete** `function`: Called with the ped once it arrives.
|
|
--- - **onTimeout** `function`: Called with the ped if it never arrives.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskGoTo(ped, vec3(455.0, -975.0, 30.0), { run = true })
|
|
--- ```
|
|
function pedTaskGoTo(ped, coords, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
opts = opts or {}
|
|
|
|
local target = resolveCoords(coords)
|
|
if not target then return false, "invalid coords" end
|
|
|
|
local speed = opts.speed or (opts.run and 3.0 or 1.0)
|
|
|
|
safeCall("pedTaskGoTo", TaskGoStraightToCoord, ped,
|
|
target.x, target.y, target.z,
|
|
speed, opts.nativeTimeout or -1,
|
|
opts.heading or -1, opts.slide or 0.0)
|
|
|
|
local name = "goTo"
|
|
local gen = registerTask(ped, name)
|
|
local radius = opts.radius or 2.0
|
|
|
|
watchTask(ped, name, gen, function(p)
|
|
return #(GetEntityCoords(p, false) - target) <= radius
|
|
end, opts)
|
|
|
|
return true
|
|
end
|
|
|
|
--- Sends a ped towards an entity for a set time or until it is within range.
|
|
---
|
|
---@param ped number The ped to move.
|
|
---@param target number The entity to walk to.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **duration** `number`: Milliseconds the ped keeps moving. Default `5000`.
|
|
--- - **distance** `number`: Distance considered "arrived". Default `4.0`.
|
|
--- - **speed** `number`: Move speed. Default `1.0`.
|
|
--- - **run** `boolean`: Run instead of walk. Default `false`.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskGoToEntity(ped, vehicle, { run = true })
|
|
--- ```
|
|
function pedTaskGoToEntity(ped, target, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
if type(target) ~= "number" or not DoesEntityExist(target) then return false, "invalid target" end
|
|
opts = opts or {}
|
|
|
|
safeCall("pedTaskGoToEntity", TaskGoToEntity, ped, target,
|
|
opts.duration or 5000, opts.distance or 4.0,
|
|
opts.speed or (opts.run and 3.0 or 1.0),
|
|
1073741824, 0)
|
|
|
|
registerTask(ped, "goToEntity")
|
|
return true
|
|
end
|
|
|
|
--- Makes a ped follow another entity at a set offset.
|
|
---
|
|
--- The task completes once the ped is within `opts.radius` of its target.
|
|
---
|
|
---@param ped number The ped that follows.
|
|
---@param target number The entity to follow.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **offset** `vector3`: Distance to keep from the target. Default `vec3(1.5, 0.0, 0.0)`.
|
|
--- - **run** `boolean`: Run instead of walk. Default `false`.
|
|
--- - **speed** `number`: Raw move speed. Default depends on `run`.
|
|
--- - **radius** `number`: Distance considered "arrived". Default `3.0`.
|
|
--- - **nativeTimeout** `number`: Milliseconds the native itself allows. Default `-1`.
|
|
--- - **timeout** `number`: Milliseconds before giving up. Default `15000`.
|
|
--- - **onComplete** `function`: Called with the ped once it catches up.
|
|
--- - **onTimeout** `function`: Called with the ped if it never catches up.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskFollow(ped, targetPed, { offset = vec3(2.0, 0.0, 0.0), run = true })
|
|
--- ```
|
|
function pedTaskFollow(ped, target, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
if type(target) ~= "number" or not DoesEntityExist(target) then return false, "invalid target" end
|
|
opts = opts or {}
|
|
|
|
local offset = opts.offset or vec3(1.5, 0.0, 0.0)
|
|
local speed = opts.speed or (opts.run and 3.0 or 1.0)
|
|
|
|
safeCall("pedTaskFollow", TaskFollowToOffsetOfEntity, ped, target,
|
|
offset.x, offset.y, offset.z,
|
|
speed, opts.nativeTimeout or -1,
|
|
opts.radius or 10.0, opts.persist ~= false)
|
|
|
|
local name = "follow"
|
|
local gen = registerTask(ped, name)
|
|
local radius = opts.radius or 3.0
|
|
|
|
watchTask(ped, name, gen, function(p)
|
|
return #(GetEntityCoords(p, false) - GetEntityCoords(target, false)) <= radius
|
|
end, opts)
|
|
|
|
return true
|
|
end
|
|
|
|
--- Sends a ped wandering randomly inside a radius.
|
|
---
|
|
--- This is a continuous task, it has no completion callback.
|
|
---
|
|
---@param ped number The ped to move.
|
|
---@param coords vector3|vector4|table|number The centre of the wandering area.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **radius** `number`: Size of the area to wander in. Default `20.0`.
|
|
--- - **length** `number`: Minimum length of each walk. Default `10`.
|
|
--- - **betweenWalks** `number`: Seconds to wait between walks. Default `5.0`.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskWander(ped, vec3(455.0, -975.0, 30.0), { radius = 10.0 })
|
|
--- ```
|
|
function pedTaskWander(ped, coords, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
opts = opts or {}
|
|
|
|
local target = resolveCoords(coords)
|
|
if not target then return false, "invalid coords" end
|
|
|
|
safeCall("pedTaskWander", TaskWanderInArea, ped,
|
|
target.x, target.y, target.z,
|
|
opts.radius or 20.0, opts.length or 10, opts.betweenWalks or 5.0)
|
|
|
|
registerTask(ped, "wander")
|
|
return true
|
|
end
|
|
|
|
--- Makes a ped run away from a threat.
|
|
---
|
|
---@param ped number The ped that flees.
|
|
---@param threat vector3|vector4|table|number The thing to run from, an entity or coordinates.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **distance** `number`: How far to run. Default `40.0`.
|
|
--- - **time** `number`: Milliseconds to keep fleeing, `-1` forever. Default `-1`.
|
|
--- - **timeout** `number`: Milliseconds before giving up. Default `10000`.
|
|
--- - **onComplete** `function`: Called with the ped once it is clear.
|
|
--- - **onTimeout** `function`: Called with the ped if it never escapes.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskFlee(ped, threatPed, { distance = 60.0 })
|
|
--- ```
|
|
function pedTaskFlee(ped, threat, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
opts = opts or {}
|
|
|
|
local distance = opts.distance or 40.0
|
|
local time = opts.time or -1
|
|
|
|
if type(threat) == "number" and DoesEntityExist(threat) then
|
|
safeCall("pedTaskFlee", TaskSmartFleePed, ped, threat, distance, time, false, false)
|
|
else
|
|
local from = resolveCoords(threat)
|
|
if not from then return false, "invalid threat" end
|
|
safeCall("pedTaskFlee", TaskSmartFleeCoord, ped, from.x, from.y, from.z, distance, time, false, false)
|
|
end
|
|
|
|
local name = "flee"
|
|
local gen = registerTask(ped, name)
|
|
|
|
watchTask(ped, name, gen, function(p)
|
|
local away = GetEntityCoords(p, false)
|
|
local from = resolveCoords(threat)
|
|
if not from then return false end
|
|
return #(away - from) >= distance
|
|
end, opts)
|
|
|
|
return true
|
|
end
|
|
|
|
--- Makes a ped turn to face an entity or a heading.
|
|
---
|
|
---@param ped number The ped to turn.
|
|
---@param target number|number The entity to face, or a heading in degrees.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **duration** `number`: Milliseconds the turn is allowed to take. Default `1000`.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskFace(ped, targetPed)
|
|
--- ```
|
|
function pedTaskFace(ped, target, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
opts = opts or {}
|
|
|
|
if type(target) == "number" and DoesEntityExist(target) then
|
|
safeCall("pedTaskFace", TaskTurnPedToFaceEntity, ped, target, opts.duration or 1000)
|
|
elseif type(target) == "number" then
|
|
safeCall("pedTaskFace", TaskAchieveHeading, ped, target, opts.duration or 1000)
|
|
else
|
|
return false, "invalid target"
|
|
end
|
|
|
|
registerTask(ped, "face")
|
|
return true
|
|
end
|
|
|
|
-------------------------------------------------------------
|
|
-- Vehicles
|
|
-------------------------------------------------------------
|
|
|
|
--- Tells a ped to get into a vehicle.
|
|
---
|
|
--- The task completes once the ped is seated.
|
|
---
|
|
---@param ped number The ped that enters.
|
|
---@param vehicle number The vehicle to enter.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **seat** `string|number`: "any", "driver", "passenger", "rearLeft", "rearRight" or an index. Default `"any"`.
|
|
--- - **speed** `number`: Approach speed, `1.0` walks and `2.0` runs. Default `1.0`.
|
|
--- - **flag** `string|number`: "normal", "teleportToVehicle", "carjackFromSeat", "teleportDirectly". Default `"normal"`.
|
|
--- - **nativeTimeout** `number`: Milliseconds the native itself allows. Default `-1`.
|
|
--- - **timeout** `number`: Milliseconds before giving up. Default `10000`.
|
|
--- - **onComplete** `function`: Called with the ped once seated.
|
|
--- - **onTimeout** `function`: Called with the ped if it never gets in.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskEnterVehicle(ped, vehicle, { seat = "driver" })
|
|
--- ```
|
|
function pedTaskEnterVehicle(ped, vehicle, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
if type(vehicle) ~= "number" or not DoesEntityExist(vehicle) then return false, "invalid vehicle" end
|
|
opts = opts or {}
|
|
|
|
local seat = resolveSeat(opts.seat or "any")
|
|
local flag = type(opts.flag) == "string" and enterVehicleFlags[opts.flag] or (opts.flag or enterVehicleFlags.normal)
|
|
|
|
safeCall("pedTaskEnterVehicle", TaskEnterVehicle, ped, vehicle,
|
|
opts.nativeTimeout or -1, seat, opts.speed or 1.0, flag, 0)
|
|
|
|
local name = "enterVehicle"
|
|
local gen = registerTask(ped, name)
|
|
|
|
watchTask(ped, name, gen, function(p)
|
|
return IsPedInAnyVehicle(p, false)
|
|
end, opts)
|
|
|
|
return true
|
|
end
|
|
|
|
--- Tells a ped to get out of a vehicle.
|
|
---
|
|
---@param ped number The ped that leaves.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **vehicle** `number`: The vehicle to leave, defaults to the one the ped is in.
|
|
--- - **flag** `string|number`: "normal", "normalSlow", "keepDoorOpen", "teleportOutside", "throwOutOfVehicle", "moveToPassengerFirst". Default `"normal"`.
|
|
--- - **timeout** `number`: Milliseconds before giving up. Default `10000`.
|
|
--- - **onComplete** `function`: Called with the ped once it is out.
|
|
--- - **onTimeout** `function`: Called with the ped if it never gets out.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskLeaveVehicle(ped)
|
|
--- ```
|
|
function pedTaskLeaveVehicle(ped, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
opts = opts or {}
|
|
|
|
local vehicle = opts.vehicle or GetVehiclePedIsIn(ped, false)
|
|
if not vehicle or vehicle == 0 then return false, "ped is not in a vehicle" end
|
|
|
|
local flag = type(opts.flag) == "string" and leaveVehicleFlags[opts.flag] or (opts.flag or leaveVehicleFlags.normal)
|
|
|
|
safeCall("pedTaskLeaveVehicle", TaskLeaveVehicle, ped, vehicle, flag)
|
|
|
|
local name = "leaveVehicle"
|
|
local gen = registerTask(ped, name)
|
|
|
|
watchTask(ped, name, gen, function(p)
|
|
return not IsPedInAnyVehicle(p, false)
|
|
end, opts)
|
|
|
|
return true
|
|
end
|
|
|
|
--- Tells a ped to open a vehicle door.
|
|
---
|
|
--- GTA opens the door nearest to the given seat, so use seat names rather than
|
|
--- door names here. "driver" opens the front left door, "passenger" the front right.
|
|
---
|
|
---@param ped number The ped that opens the door.
|
|
---@param vehicle number The vehicle to open.
|
|
---@param door string|number The seat to open, e.g. "driver" or an index.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **speed** `number`: Opening speed. Default `1.0`.
|
|
--- - **nativeTimeout** `number`: Milliseconds the native itself allows. Default `-1`.
|
|
--- - **timeout** `number`: Milliseconds before giving up. Default `10000`.
|
|
--- - **onComplete** `function`: Called with the ped once the door is open.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskOpenDoor(ped, vehicle, "passenger")
|
|
--- ```
|
|
function pedTaskOpenDoor(ped, vehicle, door, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
if type(vehicle) ~= "number" or not DoesEntityExist(vehicle) then return false, "invalid vehicle" end
|
|
opts = opts or {}
|
|
|
|
local index = resolveSeat(door)
|
|
if index < 0 then return false, "invalid door" end
|
|
|
|
-- remember which doors were shut, GTA may open a different one to the seat asked for
|
|
local beforeRatios = {}
|
|
for i = 0, 5 do beforeRatios[i] = GetVehicleDoorAngleRatio(vehicle, i) end
|
|
|
|
safeCall("pedTaskOpenDoor", TaskOpenVehicleDoor, ped, vehicle,
|
|
opts.nativeTimeout or -1, index, opts.speed or 1.0)
|
|
|
|
local name = "openDoor"
|
|
local gen = registerTask(ped, name)
|
|
|
|
watchTask(ped, name, gen, function()
|
|
for i = 0, 5 do
|
|
if beforeRatios[i] <= 0.0 and GetVehicleDoorAngleRatio(vehicle, i) > 0.0 then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end, opts)
|
|
|
|
return true
|
|
end
|
|
|
|
--- Closes a vehicle door.
|
|
---
|
|
--- GTA has no "close door" task, so this uses the door native directly.
|
|
---
|
|
--- If the ped that opened the door still has that task running it will fight the
|
|
--- close, so pass their handle as `opts.ped` to cancel their tasks first.
|
|
---
|
|
---@param vehicle number The vehicle to close.
|
|
---@param door string|number The door to close, e.g. "driver" or an index.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **instant** `boolean`: Snap the door shut. Default `false`.
|
|
--- - **ped** `number`: A ped whose tasks should be cancelled before closing.
|
|
---
|
|
---@return boolean True if the door was closed.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedCloseDoor(vehicle, "passenger", { ped = ped })
|
|
--- ```
|
|
function pedCloseDoor(vehicle, door, opts)
|
|
if type(vehicle) ~= "number" or not DoesEntityExist(vehicle) then return false, "invalid vehicle" end
|
|
opts = opts or {}
|
|
|
|
if opts.ped and DoesEntityExist(opts.ped) then
|
|
ClearPedTasksImmediately(opts.ped)
|
|
end
|
|
|
|
local index = resolveSeat(door)
|
|
if index < 0 then return false, "invalid door" end
|
|
|
|
safeCall("pedCloseDoor", SetVehicleDoorShut, vehicle, index, opts.instant == true)
|
|
return true
|
|
end
|
|
|
|
--- Makes a ped drive a vehicle to a set of coordinates.
|
|
---
|
|
---@param ped number The ped driving.
|
|
---@param vehicle number The vehicle to drive.
|
|
---@param coords vector3|vector4|table The destination.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **speed** `number`: Driving speed. Default `30.0`.
|
|
--- - **drivingMode** `number`: Driving mode bitmask. Default `16777216`.
|
|
--- - **model** `number`: Vehicle model hash, defaults to the vehicle's own model.
|
|
--- - **stopRange** `number`: How close to get before stopping. Default `1.0`.
|
|
--- - **timeout** `number`: Milliseconds before giving up. Default `30000`.
|
|
--- - **onComplete** `function`: Called with the ped once it arrives.
|
|
--- - **onTimeout** `function`: Called with the ped if it never arrives.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskDriveTo(ped, vehicle, vec3(100.0, 200.0, 30.0))
|
|
--- ```
|
|
function pedTaskDriveTo(ped, vehicle, coords, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
if type(vehicle) ~= "number" or not DoesEntityExist(vehicle) then return false, "invalid vehicle" end
|
|
opts = opts or {}
|
|
|
|
local target = resolveCoords(coords)
|
|
if not target then return false, "invalid coords" end
|
|
|
|
safeCall("pedTaskDriveTo", TaskVehicleDriveToCoord, ped, vehicle,
|
|
target.x, target.y, target.z,
|
|
opts.speed or 30.0, 1.0,
|
|
opts.model or GetEntityModel(vehicle),
|
|
opts.drivingMode or 16777216,
|
|
opts.stopRange or 1.0, true)
|
|
|
|
local name = "driveTo"
|
|
local gen = registerTask(ped, name)
|
|
local radius = opts.radius or 5.0
|
|
|
|
watchTask(ped, name, gen, function(p)
|
|
local veh = GetVehiclePedIsIn(p, false)
|
|
if not veh or veh == 0 then return false end
|
|
return #(GetEntityCoords(veh, false) - target) <= radius
|
|
end, opts)
|
|
|
|
return true
|
|
end
|
|
|
|
--- Makes a ped park a vehicle at a set of coordinates.
|
|
---
|
|
---@param ped number The ped parking.
|
|
---@param vehicle number The vehicle to park.
|
|
---@param coords vector3|vector4|table Where to park.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **heading** `number`: Final heading in degrees. Default `0`.
|
|
--- - **mode** `string|number`: "ignoreHeading", "forward", "reverse". Default `"ignoreHeading"`.
|
|
--- - **radius** `number`: How close the vehicle must end up. Default `20.0`.
|
|
--- - **keepEngineOn** `boolean`: Leave the engine running. Default `false`.
|
|
--- - **timeout** `number`: Milliseconds before giving up. Default `30000`.
|
|
--- - **onComplete** `function`: Called with the ped once parked.
|
|
--- - **onTimeout** `function`: Called with the ped if it never parks.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskPark(ped, vehicle, vec3(100.0, 200.0, 30.0), { heading = 90.0 })
|
|
--- ```
|
|
function pedTaskPark(ped, vehicle, coords, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
if type(vehicle) ~= "number" or not DoesEntityExist(vehicle) then return false, "invalid vehicle" end
|
|
opts = opts or {}
|
|
|
|
local target = resolveCoords(coords)
|
|
if not target then return false, "invalid coords" end
|
|
|
|
local mode = type(opts.mode) == "string" and parkModes[opts.mode] or (opts.mode or parkModes.ignoreHeading)
|
|
|
|
safeCall("pedTaskPark", TaskVehiclePark, ped, vehicle,
|
|
target.x, target.y, target.z,
|
|
opts.heading or 0, mode, opts.radius or 20.0, opts.keepEngineOn == true)
|
|
|
|
local name = "park"
|
|
local gen = registerTask(ped, name)
|
|
local radius = opts.radius or 20.0
|
|
|
|
watchTask(ped, name, gen, function(p)
|
|
local veh = GetVehiclePedIsIn(p, false)
|
|
if not veh or veh == 0 then return false end
|
|
return #(GetEntityCoords(veh, false) - target) <= radius
|
|
end, opts)
|
|
|
|
return true
|
|
end
|
|
|
|
--- Makes a ped in a vehicle chase another entity.
|
|
---
|
|
--- This is a continuous task, it has no completion callback.
|
|
---
|
|
---@param ped number The ped driving.
|
|
---@param target number The entity to chase.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskChase(ped, targetVehicle)
|
|
--- ```
|
|
function pedTaskChase(ped, target)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
if type(target) ~= "number" or not DoesEntityExist(target) then return false, "invalid target" end
|
|
|
|
safeCall("pedTaskChase", TaskVehicleChase, ped, target)
|
|
|
|
registerTask(ped, "chase")
|
|
return true
|
|
end
|
|
|
|
-------------------------------------------------------------
|
|
-- Combat
|
|
-------------------------------------------------------------
|
|
|
|
--- Sends a ped to attack a target.
|
|
---
|
|
--- This is a continuous task, it has no completion callback.
|
|
---
|
|
---@param ped number The ped that attacks.
|
|
---@param target number The entity to attack.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **style** `string`: "melee" (default) or "ranged".
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskCombat(ped, targetPed, { style = "melee" })
|
|
--- ```
|
|
function pedTaskCombat(ped, target, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
if type(target) ~= "number" or not DoesEntityExist(target) then return false, "invalid target" end
|
|
opts = opts or {}
|
|
|
|
if (opts.style or "melee") == "ranged" then
|
|
safeCall("pedTaskCombat", TaskCombatPed, ped, target, 0, 16)
|
|
else
|
|
safeCall("pedTaskCombat", TaskPutPedDirectlyIntoMelee, ped, target, 0.0, -1.0, 0.0, false)
|
|
end
|
|
|
|
registerTask(ped, "combat")
|
|
return true
|
|
end
|
|
|
|
--- Makes a ped shoot at an entity or a set of coordinates.
|
|
---
|
|
--- This is a continuous task, it has no completion callback.
|
|
---
|
|
--- GTA will not let a ped fire until it is already aiming at the target, so pass
|
|
--- `opts.aim = true` to have this issue an aim task first, or aim the ped yourself
|
|
--- with `pedTaskAimAt` before calling this.
|
|
---
|
|
---@param ped number The ped that shoots.
|
|
---@param target vector3|vector4|table|number The entity or coordinates to shoot at.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **duration** `number`: Milliseconds to keep shooting, `-1` indefinitely. Default `-1`.
|
|
--- - **pattern** `string|number`: Firing pattern, e.g. "FIRING_PATTERN_FULL_AUTO". Default full auto.
|
|
--- - **aim** `boolean`: Aim at the target first so the ped can actually fire. Default `false`.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskShootAt(ped, targetPed, { pattern = "FIRING_PATTERN_BURST_FIRE", aim = true })
|
|
--- ```
|
|
function pedTaskShootAt(ped, target, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
opts = opts or {}
|
|
|
|
local duration = opts.duration or -1
|
|
local pattern = type(opts.pattern) == "string" and joaat(opts.pattern) or (opts.pattern or joaat("FIRING_PATTERN_FULL_AUTO"))
|
|
|
|
if opts.aim then
|
|
pedTaskAimAt(ped, target, { duration = opts.aimDuration or duration })
|
|
Wait(100)
|
|
end
|
|
|
|
if type(target) == "number" and DoesEntityExist(target) then
|
|
safeCall("pedTaskShootAt", TaskShootAtEntity, ped, target, duration, pattern)
|
|
else
|
|
local coords = resolveCoords(target)
|
|
if not coords then return false, "invalid target" end
|
|
safeCall("pedTaskShootAt", TaskShootAtCoord, ped, coords.x, coords.y, coords.z, duration, pattern)
|
|
end
|
|
|
|
registerTask(ped, "shootAt")
|
|
return true
|
|
end
|
|
|
|
--- Makes a ped aim a weapon at an entity or a set of coordinates.
|
|
---
|
|
--- This is a continuous task, it has no completion callback.
|
|
---
|
|
---@param ped number The ped that aims.
|
|
---@param target vector3|vector4|table|number The entity or coordinates to aim at.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **duration** `number`: Milliseconds to hold the aim, `-1` indefinitely. Default `-1`.
|
|
--- - **instant** `boolean`: Snap straight into the aim. Default `false`.
|
|
--- - **intro** `boolean`: Play the aim intro animation. Default `false`.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskAimAt(ped, vec3(100.0, 200.0, 30.0))
|
|
--- ```
|
|
function pedTaskAimAt(ped, target, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
opts = opts or {}
|
|
|
|
local duration = opts.duration or -1
|
|
|
|
if type(target) == "number" and DoesEntityExist(target) then
|
|
safeCall("pedTaskAimAt", TaskAimGunAtEntity, ped, target, duration, opts.instant == true)
|
|
else
|
|
local coords = resolveCoords(target)
|
|
if not coords then return false, "invalid target" end
|
|
safeCall("pedTaskAimAt", TaskAimGunAtCoord, ped, coords.x, coords.y, coords.z,
|
|
duration, opts.instant == true, opts.intro == true)
|
|
end
|
|
|
|
registerTask(ped, "aimAt")
|
|
return true
|
|
end
|
|
|
|
--- Sets how accurate a ped is when shooting.
|
|
---
|
|
---@param ped number The ped to configure.
|
|
---@param level number The accuracy level, `0` is poor and `100` is perfect.
|
|
---
|
|
---@return boolean True if the accuracy was applied.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskAccuracy(ped, 80)
|
|
--- ```
|
|
function pedTaskAccuracy(ped, level)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
|
|
SetPedAccuracy(ped, level or 0)
|
|
return true
|
|
end
|
|
|
|
--- Sets the firing pattern a ped uses.
|
|
---
|
|
---@param ped number The ped to configure.
|
|
---@param pattern string|number The pattern name or hash, e.g. "FIRING_PATTERN_FULL_AUTO".
|
|
---
|
|
---@return boolean True if the pattern was applied.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskFiringPattern(ped, "FIRING_PATTERN_BURST_FIRE")
|
|
--- ```
|
|
function pedTaskFiringPattern(ped, pattern)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
if not pattern then return false, "invalid pattern" end
|
|
|
|
local hash = type(pattern) == "string" and joaat(pattern) or pattern
|
|
SetPedFiringPattern(ped, hash)
|
|
return true
|
|
end
|
|
|
|
-------------------------------------------------------------
|
|
-- Animation
|
|
-------------------------------------------------------------
|
|
|
|
--- Plays an animation on a ped.
|
|
---
|
|
--- The task completes once the animation has finished, unless `opts.loop` is set.
|
|
---
|
|
---@param ped number The ped to animate.
|
|
---@param dict string The animation dictionary.
|
|
---@param anim string The animation name.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **blendIn** `number`: Blend in speed. Default `8.0`.
|
|
--- - **blendOut** `number`: Blend out speed. Default `-8.0`.
|
|
--- - **duration** `number`: Milliseconds, `-1` to loop. Default `-1`.
|
|
--- - **flag** `string|number`: "looping", "holdLastFrame", "repositionWhenFinished", "notInterruptable", "upperBody". Default `"looping"`.
|
|
--- - **playbackRate** `number`: Playback speed. Default `1.0`.
|
|
--- - **lock** `boolean`: Lock the ped in place. Default `false`.
|
|
--- - **loop** `boolean`: Treat the animation as never ending. Default `false`.
|
|
--- - **timeout** `number`: Milliseconds before giving up. Default `15000`.
|
|
--- - **onComplete** `function`: Called with the ped once the animation ends.
|
|
--- - **onTimeout** `function`: Called with the ped if it never ends.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskPlayAnim(ped, "amb@world_human_clipboard@male@idle_a", "idle_c")
|
|
--- ```
|
|
function pedTaskPlayAnim(ped, dict, anim, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
if not dict or not anim then return false, "invalid animation" end
|
|
opts = opts or {}
|
|
|
|
loadAnimDict(dict)
|
|
|
|
local flag = type(opts.flag) == "string" and animFlags[opts.flag] or (opts.flag or animFlags.looping)
|
|
local lock = opts.lock and true or false
|
|
|
|
safeCall("pedTaskPlayAnim", TaskPlayAnim, ped, dict, anim,
|
|
opts.blendIn or 8.0, opts.blendOut or -8.0,
|
|
opts.duration or -1, flag, opts.playbackRate or 1.0,
|
|
lock, lock, lock)
|
|
|
|
local name = "playAnim"
|
|
local gen = registerTask(ped, name)
|
|
|
|
if not opts.loop then
|
|
watchTask(ped, name, gen, function(p)
|
|
return not IsEntityPlayingAnim(p, dict, anim, 3)
|
|
end, opts)
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
--- Starts a scenario on a ped, such as a sitting or leaning animation.
|
|
---
|
|
--- This is a continuous task, it has no completion callback.
|
|
---
|
|
---@param ped number The ped to animate.
|
|
---@param scenario string The scenario name.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **duration** `number`: Time before the ped leaves the scenario, `0` keeps it. Default `0`.
|
|
--- - **intro** `boolean`: Play the scenario intro clip. Default `true`.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskScenario(ped, "WORLD_HUMAN_STAND_IMPATIENT")
|
|
--- ```
|
|
function pedTaskScenario(ped, scenario, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
if not scenario then return false, "invalid scenario" end
|
|
opts = opts or {}
|
|
|
|
safeCall("pedTaskScenario", TaskStartScenarioInPlace, ped, scenario,
|
|
opts.duration or 0, opts.intro ~= false)
|
|
|
|
registerTask(ped, "scenario")
|
|
return true
|
|
end
|
|
|
|
--- Makes a ped look at an entity or a set of coordinates.
|
|
---
|
|
---@param ped number The ped that looks.
|
|
---@param target vector3|vector4|table|number The entity or coordinates to look at.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **duration** `number`: Milliseconds to look. Default `5000`.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskLookAt(ped, targetPed, { duration = 3000 })
|
|
--- ```
|
|
function pedTaskLookAt(ped, target, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
opts = opts or {}
|
|
|
|
local duration = opts.duration or 5000
|
|
|
|
if type(target) == "number" and DoesEntityExist(target) then
|
|
safeCall("pedTaskLookAt", TaskLookAtEntity, ped, target, duration, 1, 0)
|
|
else
|
|
local coords = resolveCoords(target)
|
|
if not coords then return false, "invalid target" end
|
|
safeCall("pedTaskLookAt", TaskLookAtCoord, ped, coords.x, coords.y, coords.z, duration, 1, 0)
|
|
end
|
|
|
|
registerTask(ped, "lookAt")
|
|
return true
|
|
end
|
|
|
|
--- Makes a ped ragdoll for a set duration.
|
|
---
|
|
---@param ped number The ped to ragdoll.
|
|
---@param duration number Milliseconds to stay ragdolled. Default `5000`.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **type** `number`: Ragdoll type, `0` relaxes and anything else balances. Default `0`.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskRagdoll(ped, 3000)
|
|
--- ```
|
|
function pedTaskRagdoll(ped, duration, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
opts = opts or {}
|
|
|
|
local time = duration or 5000
|
|
safeCall("pedTaskRagdoll", SetPedToRagdoll, ped, time, time, opts.type or 0, false, false, false)
|
|
|
|
registerTask(ped, "ragdoll")
|
|
return true
|
|
end
|
|
|
|
--- Makes a ped stand still for a set duration.
|
|
---
|
|
---@param ped number The ped to hold still.
|
|
---@param duration number Milliseconds to hold. Default `5000`.
|
|
---
|
|
---@return boolean True if the task was issued.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskStandStill(ped, 3000)
|
|
--- ```
|
|
function pedTaskStandStill(ped, duration)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
|
|
safeCall("pedTaskStandStill", TaskStandStill, ped, duration or 5000)
|
|
|
|
registerTask(ped, "standStill")
|
|
return true
|
|
end
|
|
|
|
-------------------------------------------------------------
|
|
-- Behaviour
|
|
-------------------------------------------------------------
|
|
|
|
--- Sets what a ped is allowed to do in combat.
|
|
---
|
|
--- The native takes one attribute at a time, so every option you provide is applied
|
|
--- individually. Options left out are not changed.
|
|
---
|
|
---@param ped number The ped to configure.
|
|
---@param opts table A table of named combat attributes set to `true` or `false`.
|
|
--- See `combatAttributes` for the full list, for example `alwaysFight`, `canShoot`,
|
|
--- `canUseCover`, `aggressive`, `perfectAccuracy` and `disableFleeFromCombat`.
|
|
---
|
|
---@return boolean True if the attributes were applied.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedCombatAttributes(ped, { alwaysFight = true, canShoot = true, canUseCover = true })
|
|
--- ```
|
|
function pedCombatAttributes(ped, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
opts = opts or {}
|
|
|
|
for name, enabled in pairs(opts) do
|
|
local index = combatAttributes[name]
|
|
if index then
|
|
SetPedCombatAttributes(ped, index, enabled and true or false)
|
|
else
|
|
debugPrint("^6Bridge^7: ^3pedCombatAttributes^7: ^1unknown attribute^7 '^6"..tostring(name).."^7'")
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
--- Sets what a ped is allowed to do when it is fleeing.
|
|
---
|
|
--- The native takes a bitmask, so each attribute is applied as `1 << bit`.
|
|
--- FiveM only documents bit `15` as "force cower", the rest follow the game's
|
|
--- flee attribute ordering, so use `pedSetFleeAttribute` if you need a specific bit.
|
|
---
|
|
---@param ped number The ped to configure.
|
|
---@param opts table A table keyed by flee attribute bit position, e.g. `{ [15] = true }`.
|
|
---
|
|
---@return boolean True if the attributes were applied.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedFleeAttributes(ped, { [15] = true })
|
|
--- ```
|
|
function pedFleeAttributes(ped, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
opts = opts or {}
|
|
|
|
for bit, enabled in pairs(opts) do
|
|
if type(bit) == "number" then
|
|
SetPedFleeAttributes(ped, 1 << bit, enabled and true or false)
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
--- Sets a single flee attribute on a ped by its bit position.
|
|
---
|
|
---@param ped number The ped to configure.
|
|
---@param bit number The flee attribute bit position, `15` is force cower.
|
|
---@param enabled boolean True to enable the attribute.
|
|
---
|
|
---@return boolean True if the attribute was applied.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedSetFleeAttribute(ped, 15, true)
|
|
--- ```
|
|
function pedSetFleeAttribute(ped, bit, enabled)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
if type(bit) ~= "number" then return false, "invalid bit" end
|
|
|
|
SetPedFleeAttributes(ped, 1 << bit, enabled ~= false)
|
|
return true
|
|
end
|
|
|
|
--- Sets how far a ped will engage in combat.
|
|
---
|
|
---@param ped number The ped to configure.
|
|
---@param range string|number "melee", "preferred", "far" or a raw value 0-2.
|
|
---
|
|
---@return boolean True if the range was applied.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedCombatRange(ped, "melee")
|
|
--- ```
|
|
function pedCombatRange(ped, range)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
|
|
local value = type(range) == "string" and combatRanges[range] or range
|
|
if value == nil then return false, "invalid range" end
|
|
|
|
SetPedCombatRange(ped, value)
|
|
return true
|
|
end
|
|
|
|
--- Sets how well a ped fights, from `0` (poor) to `4` (very good).
|
|
---
|
|
---@param ped number The ped to configure.
|
|
---@param level number The combat ability level, 0-4.
|
|
---
|
|
---@return boolean True if the ability was applied.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedCombatAbility(ped, 3)
|
|
--- ```
|
|
function pedCombatAbility(ped, level)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
|
|
SetPedCombatAbility(ped, level or 0)
|
|
return true
|
|
end
|
|
|
|
--- Sets how a ped moves while fighting.
|
|
---
|
|
---@param ped number The ped to configure.
|
|
---@param mode string|number "hold", "strafe", "advance" or a raw value 0-2.
|
|
---
|
|
---@return boolean True if the movement was applied.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedCombatMovement(ped, "strafe")
|
|
--- ```
|
|
function pedCombatMovement(ped, mode)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
|
|
local value = type(mode) == "string" and combatMovements[mode] or mode
|
|
if value == nil then return false, "invalid movement" end
|
|
|
|
SetPedCombatMovement(ped, value)
|
|
return true
|
|
end
|
|
|
|
--- Sets a raw ped config flag, for anything the helpers above do not cover.
|
|
---
|
|
---@param ped number The ped to configure.
|
|
---@param flag number The config flag index.
|
|
---@param value boolean True to enable the flag.
|
|
---
|
|
---@return boolean True if the flag was applied.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedSetConfigFlag(ped, 17, true) -- block non temporary events
|
|
--- ```
|
|
function pedSetConfigFlag(ped, flag, value)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
if type(flag) ~= "number" then return false, "invalid flag" end
|
|
|
|
SetPedConfigFlag(ped, flag, value ~= false)
|
|
return true
|
|
end
|
|
|
|
--- Reads a raw ped config flag.
|
|
---
|
|
---@param ped number The ped to check.
|
|
---@param flag number The config flag index.
|
|
---
|
|
---@return boolean The value of the flag.
|
|
---@usage
|
|
--- ```lua
|
|
--- local blocksEvents = pedGetConfigFlag(ped, 17)
|
|
--- ```
|
|
function pedGetConfigFlag(ped, flag)
|
|
if not isValidPed(ped) then return false end
|
|
if type(flag) ~= "number" then return false end
|
|
|
|
-- the native can hand back 1/0 instead of true/false, so normalise it
|
|
return not not GetPedConfigFlag(ped, flag, false)
|
|
end
|
|
|
|
--- Sets a group of general states on a ped.
|
|
---
|
|
--- Only the options provided are applied, everything else is left alone.
|
|
---
|
|
---@param ped number The ped to configure.
|
|
---@param opts table The states to apply.
|
|
--- - **invincible** `boolean`: Cannot be hurt or killed.
|
|
--- - **canRagdoll** `boolean`: Can be knocked over.
|
|
--- - **dropsWeapons** `boolean`: Drops weapons on death.
|
|
--- - **criticalHits** `boolean`: Suffers critical damage.
|
|
--- - **canSwitchWeapon** `boolean`: Can change weapon.
|
|
--- - **drunk** `boolean`: Staggered and unable to aim.
|
|
--- - **armour** `number`: Armour value, 0-100.
|
|
--- - **health** `number`: Health value.
|
|
--- - **canBeDamaged** `boolean`: Can take damage.
|
|
--- - **blocksEvents** `boolean`: Ignores scripted events.
|
|
--- - **moveRate** `number`: Movement rate override, `1.0` is normal.
|
|
--- - **density** `number`: Crowd density multiplier for this frame.
|
|
---
|
|
---@return boolean True if the states were applied.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedState(ped, { invincible = true, canRagdoll = false, armour = 100 })
|
|
--- ```
|
|
function pedState(ped, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
opts = opts or {}
|
|
|
|
if opts.invincible ~= nil then SetEntityInvincible(ped, opts.invincible) end
|
|
if opts.canRagdoll ~= nil then SetPedCanRagdoll(ped, opts.canRagdoll) end
|
|
if opts.dropsWeapons ~= nil then SetPedDropsWeaponsWhenDead(ped, opts.dropsWeapons) end
|
|
if opts.criticalHits ~= nil then SetPedSuffersCriticalHits(ped, opts.criticalHits) end
|
|
if opts.canSwitchWeapon ~= nil then SetPedCanSwitchWeapon(ped, opts.canSwitchWeapon) end
|
|
if opts.drunk ~= nil then SetPedIsDrunk(ped, opts.drunk) end
|
|
if opts.armour ~= nil then SetPedArmour(ped, opts.armour) end
|
|
if opts.health ~= nil then SetEntityHealth(ped, opts.health) end
|
|
if opts.canBeDamaged ~= nil then SetEntityCanBeDamaged(ped, opts.canBeDamaged) end
|
|
if opts.blocksEvents ~= nil then SetBlockingOfNonTemporaryEvents(ped, opts.blocksEvents) end
|
|
if opts.moveRate ~= nil then SetPedMoveRateOverride(ped, opts.moveRate) end
|
|
if opts.density ~= nil then SetPedDensityMultiplierThisFrame(opts.density) end
|
|
|
|
return true
|
|
end
|
|
|
|
-------------------------------------------------------------
|
|
-- Tracking
|
|
-------------------------------------------------------------
|
|
|
|
--- Returns the name of the task currently assigned to a ped.
|
|
---
|
|
---@param ped number The ped to check.
|
|
---@return string|nil The task name, or nil if the ped has no tracked task.
|
|
---@usage
|
|
--- ```lua
|
|
--- if pedTaskStatus(ped) == "goTo" then print("on the move") end
|
|
--- ```
|
|
function pedTaskStatus(ped)
|
|
if not PedTasks[ped] then return nil end
|
|
return PedTasks[ped].current
|
|
end
|
|
|
|
--- Checks if a named task is currently assigned to a ped.
|
|
---
|
|
---@param ped number The ped to check.
|
|
---@param name string The task name to look for.
|
|
---@return boolean True if the task is running.
|
|
---@usage
|
|
--- ```lua
|
|
--- if pedTaskIsRunning(ped, "combat") then print("fighting") end
|
|
--- ```
|
|
function pedTaskIsRunning(ped, name)
|
|
if not PedTasks[ped] then return false end
|
|
return PedTasks[ped].tasks[name] == true
|
|
end
|
|
|
|
--- Checks if a native task is active on a ped by its task index.
|
|
---
|
|
--- Use `pedTaskWanderIndex()` to get the index for the wander task.
|
|
---
|
|
---@param ped number The ped to check.
|
|
---@param index number The native task index.
|
|
---@return boolean True if the task is active.
|
|
---@usage
|
|
--- ```lua
|
|
--- if pedTaskActive(ped, pedTaskWanderIndex()) then print("wandering") end
|
|
--- ```
|
|
function pedTaskActive(ped, index)
|
|
if not isValidPed(ped) then return false end
|
|
if type(index) ~= "number" then return false end
|
|
|
|
return GetIsTaskActive(ped, index)
|
|
end
|
|
|
|
--- Returns the native task index used by the wander task.
|
|
---
|
|
---@return number The task index.
|
|
function pedTaskWanderIndex()
|
|
return TASK_INDEX_WANDER
|
|
end
|
|
|
|
--- Cancels a task on a ped.
|
|
---
|
|
--- GTA has no per task cancellation, so the ped's whole task queue is cleared.
|
|
--- The `name` is used to confirm the task is still the one running before clearing.
|
|
---
|
|
---@param ped number The ped to cancel the task on.
|
|
---@param name string The task name to cancel.
|
|
---@return boolean True if the task was cancelled.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskCancel(ped, "goTo")
|
|
--- ```
|
|
function pedTaskCancel(ped, name)
|
|
if not PedTasks[ped] then return false, "no task running" end
|
|
if name and PedTasks[ped].current ~= name then return false, "task is not running" end
|
|
|
|
ClearPedTasksImmediately(ped)
|
|
|
|
TaskGen[ped] = TaskGen[ped] or {}
|
|
if name then
|
|
TaskGen[ped][name] = (TaskGen[ped][name] or 0) + 1
|
|
unregisterTask(ped, name)
|
|
else
|
|
PedTasks[ped].current = nil
|
|
PedTasks[ped].tasks = {}
|
|
end
|
|
|
|
debugPrint("^6Bridge^7: ^3pedTask^7: '^6"..(name or "all").."' ^1cancelled^7 for ped '^6"..ped.."'")
|
|
return true
|
|
end
|
|
|
|
--- Clears every task on a ped and resets its tracking.
|
|
---
|
|
---@param ped number The ped to clear.
|
|
---@return boolean True if the ped was cleared.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskClear(ped)
|
|
--- ```
|
|
function pedTaskClear(ped)
|
|
if not DoesEntityExist(ped) then return false, "invalid ped" end
|
|
|
|
ClearPedTasksImmediately(ped)
|
|
|
|
PedTasks[ped] = { current = nil, tasks = {} }
|
|
TaskGen[ped] = {}
|
|
|
|
return true
|
|
end
|
|
|
|
--- Waits for a condition to become true for a ped.
|
|
---
|
|
--- Useful for chaining tasks together without needing a separate script thread.
|
|
---
|
|
---@param ped number The ped to watch.
|
|
---@param condition function A function taking the ped and returning true when done.
|
|
---@param opts table|nil Optional settings.
|
|
--- - **timeout** `number`: Milliseconds before giving up. Default `15000`.
|
|
--- - **interval** `number`: How often to check. Default `100`.
|
|
--- - **onComplete** `function`: Called with the ped once the condition is met.
|
|
--- - **onTimeout** `function`: Called with the ped if the condition is never met.
|
|
---
|
|
---@return boolean True if the wait was started.
|
|
---@usage
|
|
--- ```lua
|
|
--- pedTaskWait(ped, function(p) return #(GetEntityCoords(p) - target) < 3.0 end, {
|
|
--- onComplete = function(p) pedTaskPlayAnim(p, "dict", "anim") end,
|
|
--- })
|
|
--- ```
|
|
function pedTaskWait(ped, condition, opts)
|
|
if not isValidPed(ped) then return false, "invalid ped" end
|
|
if type(condition) ~= "function" then return false, "invalid condition" end
|
|
opts = opts or {}
|
|
|
|
local name = "wait"
|
|
local gen = registerTask(ped, name)
|
|
local interval = opts.interval or 100
|
|
|
|
CreateThread(function()
|
|
local timeout = opts.timeout or 15000
|
|
local start = GetGameTimer()
|
|
|
|
while DoesEntityExist(ped) and (GetGameTimer() - start) < timeout do
|
|
local ok, result = pcall(condition, ped)
|
|
if ok and result then
|
|
if TaskGen[ped] and TaskGen[ped][name] == gen then
|
|
unregisterTask(ped, name)
|
|
end
|
|
if opts.onComplete then opts.onComplete(ped) end
|
|
return
|
|
end
|
|
Wait(interval)
|
|
end
|
|
|
|
if TaskGen[ped] and TaskGen[ped][name] == gen then
|
|
unregisterTask(ped, name)
|
|
end
|
|
if opts.onTimeout then opts.onTimeout(ped) end
|
|
end)
|
|
|
|
return true
|
|
end
|
|
|
|
-------------------------------------------------------------
|
|
-- Cleanup
|
|
-------------------------------------------------------------
|
|
|
|
--- Clears any outstanding tasks when the resource stops.
|
|
onResourceStop(function()
|
|
for ped in pairs(PedTasks) do
|
|
if DoesEntityExist(ped) then
|
|
ClearPedTasksImmediately(ped)
|
|
end
|
|
end
|
|
PedTasks = {}
|
|
TaskGen = {}
|
|
end, true)
|
|
end
|