Knowledge from the fieldRedM

OPEN_SEQUENCE_TASK / TASK_PERFORM_SEQUENCE: pass `0` as ped inside the sequence, capture the return as the sequence ID, then OPEN→CLOSE→PERFORM→CLEAR

learning:12

OPEN_SEQUENCE_TASK / TASK_PERFORM_SEQUENCE: pass 0 as ped inside the sequence, capture the return as the sequence ID, then OPEN→CLOSE→PERFORM→CLEAR

Context

You want a ped to chain tasks — walk to coords, play anim A, then anim B, then clear. Calling TaskGoToCoord / TaskPlayAnim etc. sequentially makes each one cancel the previous, so the naive serial-call approach doesn't work. OPEN_SEQUENCE_TASK / TASK_PERFORM_SEQUENCE is the right primitive, but every part of how to call it is non-obvious — the native docs only show the C signature.

The full pattern

local sequence = OpenSequenceTask(math.random(1000)) -- ANY int. Return value IS the sequence ID.
TaskFollowNavMeshToCoord(0, x, y, z, 1.0, -1, 1.0, 4, heading) -- ped = 0, NOT PlayerPedId()
TaskPlayAnim(0, dict, "enter", 1.0, 8.0, -1, 0, 0.0, false, false, false)
TaskPlayAnim(0, dict, "base",  1.0, 8.0, -1, 0, 0.0, false, false, false)
CloseSequenceTask(sequence)
TaskPerformSequence(actualPed, sequence) -- NOW the real ped
ClearSequenceTask(sequence)              -- always clear, sequence handle pool is small

while GetSequenceProgress(actualPed) ~= -1 do Wait(100) end -- -1 == finished

The non-obvious bits

1. Ped arg inside the sequence is 0, not the real ped

While the sequence is open, all task natives that take a ped (TaskFollowNavMeshToCoord, TaskPlayAnim, TaskPickupCarriableEntity, TaskGoToCoordAnyMeans, TaskTurnPedToFaceCoord …) get 0 as the ped. The 0 means "whichever ped this sequence is eventually performed on." Pass PlayerPedId() here and the task fires immediately on the player AND fails to add to the sequence — sequence ends up empty.

2. The int* parameter is a write-back, not an input

OPEN_SEQUENCE_TASK(int* taskSequenceId) — the C signature is a pointer the native writes the new ID into. In Lua you pass any int (math.random(1000) is the convention, 0 works too); the bridge captures the written-back value and returns it. Always capture the return value as the sequence ID — it has nothing to do with what you passed.

3. Order: open → tasks → close → perform → clear

  • Add tasks only between OpenSequenceTask and CloseSequenceTask.
  • TaskPerformSequence only works after CloseSequenceTask.
  • ClearSequenceTask releases the slot. Without it the sequence pool fills up and future OpenSequenceTask returns garbage. Safe to clear right after TaskPerformSequence — the task data is copied to the ped, the sequence keeps running.

4. Tracking completion: GetSequenceProgress(ped) == -1 means done

While running, GetSequenceProgress(ped) returns the current task index (0, 1, 2, …). On completion it returns -1. Also useful for hooking phase changes — kd_stable's saddle cutscene swaps cameras and props when progress advances to specific indices.

5. Two synchronized peds → two sequences

For coordinated multi-entity animations (e.g. man fitting horseshoes on a horse), open two sequences, fill each with the matching track, close both, then call TaskPerformSequence(ped1, seq1) and TaskPerformSequence(ped2, seq2) back-to-back — they start in the same frame and stay in lockstep. (Used in kd_stable sceneChangeHorseShoes.)

What does NOT work

  • TaskPerformSequence before CloseSequenceTask → silently does nothing.
  • Real ped instead of 0 inside sequence → tasks fire immediately, sequence empty.
  • Skipping ClearSequenceTask → leak the sequence handle.
  • Mixing tasks for two peds in one sequence → only the first ped's tasks bind.

Bonus: not networked

Sequences and the tasks within them are local-only. To run a sequence on a remote-owned ped, NetworkRequestControlOfEntity first and wait for ownership before calling TaskPerformSequence.


Tags: task, sequence, ai, animation, ped, coordination
Category: natives
Source: research-agent-rdr2
Created: 2026-05-01T11:59:11.744Z

Back to documentation