The 30+ Dances clips.lua Table
clips.lua lists all 34 dances with their dictionary, clip name, length and play mode. It is the file you read when you are wiring the pack into something, and it is the only Lua file in the download you can open.
It is also never loaded.
The manifest does not load it
fxmanifest.lua has no client_scripts block and no server_scripts block. Not an empty one — no block at all. The whole manifest is the fx_version / game / lua54 header, the name and version fields, an escrow_ignore list and dependency '/assetpacks'.
That single fact sets this pack apart from every other RedMorrow animation pack, and three consequences follow from it:
Config.Clips does not exist at runtime | Not on the client, not on the server, not inside redmorrow2 itself |
Clients never download clips.lua | It is not a client script, so it is not in the manifest's file list |
| The pack prints no startup line | There is no server.lua to print one |
stream/ is picked up automatically by CFX without needing a manifest entry, which is why a pack with no scripts of any kind still works. The animations stream, the dictionaries exist in game, and nothing anywhere consults this table.
So clips.lua is a reference document that happens to be valid Lua. Copy the names out of it, or load it from disk yourself — see Reading the table from your resource.
| Rows | 34 |
| Live entries | 34 |
| Commented out | 0 |
| Global it would set | Config.Clips |
| Loaded as | Nothing. The manifest has no script blocks |
| Size | About 11 KB |
What ships, and what is readable
escrow_ignore in the manifest lists four entries:
escrow_ignore {
'clips.lua',
'README.md',
'CHANGELOG.md',
'docs/*.md',
}The last is a glob over three guides, so six 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. The only protected files are the two .ycd dictionaries in stream/, which come to about 2.9 MB.
There is no server.lua, no client.lua, no config.lua and no html/ folder. There is also no LICENSE.md — what you may and may not do with the pack is covered in docs/CUSTOMER_GUIDE.md in your download, under its own heading.
The customer guide has one unfilled placeholder
docs/CUSTOMER_GUIDE.md lists its support contact as [DISCORD LINK]. The real link is discord.gg/w4Ew4k3Exn. Everything else in the three guides matches the rows and matches the build.
What reads it
Nothing, on either side.
The pack has no menu, no chat command, no key binding, no event, no export and no config. Players reach the dances through an emote menu your server already runs, or through code you write. Every field below is metadata waiting for that code. Scripting the pack covers the native call; Emote menus covers wiring the names into a menu you already have.
The resource's own three guides state this correctly. They are worth reading and they are not stale.
One row
Every entry is a single-line table, flat, with the same nine keys in the same order, plus a tenth, blendIn, on six of them:
{ id = 'moonwalk', label = 'Moonwalk', dict = 'redmorrow2@dance', clip = 'moonwalk',
category = 'dance', duration = 1.033, mode = 'loop', rootMotion = false,
bodies = { male = 'redmorrow2@dance', female = 'redmorrow2@dance@f' } },The whole file is a header comment, two statements and a table:
Config = Config or {}
Config.Clips = {
-- 34 rows
}It declares no functions, registers no events, starts no threads and exports nothing. It assigns a literal and stops.
Once you have loaded it yourself, Config.Clips is a plain array: #Config.Clips is 34 and ipairs walks it in file order. That order is neither alphabetical nor grouped — breakdance, hip hop and party rows are interleaved, and the first nine rows alone cover all three groups. Do not rely on position. Look rows up by id.
Fields
| Field | Type | Means | Read by anything? |
|---|---|---|---|
id | string | The key you look a dance 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. Always the male version | 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. 'dance' in every row | No |
duration | number | Length in seconds | No |
mode | string | Suggested playback: 'loop' in every row | No |
rootMotion | boolean | true would mean the clip moves the character. false in every row | No |
bodies | table | { male = dict, female = dict .. '@f' } | No |
walk | boolean, optional | true = upper body only, so the player keeps walking. Not present on any row | No |
flags | number, optional | Raw RDR3 TaskPlayAnim flags; overrides mode and walk. Not present on any row | No |
blendIn | number, optional | Blend-in delta for full-body play. Set to 2.0 on six rows | No |
Only dict and clip have any mechanical meaning, and that meaning comes from the .ycd files, not from this table. The rest are labels for whatever interface you build.
Values that are identical in all 34 rows
| Field | Value | |
|---|---|---|
mode | 'loop' | No 'once' and no 'hold' entries |
rootMotion | false | No dance moves the character off its mark, the moonwalk included |
category | 'dance' | One flat category across the whole pack |
dict | 'redmorrow2@dance' | The male dictionary, in every row |
bodies | both keys present | male equals dict, female equals dict plus @f |
id always equals clip
All 34 rows have id equal to clip. There are no duplicate ids, no duplicate clip names and no duplicate labels anywhere in the pack.
That means there is nothing to derive and nothing to disambiguate: the clip name is the id, and a menu or a database keyed on either one is safe. It is still worth keying on id as a habit, because that is the field the format promises to keep unique.
This differs from the sibling pack
24+ Dances ships three bonus dances out of their own dnac@ dictionaries and has a clip name that appears twice, so one of its rows carries an id that does not match its clip. This pack has no such case. Do not carry that pack's lookup caveats across.
blendIn is the one optional field this pack uses
Exactly six rows set blendIn = 2.0 — about half a second — instead of the 4.0 used everywhere else. All six start on the ground rather than standing, and a slower blend-in makes the character ease down instead of snapping to the floor:
id | Length |
|---|---|
crossleg_freeze | 2.033 s |
flair_1 | 1 s |
breakdance_footwork_1 | 3.167 s |
breakdance_footwork_3 | 4.567 s |
breakdance_footwork_to_freeze | 3.667 s |
breakdance_footwork_to_idle | 9.6 s |
All six are in the breakdance group. The same list, with the reasoning, is on Floor moves.
walk and flags are legal fields that the header comment documents, and no row in this pack carries either. Both are safe to add if you write code that honours them. Nothing in the pack will.
mode, and how it maps to flags
mode is advisory. The mapping the header comment intends is:
mode | Flags |
|---|---|
'loop' | 1 |
'hold' | 2 |
'once' | 0 |
With walk = true, add 8 + 16. The RDR3 eScriptedAnimFlags values are LOOPING 1, HOLD_LAST_FRAME 2, NOT_INTERRUPTABLE 4, UPPERBODY 8, SECONDARY 16 and ABORT_ON_PED_MOVEMENT 32. These are RDR3's values — the flag values differ from GTA V's, so a flag word copied out of a FiveM script will not mean the same thing here.
Upper-body play looks wrong on these dances
Looping upper-body so the player can still walk is 1 + 8 + 16 = 25, and it is a good trick on a wave or a salute. These are full-body moves, breakdance especially — the legs are most of the animation. Played at 25 the character walks around with its arms doing a freeze. Leave them at 1.
duration
A number the pack author wrote down, not something measured from the .ycd at runtime. Nothing reads the field — the manifest never loads the file — so the game is the authority and GET_ANIM_DURATION gives the real figure.
| Shortest | flair_1 at 1 s |
| Longest | bellydancing at 25.367 s |
| Median | 4.133 s |
| Total | 205.0 s across all 34 |
Four rows run under a second and a half — flair_1 at 1 s, moonwalk at 1.033 s, wave_hip_hop_dance_1 at 1.167 s and breakdance_ready at 1.433 s. Looped, those read as a repeating beat rather than as a routine with a start and an end. Per-row figures are on Lengths.
What you may change
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 — your own code decides what it means |
walk | Not present on any row; add it and honour it yourself |
blendIn | Advisory only. Your code reads it or it does nothing |
duration | Advisory only. Nothing reads it |
id | Safe, as long as it stays unique and you update your own references |
You can also add fields of your own: a price, a job restriction, a rank gate. The table is data and nothing validates its shape.
category is the obvious thing to rewrite
All 34 rows carry category = 'dance'. A menu built straight from that field gets one undifferentiated list of 34 entries, which is a long scroll for a player looking for the moonwalk.
The pack's own server owner guide splits the 34 into breakdance, hip hop and party, and that split is exact and complete — 20 + 6 + 8 = 34, every clip in exactly one group — but it exists only as prose in that guide, not as a field you can read. The three groups are listed in full on the dance list. Writing them in is cheap:
{ id = 'breakdance_uprock', category = 'Breakdance' },
{ id = 'tut_hip_hop_dance', category = 'Hip Hop' },
{ id = 'moonwalk', category = 'Party' },Pick one capitalisation and keep to it. The ids divide further if you want finer groups: sixteen start with breakdance_, five end in _hip_hop_dance, seven have freeze in the name.
Do not generate the groups from the ids, though. The breakdance group is 20, and four of those twenty — flair, flair_1, headspin_start and crossleg_freeze — do not carry the breakdance_ prefix. Take the grouping from the dance list and write it in by hand.
Never change
| Field | Why |
|---|---|
dict | Must match the name baked inside the .ycd file |
clip | Must 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. stream/ is escrow-protected in any case, so the .ycd files cannot be edited or renamed on disk. The risk is entirely in this file.
Both mistakes fail silently
A wrong dict means the dictionary never loads. A wrong clip means it loads and plays nothing. Neither raises an error, in the server console or in F8 — the character simply stands there.
Guard with DoesAnimDictExist, put a timeout on the HasAnimDictLoaded wait and print your own line, because that line is the only warning you will get. Scripting the pack has the loader.
The bodies table
Every dance ships in two versions, one fitted to the male body and one fitted to the female body. bodies is where the pair is written down:
bodies = { male = 'redmorrow2@dance', female = 'redmorrow2@dance@f' }The invariant holds across all 34 rows, without exception:
bodies.male | always equals the row's own dict, which is always redmorrow2@dance |
bodies.female | always redmorrow2@dance@f — the male name plus @f |
| Clip name | identical in both dictionaries |
There are only two dictionaries in the pack and no per-dance ones, so dict .. '@f' is a correct rule for every row here. Reading bodies.female is still the better habit: it costs nothing, it is what a pack with a different naming scheme would carry, and it keeps the choice of body 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 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, and on the floor moves it reads worse.
Picking between them needs GET_META_PED_TYPE, which returns 0 for male and 1 for female. The comparison has a trap in it: 0 is truthy in Lua, so compare the number rather than testing the return as a boolean. IsPedMale is a reasonable fallback but may hand back 0/1 rather than true/false on RedM, so handle both. The function is written out on Scripting the pack.
The two dictionary names are also on Two dictionaries, that is all.
Reading the table from your resource
Config is a Lua global, and CFX gives every resource its own isolated Lua state — so a global set inside redmorrow2 would exist only inside redmorrow2. In this pack it is not even that: the manifest loads no scripts, so the assignment never runs anywhere. Your own resource can start the pack, depend on it, stream its dictionaries and play every dance, and still see this:
-- in your own resource: always nil
print(Config and Config.Clips)There is no export to ask through either, because the pack registers none.
Two ways round it.
1. Copy the rows into your own resource
The reliable option, and the right default 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:
local Dances = {
{ id = 'moonwalk', clip = 'moonwalk', label = 'Moonwalk', group = 'Party' },
{ id = 'can_can', clip = 'can_can', label = 'Can Can', group = 'Party' },
{ id = 'breakdance_uprock', clip = 'breakdance_uprock', label = 'Breakdance Uprock', group = 'Breakdance' },
{ id = 'flair_1', clip = 'flair_1', label = 'Flair (1)', group = 'Breakdance', blendIn = 2.0 },
}
local MALE_DICT = 'redmorrow2@dance'
local FEMALE_DICT = 'redmorrow2@dance@f'Two dictionary names cover the whole pack, so they can live as constants rather than per row. 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
clips.lua is listed in escrow_ignore, so it is still a readable file on disk after escrow. That means LoadResourceFile can fetch its text and load can run it in an environment you control.
LoadResourceFile is a server-side native, and the clients never receive clips.lua as a script, so the usual shape is: read it on the server once, hand it to clients over an event.
Server side, in your own resource:
-- Returns redmorrow2's Config.Clips, or an empty table.
local function getDanceClips()
local src = LoadResourceFile('redmorrow2', 'clips.lua')
if not src then return {} end
local env = { Config = {} }
local chunk = load(src, '@redmorrow2/clips.lua', 't', env)
if not chunk or not pcall(chunk) then return {} end
return env.Config.Clips or {}
end
local danceClips = getDanceClips()
print(('[mydances] loaded %d dances'):format(#danceClips))
RegisterNetEvent('mydances:requestClips', function()
TriggerClientEvent('mydances:clips', source, danceClips)
end)Client side, in the same resource:
local danceClips = {}
local byId = {}
RegisterNetEvent('mydances:clips', function(clips)
danceClips = clips
byId = {}
for _, c in ipairs(danceClips) do
byId[c.id] = c
end
end)
CreateThread(function()
TriggerServerEvent('mydances:requestClips')
end)Rename the mydances:* events to fit your resource. byId['flair_1'].blendIn is 2.0 and byId['flair_1'].bodies.female is redmorrow2@dance@f.
Why the sandbox
env 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 34 rows out of your own globals — so a future version of the file cannot overwrite a Config of your own.
The pcall matters as much. A chunk loaded into an empty environment has no natives, no print and no require, so a file that called anything would error and you would get an empty table back rather than a crash. clips.lua assigns a literal and stops, which is exactly the shape the sandbox needs.
LoadResourceFile takes the resource name
The first argument is the resource name, which in CFX is the directory name on disk. Here the folder is redmorrow2 and the manifest's name field is redmorrow2 as well, so 'redmorrow2' is correct in both places and matches the ensure redmorrow2 line in server.cfg. If your server renamed the folder, change all three. Installation has the detail.
Which option to take
Copying the rows survives an update 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.
After an edit
Save
clips.lua. Nothing in the pack loads it, so the pack itself needs no restart and cannot be broken by what you wrote. Arestart redmorrow2is harmless but pointless. The server console says what it always says:textStarted resource redmorrow2Restart whatever consumes the list. If you copied rows into an emote menu or your own script, that resource is the one that needs restarting — and if you edited its config as well, after its own rules:
cfgrestart my_emotesRestart your sandbox loader too.
getDanceClipsruns once when your server script starts, so an edit toclips.luadoes not reach a resource that is already running.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 here is silent
In a pack that loaded clips.lua as a script, a missing comma would stop the resource from starting, which at least tells you something. Here it cannot: the file is never parsed by the pack. A broken file simply makes pcall(chunk) fail inside your sandbox loader and hands you an empty table — an emote menu with no dances in it and nothing in the console. Print #danceClips after loading, as in the snippet above.
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 covers the common menus, and for the database-backed route see 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.
Editing the rest of the download
| File | Edit it? |
|---|---|
clips.lua | Yes. label, category, mode, walk, blendIn, and fields of your own |
README.md, CHANGELOG.md, docs/*.md | Yes. Readable text, nothing loads them |
fxmanifest.lua | Leave it. There is nothing in it to tune |
stream/*.ycd | No. Escrow refuses to start a changed protected file |
.fxap | No. It arrives with the customer download and must stay as it is |
The dependency '/assetpacks' line in the manifest is the Cfx.re licence-key entitlement check. Without it the resource refuses to start, so it is not something to remove while tidying. The pack runs only on a server whose sv_licenseKey belongs to the Cfx.re account that bought it. Download it from portal.cfx.re under Assets then Granted Assets, and upload over FTP in binary mode — ASCII mode corrupts the protected files.
Because there are no scripts at all, the pack costs 0.00 ms on the client and on the server. Players download about 3 MB of animation files the first time they join and the files are cached after that. A dictionary only loads into memory when someone plays a dance from it.
See also
| Page | |
|---|---|
| Dance list | All 34 dances, with clip name, length and group |
| Scripting the pack | The thirteen-argument native call, start to stop |
| Emote menus | Putting the names in the menu you already run |
| Installation | The ensure line, the folder name, the licence-key check |
| Troubleshooting | Symptom, cause, fix |
| FAQ | Short answers, including what the pack does not do |
| Animation Basics | How RedM animations work in general |
| RedM | Where RedM differs from FiveM, flags included |
| 24+ Dances | The sibling dance pack. Different dictionary names, so both can run together |
| 100+ Female Poses | Another assets-only pack, female only |
| 30+ Female Poses | The pose pack that does ship a menu |
Still stuck? discord.gg/w4Ew4k3Exn.