Playing an Everyday Animation
This pack streams 38 .ycd dictionaries into the game and prints one line in the server console. That is the whole of its behaviour. There is no client script in the download, so nothing requests a dictionary and nothing calls TaskPlayAnim until your own code does.
Writing that code is the job of this page.
What you are driving
Every animation is two strings — a dictionary and a clip name — and both come out of the animation list.
| Animations | 94 |
| Clips | 188 — a male and a female version of each |
| Dictionaries | 19, each shipping as a pair |
.ycd files | 38, all present |
| Root-motion clips | 4 |
| Single-frame poses | 5 |
clips.lua sets a global table of those pairs and contains no functions. Nothing in the pack reads it, so it is a reference list, not a runtime. Your code supplies the two strings.
Where to get the two strings
Take them from the animation list. All 94 are playable today because all 38 .ycd files ship and every clip name resolves inside them.
If you copy rows straight out of clips.lua instead, read The clips.lua file first: only the 15 greetings are uncommented, and the table assigns rather than appends, so uncommenting the other blocks discards all but the last one.
The minimal working script
Three steps in this order, every time: ask for the dictionary, wait until the game confirms it is loaded, then start the task.
-- client.lua in your own resource
local function loadDict(dict)
if not DoesAnimDictExist(dict) then
print(('[everyday] no such dictionary: %s'):format(dict))
return false
end
RequestAnimDict(dict)
local deadline = GetGameTimer() + 5000
while not HasAnimDictLoaded(dict) do
if GetGameTimer() > deadline then
print(('[everyday] dictionary never loaded: %s'):format(dict))
return false
end
Wait(0)
end
return true
end
local function playAnim(dict, clip)
if not loadDict(dict) then
return false
end
TaskPlayAnim(PlayerPedId(), dict, clip, 4.0, -4.0, -1, 1, 0.0, false, 0, false, 0, false)
return true
end
local function stopAnim()
local ped = PlayerPedId()
ClearPedSecondaryTask(ped)
ClearPedTasks(ped)
endplayAnim('rm@waving', 'waving') waves. stopAnim() ends it.
The DoesAnimDictExist guard at the top is what turns a typo, or a pack that is not started, into a line in the F8 console instead of a character standing still. The deadline does the same for a dictionary that exists but never finishes loading.
The pack must be started for any of this to work — the dictionaries do not exist in the game otherwise, and the ensure line has to match the folder name on disk. See Installation.
The thirteen arguments
RDR3's TaskPlayAnim takes thirteen arguments.
| # | Argument | Above | What it does |
|---|---|---|---|
| 1 | ped | PlayerPedId() | Who plays it |
| 2 | dict | from the animation list | The dictionary |
| 3 | clip | from the animation list | The clip name inside it |
| 4 | blendIn | 4.0 | How fast the animation blends on. Higher is snappier |
| 5 | blendOut | -4.0 | How fast it blends off at the end. Negative is the normal form |
| 6 | duration | -1 | Milliseconds, or -1 for no time limit |
| 7 | flags | 1 | The flag word |
| 8 | startPhase | 0.0 | Where in the clip to begin. 0.0 is the first frame |
| 9 | — | false | Leave it |
| 10 | ikFlags | 0 | Leave it |
| 11 | — | false | Leave it |
| 12 | taskFilter | 0 | Leave it |
| 13 | — | false | Leave it |
Four of those are worth understanding and the rest you can copy as written.
Blend in, argument 4. 4.0 is a quick, clean transition into the animation. Lower it to around 2.0 for a slower ease, which reads better on anything that starts seated or kneeling — see the nine clips that want a slower blend-in.
Duration, argument 6. -1 means no time limit: the task runs until something stops it or the clip ends under its own flag behaviour. A positive number is a millisecond count, so 5000 holds for five seconds and then releases the character. The lengths in the animation list are metadata the author wrote into clips.lua; they are not what you pass here. GET_ANIM_DURATION gives the game's own figure if you need it.
The flag word, argument 7. 1 is the known-good starting point. Use it first, confirm the animation plays, and only then try variants — and verify each one in game rather than trusting a number from a snippet.
RDR3 flag values are not GTA V's
A flag copied out of a FiveM resource does something other than what you expect here, and what it does is usually nothing visible. If you need behaviour beyond the call above, read the values from the RedM platform notes.
Why the wait is not optional
A clip requested from a dictionary that has not loaded fails silently. No console error, no warning, no T-pose. The character stands there, and your code cannot tell that anything went wrong, because TaskPlayAnim returns nothing either way.
This is cause number one of "it does nothing".
It is also why the loop above has a deadline. A bare while not HasAnimDictLoaded(dict) do Wait(0) end spins forever on a misspelled dictionary, and a thread stuck in that loop looks exactly like the silent failure it was supposed to catch.
When one specific animation refuses to come up, check the dictionary name first:
print(DoesAnimDictExist('rm@waving'))false means the name is wrong or the resource is not started. true with a motionless character means the dictionary loaded and the clip name is wrong. More symptoms in Troubleshooting.
Your licence key needs asset packs enabled
The manifest declares dependency '/assetpacks', a Cfx.re licence-key entitlement check. Without it the resource starts but nothing streams, so every dictionary is missing and every play fails in exactly the silent way described above. This is the most common cause of a menu that works while nothing happens on screen.
Choosing the male or female version
Each animation ships twice, and the rule holds on all 94 rows with no exceptions:
male → dict
female → dict .. '@f'The clip name inside both dictionaries is identical. So picking a body is a dictionary swap and nothing else changes:
local function dictFor(dict, female)
if female then
return dict .. '@f'
end
return dict
end-- same clip name, either body
playAnim(dictFor('rm@salute', false), 'salute') -- rm@salute
playAnim(dictFor('rm@salute', true), 'salute') -- rm@salute@fThe pack does not choose for you. It streams both versions and lists both names; which one a given character plays is your decision, made in your code.
Prefer your own character-sex field over IsPedMale
IsPedMale(PlayerPedId()) is reliable on standard story and multiplayer peds, and it can be wrong on heavily customised ones — which, on a roleplay server, is most of them.
If your framework already stores a sex on the character, read that instead and pass it to dictFor. Fall back to IsPedMale only where you have nothing better.
Playing the wrong body's dictionary does not error. The skeletons are close enough that the animation plays; it reads as the other body's performance on your character, which is usually not what you want. See every dictionary ships twice for the full pairing.
The five single-frame poses
Five rows are 0.033 s long — one frame at 30 fps. They are poses, not animations:
male_sitting_pose · female_standing_pose · hand_in_back · cross_legged_sitting · leaning_against_wall
Played as a one-shot, a one-frame clip is over before the blend finishes and you see nothing at all. The character flickers and returns to idle, which looks identical to a failed play.
They need the task to hold the last frame. Keep duration at -1 and use a flag word that loops or holds, starting from 1; a single frame on loop is a static pose held indefinitely, which is the point of them. Then stop it deliberately when the player is done.
They are also the worst case for a fast blend-in, because there is nothing to blend toward — a slower blendIn of 2.0 settles into them far better than 4.0. Listed at single-frame poses.
The four root-motion clips
Four clips move the character off its starting spot:
a_man_stands_on_the_left_leg_in_a_yoga_tree_pose_with_the_hands · a_woman_stands_holding_a_map_open_in_both_hands_and_looks_aroun · look_binoculars_animation · whiteboard_animation
Two things follow. They want space — a metre or two of clearance, because a character that drifts into a wall or a hitching post ends the animation clipped into it. And they have to play full body: a flag word that restricts the animation to the upper body discards the movement the clip was built around, so it reads as a broken version of itself.
The other 90 rows carry rootMotion = false and leave the character where it stood, which makes them safe in tight interiors and on a photo mark. The four are listed at root-motion clips.
Nine clips that want a slower blend-in
Nine rows record blendIn = 2.0 instead of the default, which the author's note in clips.lua reads as roughly half a second:
sitting_yell · standing_clap · male_sitting_pose · finger_tapping · cross_legged_sitting · kneeling_idle · sitting_ground · sleeping_idle · sleeping
Every one of them starts seated, kneeling or lying down. Entered at 4.0, the character snaps from standing to the floor in a couple of frames. At 2.0 it eases down.
Nothing in the pack applies that number for you — it is a recommendation stored next to the clip. Pass it yourself as argument 4:
TaskPlayAnim(ped, dict, clip, 2.0, -4.0, -1, 1, 0.0, false, 0, false, 0, false)
-- ^^^ slower ease for the nine seated and kneeling clipsA practical way to carry this is a per-animation blend value in your own table, defaulting to 4.0, which is what the worked example below does.
Stopping
ClearPedTasks(PlayerPedId())That cancels what the character is doing and returns it to idle. It is the stop you want for a full-body play, which is what the call on this page performs.
If you use a flag word that puts the animation in the ped's secondary task slot — the slot an upper-body gesture plays in while the character keeps walking — ClearPedTasks is not guaranteed to reach it. Clear both:
local ped = PlayerPedId()
ClearPedSecondaryTask(ped)
ClearPedTasks(ped)Calling both is harmless when only one applies, which makes it the right default for a stop command. Nothing in the shipped data expresses upper-body play, so unless you choose those flags yourself, ClearPedTasks alone is enough.
Another script's animation will clear yours
Starting an animation clears the character's tasks. If an emote resource is also running, its next play wipes yours and yours wipes its emote. Stop the other one deliberately rather than letting the two fight — Integrating with an emote system covers sharing a character between scripts.
Releasing the dictionary
RemoveAnimDict(dict)This tells the game you no longer need the dictionary resident. It does not cut a running animation, so it is safe to call once playback has started — the task already holds what it needs.
Call it after TaskPlayAnim, never between RequestAnimDict and the task. Dropping it in before the clip starts is a reliable way to produce the silent failure above.
With 19 dictionaries and a pair of files for each, requesting a handful over a long session and never releasing them is not a crisis, but releasing is tidier, and it matters on a menu that lets players run through all 94.
A worked example
A /everyday command, in your own resource — the pack provides no command, so this is code you write and own. It builds on loadDict, playAnim and stopAnim from the top of this page.
-- client.lua in your own resource
local Anims = {
wave = { dict = 'rm@waving', clip = 'waving' },
salute = { dict = 'rm@salute', clip = 'salute' },
bow = { dict = 'rm@quick_formal_bow', clip = 'quick_formal_bow' },
thumbs = { dict = 'rm@standing_thumbs_up', clip = 'standing_thumbs_up' },
clap = { dict = 'rm@communication_gestures', clip = 'standing_clap', blendIn = 2.0 },
sleep = { dict = 'rm@casual_actions', clip = 'sleeping', blendIn = 2.0 },
}
RegisterCommand('everyday', function(_, args)
local id = args[1]
if not id or id == 'stop' then
stopAnim()
return
end
local anim = Anims[id]
if not anim then
print(('[everyday] no such animation: %s'):format(id))
return
end
local ped = PlayerPedId()
local female = not IsPedMale(ped) -- your character-sex field is better
local dict = dictFor(anim.dict, female)
if not loadDict(dict) then
return
end
TaskPlayAnim(ped, dict, anim.clip, anim.blendIn or 4.0, -4.0, -1, 1, 0.0, false, 0, false, 0, false)
end, false)/everyday wave plays it, /everyday stop clears it, /everyday nonsense says so in the F8 console instead of failing quietly. The blendIn key is set only on the two rows that need the slower ease and defaults to 4.0 everywhere else.
Six animations are listed there and the pack has 94. Add the rows you want from the animation list rather than typing names from memory — and key your table on the dictionary and clip together, because pointing and pointing_gesture each name two different clips in two different dictionaries. See duplicate ids.
Next
- The animation list — the dictionary and clip name for all 94
- The clips.lua file — what the shipped table contains and what you may change
- Integrating with an emote system — handing the pairs to a menu instead of maintaining a command
- Troubleshooting — when nothing plays and nothing is logged