Skip to content

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.

Rows105
Live entries99
Commented out6
Global it setsConfig.Clips
Loaded asA client script
SizeAbout 23 KB

The whole file is two statements and a table:

lua
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:

lua
{ 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 ​

FieldTypeMeansRead by anything?
idstringThe key you look a pose up by. Unique across the tableNo
labelstringA display name for a menu or listNo
dictstringThe animation dictionary — the .ycd filename without the extensionThis is the value you pass to RequestAnimDict
clipstringThe clip name inside that dictionaryThis is the value you pass to TaskPlayAnim
categorystringA grouping key for a menuNo
durationnumberLength in secondsNo
modestringSuggested playback: 'loop' here, in every rowNo
rootMotionbooleantrue would mean the clip moves the character. false in every rowNo
bodiestable{ female = dict }. The dictionary for each body typeNo

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 ​

FieldValue
mode'loop'There are no 'once' and no 'hold' entries
rootMotionfalseNo pose moves the character off its mark
bodies{ female = ... } onlyNo 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
walkDescribed as upper-body-only. Not present on any row
flagsDescribed 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:

iddictclip
pose47redmorrow@pose46@fpose47
pose47_pose47redmorrow@pose47@fpose47

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:

RowIts dictionaryDuration in the row
pose78redmorrow@pose78@f0.033 s
pose87redmorrow@pose87@f1.933 s
pose93redmorrow@pose93@f1.967 s
redmorrow_p23redmorrow@redmorrow_p3@f1.967 s
pose29redmorrow@pose26@f3.7 s
pose35redmorrow@pose35@f9 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.

lua
--{ 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

lua
{ 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:

cfg
restart rm_100_female_pose

Re-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:

FileBelongs 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.

lua
-- 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:

lua
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:

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:

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:

lua
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 ​

FieldWhy it is safe
labelA display string. Nothing matches on it
categoryA grouping string. Nothing matches on it
modeAdvisory only in this build — your own code decides what it means
durationAdvisory only. Nothing reads it
idSafe, 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:

lua
{ 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 ​

FieldWhy
dictMust match the name baked inside the .ycd file
clipMust 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 ​

cfg
restart rm_100_female_pose

clips.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 listAll 99 rows, with dictionary, clip and duration
Playing a poseThe native call, start to stop
Integrating with other resourcesEmote systems, menus, your own scripts
TroubleshootingSymptom, cause, fix
Animation BasicsHow RedM animations work in general
30+ Female PosesThe sibling pack, which does ship a menu, a command and exports

Documentation for RedMorrow. Scripts are licensed per server — redistribution is not permitted.