The clips.lua Table
clips.lua is the only Lua file in this pack you can read.
escrow_ignore in the manifest covers two files — clips.lua and README.md — so after escrow the table ships as plain, editable text. The .ycd files in stream/ are assets rather than Lua, so escrow never applied to them in the first place; they are the animation data itself and cannot be opened or altered either way.
| Rows | 105 |
| Live entries | 99 |
| Commented out | 6 |
| Global it sets | Config.Clips |
| Loaded as | A client script |
| Size | About 23 KB |
The whole file is two statements and a table:
Config = Config or {}
Config.Clips = {
-- 105 rows
}That is all it does. It declares no functions, registers no events, starts no threads and exports nothing. It is a list of names, and the names are its entire value.
What reads it
Nothing.
This build of the pack contains clips.lua, server.lua, fxmanifest.lua, README.md and stream/. server.lua is a single print line. There is no client.lua, no config.lua and no html/ folder, which means there is no menu, no command, no key binding and no export that consults the table. Every field below is metadata waiting for code you write.
The header comment in clips.lua describes a build you do not have
The comment block at the top of the file — and the resource's own README.md — document an in-game menu, a /redmorrow command, exports['redmorrow']:Play() and :GetClips(), a Config.ForceBody option, and files named config.lua, client.lua and html/. None of those exist in this download. The documents were written against a different build and were not trimmed back.
Take the field names from the header as accurate, because they match the rows. Ignore everything it says about playing a pose. Playing a pose covers what actually works here.
One entry
Every live row is a single-line table, flat, with the same nine keys in the same order:
{ id = 'pose67', label = 'pose67', dict = 'redmorrow@pose67@f', clip = 'pose67',
category = 'pose67', duration = 89, mode = 'loop', rootMotion = false,
bodies = { female = 'redmorrow@pose67@f' } },Config.Clips is a plain array, so #Config.Clips is 99 and ipairs walks it in file order. The order is not alphabetical and not numeric — the file opens at pose67 and wraps round to pose66 at the end. Do not rely on position; look rows up by id.
Fields
| Field | Type | Means | Read by anything? |
|---|---|---|---|
id | string | The key you look a pose up by. Unique across the table | No |
label | string | A display name for a menu or list | No |
dict | string | The animation dictionary — the .ycd filename without the extension | This is the value you pass to RequestAnimDict |
clip | string | The clip name inside that dictionary | This is the value you pass to TaskPlayAnim |
category | string | A grouping key for a menu | No |
duration | number | Length in seconds | No |
mode | string | Suggested playback: 'loop' here, in every row | No |
rootMotion | boolean | true would mean the clip moves the character. false in every row | No |
bodies | table | { female = dict }. The dictionary for each body type | No |
Only dict and clip have any mechanical meaning, and that meaning comes from the .ycd files, not from this table. The other seven are labels for whatever interface you build.
Values that are the same in all 99 rows
| Field | Value | |
|---|---|---|
mode | 'loop' | There are no 'once' and no 'hold' entries |
rootMotion | false | No pose moves the character off its mark |
bodies | { female = ... } only | No male key appears anywhere in the pack |
bodies.female always repeats the row's own dict, including for the seven rows whose dictionary does not match their id. It carries no information this build uses, but it is the field a male companion pack would extend, so leave it in place.
Fields the header documents and no row carries
| Field | |
|---|---|
walk | Described as upper-body-only. Not present on any row |
flags | Described as raw RDR3 TaskPlayAnim flags. Not present on any row |
Both are legal to add if you write code that honours them. Nothing in the pack will.
duration
A number the pack author wrote down, not something measured from the .ycd. The spread across the 99 live rows is listed in the animation list. Eight rows read 0.033 — one frame at 30 fps, a static pose, which is what a pose pack wants once the clip is looped.
Trust the game over the field
If a pose plays longer or shorter than duration claims, the field is wrong, not the clip. GET_ANIM_DURATION returns the real length.
id and the one deliberate duplicate
Ids are unique, which is why one of them looks odd. Two rows both play a clip named pose47:
id | dict | clip |
|---|---|---|
pose47 | redmorrow@pose46@f | pose47 |
pose47_pose47 | redmorrow@pose47@f | pose47 |
They are two different animations that happen to share a clip name across two dictionaries. The second id was suffixed to keep the table addressable. Both are live and both play.
The six commented-out rows
Six of the 105 rows are commented out and never load:
| Row | Its dictionary | Duration in the row |
|---|---|---|
pose78 | redmorrow@pose78@f | 0.033 s |
pose87 | redmorrow@pose87@f | 1.933 s |
pose93 | redmorrow@pose93@f | 1.967 s |
redmorrow_p23 | redmorrow@redmorrow_p3@f | 1.967 s |
pose29 | redmorrow@pose26@f | 3.7 s |
pose35 | redmorrow@pose35@f | 9 s |
105 rows minus these six leaves the 99 in the animation list. The product is named "100+" and 99 play. That is the real number, and it is one short of the hundred the name promises.
Nothing is missing from your download. All six have their animation data present — five as their own .ycd file in stream/, and pose29 inside redmorrow@pose26@f, which the live pose26 row already uses. The rows were switched off in the file; the assets shipped anyway.
Re-enabling one
Delete the leading --. That is the whole operation.
--{ id = 'pose78', label = 'pose78', dict = 'redmorrow@pose78@f', clip = 'pose78', category = 'pose78', duration = 0.033, mode = 'loop', rootMotion = false, bodies = { female = 'redmorrow@pose78@f' } },becomes
{ id = 'pose78', label = 'pose78', dict = 'redmorrow@pose78@f', clip = 'pose78', category = 'pose78', duration = 0.033, mode = 'loop', rootMotion = false, bodies = { female = 'redmorrow@pose78@f' } },The -- is not always in the same column
The six rows are not commented out consistently. Two read --{ with no space, two read -- {, and two read -- { with the dashes pulled left out of the indent. Search for the id rather than for a fixed prefix, and remove only the two dashes — the leading whitespace does not matter to Lua.
Restart the resource afterwards, because clips.lua is a client script read at load:
restart rm_100_female_poseRe-enable one at a time and look at it in game
There is no record of why these six were switched off. The likeliest reasons are a pose that reads badly on a female skeleton or one that duplicates another row. The assets load and the game will play them, so judge each one on screen before you put it in front of players.
pose29 is the one to watch: it shares redmorrow@pose26@f with the live pose26 entry, so re-enabling it adds a second clip out of a dictionary that is already streamed — no new asset, no extra memory.
The five orphan dictionaries
stream/ holds 104 .ycd files. The 99 live rows reference 99 distinct dictionaries. Five files therefore belong to no live entry:
| File | Belongs to |
|---|---|
redmorrow@[email protected] | the commented-out pose35 |
redmorrow@[email protected] | the commented-out pose78 |
redmorrow@[email protected] | the commented-out pose87 |
redmorrow@[email protected] | the commented-out pose93 |
redmorrow@[email protected] | the commented-out redmorrow_p23 |
That accounts for all 104 files and all 105 rows, with nothing left over in either direction. Every live row has its dictionary on disk — zero missing. There is no sixth orphan because pose29 was never in a file of its own.
An unreferenced .ycd costs nothing at runtime. RedM streams a dictionary when something asks for it, so a file nobody names is a few kilobytes on disk and in the player's cache, and no more. All five together come to about 7 KB of the pack's 452 KB. Leave them in place; they are what makes re-enabling a row a one-character edit instead of a re-download.
Config.Clips is not visible to other resources
This is the part that surprises people.
Config is a Lua global, and CFX gives every resource its own isolated Lua state. A global set inside redmorrow exists only inside redmorrow. Your own resource can start this pack, depend on it and stream its animations, and still see Config.Clips as nil.
-- in your own resource: this is always nil
print(Config and Config.Clips)There is no export to ask through, because the pack registers none. And clips.lua is listed under client_scripts only, so even inside this resource the table exists on the client and not on the server — server.lua never sees it.
Two ways round it.
1. Copy the pairs into your own resource
The reliable option, and the one to pick if you are feeding an emote system, a job script or a photo-mode menu. Read the values out of clips.lua or out of the animation list and write them into your own file:
local Poses = {
{ id = 'pose67', dict = 'redmorrow@pose67@f', clip = 'pose67' },
{ id = 'lean', dict = 'redmorrow@lean@f', clip = 'lean' },
{ id = 'smoke', dict = 'redmorrow@smoke@f', clip = 'smoke' },
{ id = 'regard5', dict = 'redmorrow@regard6@f', clip = 'regard5' },
}Copy the pairs — do not build them from the id
'redmorrow@' .. id .. '@f' is correct for 92 of the 99 poses and wrong for seven. See the seven ids that do not match their dictionary. A wrong dictionary name fails without an error message.
The pack still has to be started for the dictionaries to exist in the stream. Copying the table copies the names, not the animations. Start rm_100_female_pose before or alongside whatever drives it — see Integrating with other resources and, for the database-backed route, RedM Emotes.
2. Add your own client script inside this resource
A script that lives in rm_100_female_pose shares its Lua state, so it can read Config.Clips directly. Create the file next to clips.lua:
-- play.lua
CreateThread(function()
print(('[redmorrow] %d poses in Config.Clips'):format(#Config.Clips))
end)and add it to client_scripts in fxmanifest.lua, after clips.lua:
client_scripts {
'clips.lua',
'play.lua',
}Order matters. client_scripts loads in the order listed, so a file placed before clips.lua runs while Config.Clips is still nil.
The Config = Config or {} line at the top of clips.lua is written so it will not clobber an existing Config table in the same state. If your added script also keeps settings on a global called Config, they coexist — but give your own keys distinct names so a future pack update that adds fields cannot collide with them.
An edit inside the pack is yours to carry forward
Files you add to rm_100_female_pose, and edits you make to its fxmanifest.lua, are overwritten when you re-download the resource. Keep your additions in version control, or outside the folder with a note, so an update does not quietly undo them.
If you would rather not maintain a patch, take option 1 instead. It survives updates untouched.
Either way, the playback code is the same thirteen-argument TaskPlayAnim call described in Playing a pose:
local dict, clip = 'redmorrow@pose67@f', 'pose67'
RequestAnimDict(dict)
local tries = 0
while not HasAnimDictLoaded(dict) and tries < 300 do
Wait(0)
tries = tries + 1
end
if not HasAnimDictLoaded(dict) then
print(('[redmorrow] dictionary never loaded: %s'):format(dict))
return
end
TaskPlayAnim(PlayerPedId(), dict, clip, 4.0, -4.0, -1, 1, 0.0, false, 0, false, 0, false)Duration -1 means no time limit, so with the loop flag the pose holds until something stops it. Stop with ClearPedTasks or ClearPedSecondaryTask.
Always put a timeout on the wait
A clip requested from a dictionary that never loaded fails silently. No console error, no warning — the character stands there. The bounded loop above is what turns that into a line you can read. RDR3's flag word is not GTA V's, so do not lift flag numbers out of a FiveM script; the platform differences are collected under RedM.
Editing safely
Safe to change
| Field | Why it is safe |
|---|---|
label | A display string. Nothing matches on it |
category | A grouping string. Nothing matches on it |
mode | Advisory only in this build — your own code decides what it means |
duration | Advisory only. Nothing reads it |
id | Safe, as long as it stays unique and you update your own references |
Giving the poses real labels and categories is the highest-value edit in the file. Shipped, every label and every category repeats the internal id, which tells a player nothing:
{ id = 'lean', label = 'Leaning on the rail', dict = 'redmorrow@lean@f', clip = 'lean',
category = 'Saloon', duration = 1.967, mode = 'loop', rootMotion = false,
bodies = { female = 'redmorrow@lean@f' } },You can also add fields of your own — a walk or a flags value, a price, a job restriction. The table is data; nothing validates its shape.
Never change
| Field | Why |
|---|---|
dict | Must match the name baked inside the .ycd file |
clip | Must match the clip name inside that dictionary |
RDR2 dictionaries store their own name, so dict is not a filename you are free to rename — it is a lookup key that has to agree with the asset. A wrong dict means the dictionary never loads. A wrong clip means it loads and nothing plays. Neither raises an error.
The seven mismatched pairs look like typos and are not
regard5 really does live in redmorrow@regard6@f. pose1 is in redmorrow@regard7@f, pose4 in redmorrow@regard8@f, hatsdown_clip in redmorrow@pose7@f, pose28 in redmorrow@pose27@f, pose47 in redmorrow@pose46@f and pose47_pose47 in redmorrow@pose47@f. "Correcting" any of them to match its id breaks that pose. The full list is in the animation list.
Keep bodies.female equal to dict. It is unused here, but every row holds that invariant and a male companion pack would read it.
After any edit
restart rm_100_female_poseclips.lua is a client script read at load. A refresh on its own is not enough, and the ensure line has to match the folder name on disk — see Installation.
A syntax error takes the whole resource down
clips.lua is the first client script loaded. A missing comma or an unclosed brace stops the resource from starting at all, and the asset-pack entitlement check runs before any of it, so a key problem and a Lua problem look alike in the console. Troubleshooting separates the two.
See also
| Page | |
|---|---|
| Animation list | All 99 rows, with dictionary, clip and duration |
| Playing a pose | The native call, start to stop |
| Integrating with other resources | Emote systems, menus, your own scripts |
| Troubleshooting | Symptom, cause, fix |
| Animation Basics | How RedM animations work in general |
| 30+ Female Poses | The sibling pack, which does ship a menu, a command and exports |