Skip to content

The Dance clips.lua Table ​

clips.lua is the only Lua file in this pack you can read, and it is pure data.

escrow_ignore in the manifest lists five entries — clips.lua, README.md, CHANGELOG.md, LICENSE.md and docs/*.md. The last is a glob over three guides, so seven files in the download stay plain, readable text. clips.lua is the only Lua file among them. fxmanifest.lua is readable as a matter of course. Everything else is protected: server.lua and the eight .ycd files in stream/.

Rows27
Live entries27
Commented out0
Global it setsConfig.Clips
Loaded asA client script
SizeAbout 9 KB

The whole file is a header comment, two statements and a table:

lua
Config = Config or {}

Config.Clips = {
    -- 27 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.

The header comment is accurate

The comment block at the top of clips.lua documents the field names, says plainly that the resource "only streams the animations: it has no menu, command or exports", and gives the correct thirteen-argument TaskPlayAnim call. It matches the rows and it matches the build. Read it; it is not stale.

The three guides in docs/ are the same. The only defect is an unfilled [DISCORD LINK] placeholder in the customer guide — the real support link is discord.gg/w4Ew4k3Exn.

What reads it ​

Nothing.

The download is fxmanifest.lua, clips.lua, server.lua, stream/, three Markdown guides and three licence and changelog files. server.lua is one line:

lua
print('^2[redmorrow_dance_24]^7 Dance animation pack started.')

There is no client.lua, no config.lua and no html/ folder, which means there is no menu, no chat command, no key binding, no event and no export that consults the table. Every field below is metadata waiting for code you write, or for the emote menu you already run. Playing a dance covers the native call; Emote menus covers wiring the names into a menu.

One entry ​

Every row is a single-line table, flat, with the same nine keys in the same order:

lua
{ id = 'samba_dancing', label = 'Samba Dancing', dict = 'redmorrow_dance_24@dance', clip = 'samba_dancing',
  category = 'dance', duration = 18.2, mode = 'loop', rootMotion = false,
  bodies = { male = 'redmorrow_dance_24@dance', female = 'redmorrow_dance_24@dance@f' } },

Config.Clips is a plain array, so #Config.Clips is 27 and ipairs walks it in file order. Rows 1 to 3 are the three bonus dances and rows 4 to 27 are the 24 main ones, in the order the dance list numbers them. The order is not alphabetical and the two halves are not separated by anything you can test for except the dictionary name. Do not rely on position; look rows up by id.

Fields ​

FieldTypeMeansRead by anything?
idstringThe key you look a dance up by. Unique across the tableNo
labelstringA display name for a menu or listNo
dictstringThe animation dictionary — the .ycd filename without the extension. Always the male versionThis 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' in every row hereNo
rootMotionbooleantrue would mean the clip moves the character. false in every rowNo
bodiestable{ male = dict, female = dict .. '@f' }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 27 rows ​

FieldValue
mode'loop'There are no 'once' and no 'hold' entries
rootMotionfalseNo dance moves the character off its mark
bodiesboth keys presentmale equals dict, female equals dict plus @f, in every row

Fields the header documents and no row carries ​

FieldDescribed as
walktrue = upper body only, so the player keeps walkingNot present on any row
flagsRaw RDR3 TaskPlayAnim flags; overrides mode and walkNot present on any row
blendInBlend-in delta for full-body play, 2.0 being half a secondNot present on any row

All three are legal to add if you write code that honours them. Nothing in the pack will. The mapping the header intends is the one in Playing a dance: mode 'loop' is flags 1, 'hold' is 2, 'once' is 0, and walk = true adds 8 + 16.

duration ​

A number the pack author wrote down, not something measured from the .ycd at runtime. The spread runs from 0.5 s to 27.867 s with a median of 8.833 s, and the 27 clips come to 299.3 s in total — the per-row figures are in the dance list.

Trust the game over the field

If a dance plays longer or shorter than duration claims, the field is wrong, not the clip. GET_ANIM_DURATION returns the real length. breakdance_1990_2 is the one to look at: it reads 0.5, the only entry under a second, and looped that is a fast repeating twitch rather than a dance.

id and the one name that does not match its clip ​

Twenty-six of the 27 rows have id equal to clip. One does not:

iddictclip
jazz_dancing_2dnac@jazz_dancingjazz_dancing
jazz_dancingredmorrow_dance_24@dancejazz_dancing

Those are two different dances that happen to share a clip name across two dictionaries. The bonus one was given the _2 suffix to keep the table addressable, which is why it is also the only row whose id and clip differ.

Key your lookups on id, or on dictionary and clip together

A menu or a database keyed on the clip name alone collides on jazz_dancing and will play whichever of the two it indexed last. id is unique across all 27 rows; the pair dict plus clip is unique too. Either is safe. The clip name on its own is not.

What you may change ​

Safe to change ​

FieldWhy it is safe
labelA display string. Nothing matches on it
categoryA grouping string. Nothing matches on it
modeAdvisory only — your own code decides what it means
walkNot present on any row; add it and honour it yourself
durationAdvisory only. Nothing reads it
idSafe, as long as it stays unique and you update your own references

The labels in this pack are already readable — Samba Dancing (4), Northern Soul Floor Spin — so the highest-value edit is the category field, covered below. You can also add fields of your own: a price, a job restriction, a rank gate. 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

An RDR2 dictionary stores its own name. dict is therefore not a filename you are free to rename — it is a lookup key that has to agree with the asset, and renaming the file in stream/ breaks it just as surely as editing the string here.

Both mistakes fail silently

A wrong dict means the dictionary never loads. A wrong clip means it loads and nothing plays. Neither raises an error, in the server console or in F8. The character simply stands there. Put a timeout on your HasAnimDictLoaded wait and print your own line — see Playing a dance — because that line is the only warning you will get.

stream/ is protected by escrow in any case, so the .ycd files cannot be edited or renamed on disk. The risk is entirely in this file.

The bodies table ​

Every dance ships in two versions, one fitted to the male body and one to the female body, and bodies is where the pair is written down:

lua
bodies = { male = 'redmorrow_dance_24@dance', female = 'redmorrow_dance_24@dance@f' }

The invariant holds across all 27 rows without exception:

bodies.malealways equals the row's own dict
bodies.femalealways equals dict with @f appended
Clip nameidentical in both dictionaries

That is true of the 24 main dances, which share redmorrow_dance_24@dance, and of the three bonus dances, which each carry their own pair — dnac@chicken_dance and dnac@chicken_dance@f, and so on. The full mapping is in the dance list.

So dict .. '@f' is a correct rule here, for every row. Reading bodies.female is still the better habit: it costs nothing, it is what a future pack with a different naming scheme would carry, and it means the choice of body lives in one place in your code.

dict alone is the male version

A row's dict is the male dictionary, not a body-neutral one. If you ignore bodies entirely and pass dict to RequestAnimDict, every character on your server dances on the male skeleton. Both versions play on either body — the mismatched one just reads badly. Picking between them needs GET_META_PED_TYPE, which returns a number, and the comparison has a trap in it: see Playing a dance.

Reading the table from another resource ​

Config is a Lua global, and CFX gives every resource its own isolated Lua state. A global set inside rm_dance_24 exists only inside rm_dance_24. 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 rows into your own resource ​

The reliable option, and the one to pick if you are feeding an emote system, a job script or a saloon scene. Read the values out of clips.lua or out of the dance list and write them into your own file:

lua
local Dances = {
    { id = 'samba_dancing',  dict = 'redmorrow_dance_24@dance', clip = 'samba_dancing'  },
    { id = 'house_dancing',  dict = 'redmorrow_dance_24@dance', clip = 'house_dancing'  },
    { id = 'chicken_dance',  dict = 'dnac@chicken_dance',       clip = 'chicken_dance'  },
    { id = 'jazz_dancing_2', dict = 'dnac@jazz_dancing',        clip = 'jazz_dancing'   },
}

The pack still has to be started for the dictionaries to exist in the stream. Copying the table copies the names, not the animations.

2. Load clips.lua into a sandbox ​

Because clips.lua is listed in escrow_ignore, it is still a readable file on disk after escrow, which means LoadResourceFile can fetch its text and load can run it in an environment you control. The pack's own developer guide shows this pattern:

lua
-- Returns rm_dance_24's Config.Clips, or an empty table.
local function getDanceClips()
    local src = LoadResourceFile('rm_dance_24', 'clips.lua')
    if not src then return {} end
    local env = { Config = {} }
    local chunk = load(src, '@rm_dance_24/clips.lua', 't', env)
    if not chunk or not pcall(chunk) then return {} end
    return env.Config.Clips or {}
end

for _, c in ipairs(getDanceClips()) do
    print(c.id, c.label, c.category, c.duration)
end

The env table is the chunk's entire world. Seeding it with Config = {} is what the Config = Config or {} line at the top of the file expects, and reading env.Config.Clips afterwards keeps the 27 rows out of your own globals. LoadResourceFile works on the client and on the server, so you can build the list on either side.

This works only because the file is data. A chunk loaded into an empty environment has no natives, no print and no require, so a file that called anything would error — and pcall would hand you an empty table. clips.lua assigns a literal and stops, which is exactly the shape the sandbox needs.

The first argument is the folder name

LoadResourceFile('rm_dance_24', ...) takes the resource name, and in CFX the resource name is the directory name on disk. The manifest's name field is redmorrow_dance_24 and that is metadata — it is why the startup line reads [redmorrow_dance_24] while the folder is rm_dance_24.

The same applies to server.cfg: the line is ensure rm_dance_24, matching the folder. If your server renamed the folder, change both. Installation has the detail.

Build an index if you are going to look rows up more than once:

lua
local byId = {}
for _, c in ipairs(getDanceClips()) do
    byId[c.id] = c
end

local entry = byId['northern_soul_spin_combo']
-- entry.bodies.female == 'redmorrow_dance_24@dance@f'

Index on id, not on clip — see the one name that does not match its clip.

Which option to take

Copying the rows survives updates untouched and is the right default. The sandbox loader is worth it when you want the pack to stay the single source of truth, or when you are generating a menu rather than hand-writing one. Re-check it after an update, because a future version could change the file's shape; copied rows cannot break underneath you.

The category field is lopsided ​

The shipped grouping is uneven, and the capitalisation is inconsistent with it:

CategoryRows
dance24
Chicken Dance1
Jazz Dancing1
Dancing Twerk1

A menu built straight from category gets one group of 24 and three groups of exactly one, each named after the dance inside it. That is not useful navigation. The three bonus dances carry their own name as their category because they came from their own dictionaries, not because they belong apart.

Nothing reads the field, so rewriting it is free. Group by the thing your players will look for:

lua
{ id = 'samba_dancing_1',  category = 'Samba' },
{ id = 'northern_soul_spin', category = 'Northern Soul' },
{ id = 'chicken_dance',    category = 'Silly' },
{ id = 'dancing_twerk',    category = 'Silly' },

The ids divide cleanly already: five Samba entries, four Northern Soul, four numbered Dancing, three Hip Hop Dancing, two Jazz Dancing, and nine one-offs. The shipped grouping, with its counts, is in the dance list.

Pick one capitalisation while you are in there. Mixing dance with Chicken Dance shows up in a sorted menu as two different conventions.

After an edit ​

  1. Save clips.lua and restart the resource. It is a client script read at load, so a refresh on its own is not enough:

    cfg
    restart rm_dance_24
  2. Watch the console. The pack prints one line and that line is the whole of its server-side behaviour:

    text
    [redmorrow_dance_24] Dance animation pack started.
  3. Restart whatever consumes the list. If you copied rows into an emote menu or your own script, that resource needs restarting too, and if you edited its config, after its own rules.

  4. Play one dance in game. A silent failure looks identical to no failure, so look at the character rather than at the console.

A syntax error takes the whole resource down

clips.lua is the only client script, and a missing comma or an unclosed brace stops the resource from starting at all. The /assetpacks entitlement check runs before any Lua, so a licence-key problem and a Lua problem can look alike in the console. Troubleshooting separates the two.

Your emote menu keeps its own list

Editing clips.lua changes this pack's reference list and nothing else. It does not change your emote menu, your database or the copy you pasted into your own resource — those hold their own names, and a label you rename here will not appear in front of players until you rename it there as well. Emote menus and, for the database-backed route, RedM Emotes.

Keep a copy of your edited file outside the folder. Re-downloading the resource from the Cfx.re portal replaces clips.lua with the shipped version.

See also ​

Page
Dance listAll 27 dances, with dictionary, clip and length
Playing a danceThe thirteen-argument native call, start to stop
Emote menusPutting the names in the menu you already run
InstallationThe ensure line, the folder name, the licence-key check
TroubleshootingSymptom, cause, fix
FAQShort answers, including what the pack does not do
Animation BasicsHow RedM animations work in general
RedMWhere RedM differs from FiveM, flags included
100+ Female PosesThe sibling pack — also assets-only, female only
30+ Female PosesThe sibling pack that does ship a menu and exports

Still stuck? discord.gg/w4Ew4k3Exn.

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