Skip to content

RedM Emotes Configuration

Every key in config.lua, what it accepts, and the exact lines to change for the setups people actually run.

Fileconfig.lua in the resource root
Loaded asa shared_script — the same table is read client and server side
Applying changesrestart rm_emotes. There is no config reload command
EscrowListed in escrow_ignore, so it stays fully editable in both editions
Not in this fileChair settings live in shared/seats.lua, prop and shared emotes in shared/extras.lua

Two rules for the whole file

Do not delete a top-level table

RM.Command, RM.SlashCommand, RM.Keys and RM.Showcase are read by literal path with no guard — RM.Keys.open.active, RM.Showcase.enabled. Removing the table raises a Lua error at resource start. To switch a feature off, set its flag to false and leave the table in place.

Several booleans are tested against literal false

RM.Adjust.enabled, RM.Adjust.ghost, RM.Adjust.raycast, RM.Adjust.gizmo, RM.Adjust.freezeOnConfirm, RM.Showcase.inMenu and RM.Showcase.levelCamera use ~= false or == false.

Deleting one of these keys enables the feature. Always write false explicitly; never comment the line out.

General and appearance

KeyDefaultWhat it does
RM.Lang'English'Selects the locale table for menu text and notifications. Must match a name in locales/*.lua. An unknown name silently falls back to English at every read site
RM.Ui.layout'grand'Default menu layout. 'grand' is the full dashboard, 'compact' the smaller rail. Any other value is coerced to 'grand'

RM.Ui.layout is a default, not a lock

The menu has a Grand/Compact switch and a player's choice is stored in their own browser storage. Once a player uses that switch, your value no longer applies to them.

Server-side log and audit strings are hard-coded English and ignore RM.Lang.

Commands

KeyDefaultWhat it does
RM.Command.enabledtrueRegisters the menu command
RM.Command.name'emotes'/emotes. No chat autocomplete is registered for it
RM.SlashCommand.enabledtrueRegisters the quick-play command and its suggestion. Also controls whether cards show the /e <name> hint
RM.SlashCommand.name'e'No argument opens the menu; stop/c stops; random plays a random one; anything else is a slug lookup

stop, c and random are reserved by the slug allocator and can never be used as an emote command. They are hard-coded and cannot be translated or extended.

Admin access and audit

KeyDefaultWhat it does
RM.AdminAce'rm_emotes.admin'ACE object checked with IsPlayerAceAllowed
RM.Admins{ 'steam:11000014864548d' }Alternative grant. Each entry is compared verbatim against every identifier the player has
RM.Webhook''Discord webhook for the admin audit log. '' disables it

Change RM.Admins before you go live

The shipped file contains a real sample Steam identifier. Any server that leaves it in place grants full create/edit/delete access, plus visibility of every hidden and restricted emote, to whoever owns that account.

lua
RM.Admins = {}

RM.Admins is read once and cached for the lifetime of the resource — editing it requires a resource restart, not a reconnect.

There is one admin level. No tiers, no per-action ACE: the same flag unlocks viewing, creating, editing and deleting.

Framework and notifications

KeyDefaultWhat it does
RM.Framework'auto''auto' probes vorp_core, then rsg-core, then redem_roleplay, falling back to standalone
RM.NotifyMode'auto'Which notification pipe player notices use

Accepted RM.Framework values: 'auto', 'vorp', 'rsg', 'redemrp', 'standalone'. The value is not validated — an unrecognised string behaves exactly like standalone.

Detection runs once at resource start and matches a resource state containing start, so both starting and started count. A core that starts after this resource is never re-detected.

RM.NotifyMode values:

ValueEmitsNotes
'auto'follows the detected framework
'vorp'vorp:TipRighttitle and error styling ignored
'rsg'RSGCore:Notifypasses error or primary
'redemrp'redem_roleplay:Tiptitle and error styling ignored
'ctrl'ctrl:cAvancedNotifyreceives kind, title and message. Needs a resource that handles it
'text'chat:addMessageplain chat line, works everywhere
'none'nothingsuppressed entirely
anything elsechat:addMessageunknown strings fall through to chat

Notification duration is hard-coded at 3000 ms on the vorp, rsg, redemrp and ctrl branches. The chat fallback (text, standalone, unknown values) passes no duration — how long the line stays is up to your chat resource.

Keys and hotkeys

KeyDefaultWhat it does
RM.Keys.open.activetrueRuns the poll thread for the open key. false removes the key entirely
RM.Keys.open.control0x4CC0E2FE (B)Control hash checked each frame
RM.Keys.cancel.activetruePoll thread for the stop key
RM.Keys.cancel.control0x7DA48D2A (X)Ignored while seated, because X is the stand-up prompt
RM.Keys.cancel.forceStoptrueAlso calls ClearPedTasksImmediately
RM.BindListener'raw'Where per-emote saved binds come from
RM.ShiftHotkeystrueSHIFT+1 to SHIFT+5 for the first five starred emotes

RM.Keys.*.control must be a numeric hash

The value goes straight to IsControlJustPressed and input names are never resolved here. 'INPUT_OPEN_SATCHEL_MENU' will not work — unlike RM.Adjust.keys, which does accept names.

RM.BindListener values:

ValueBehaviour
'raw'Built-in raw keyboard listener. Single letters and digits only. No dependencies
'ctrl'Binds come from incoming ctrl_basic:keyPress events instead
'both'Behaves identically to 'raw'. The raw listener runs whenever the value is not exactly 'ctrl', and the keyPress handler only dispatches binds when it is exactly 'ctrl' — so there is no double-fire and no extra coverage

Any unrecognised string behaves as 'raw'.

Fixed in code and not configurable: only single letters and digits can be bound; F1F12, Alt, Backspace, Escape and X are permanently rejected; the SHIFT hotkey count is fixed at five and the modifier cannot be changed; and an incoming ctrl_basic:keyPress carrying F5 always opens the menu, regardless of RM.Keys.open.active or RM.Command.enabled.

The SHIFT+1..5 order is the order the server returns saved emotes in, which is unordered — it is not a slot list the player can sort.

Adjust mode

All keys in RM.Adjust.keys accept either a RedM input name (hashed at runtime) or a raw control hash.

KeyDefaultWhat it does
RM.Adjust.enabledtrueMaster switch. Also gates the command and the gizmo keymapping
RM.Adjust.command'adjustanim'Chat command that starts adjust mode. A falsy value registers none
RM.Adjust.moveStep0.015Metres per frame while a nudge key is held
RM.Adjust.rotateStep1.2Degrees per frame while a rotate key is held
RM.Adjust.maxOffset5.0Maximum horizontal nudge away from the placement base — the aim point while raycast placement is still following the crosshair, otherwise the spot where adjust started. It does not cap total distance from the start when raycast = true; aimDistance does
RM.Adjust.maxHeight1.5Maximum up or down nudge, metres
RM.Adjust.ghosttrueShows a translucent double instead of moving the real character. If the model fails to stream it falls back to moving the player
RM.Adjust.ghostAlpha180Ghost transparency, 0–255
RM.Adjust.ghostModel''Explicit ghost model. '' uses the RM.Showcase model for the player's gender
RM.Adjust.ghostClonefalseClone the player's own character. Renders as detached clothing on VORP and RSG MP characters — leave this false
RM.Adjust.raycasttrueAim placement: the preview follows the crosshair until the player nudges or drags. Requires ghost = true
RM.Adjust.aimDistance25.0Maximum aim-placement distance, metres
RM.Adjust.freezeOnConfirmtrueKeeps the character frozen on the confirmed spot so they cannot slide off a ledge
RM.Adjust.gizmotrueEnables the drag handles, /rm_gizmo and its keymapping
RM.Adjust.gizmoKey'G'Key registered for the gizmo toggle

Changing gizmoKey later only affects new players

It is registered through RedM's keymapping system, so anyone who already played with the old value keeps it until they rebind in the RedM settings.

RM.Adjust.keys:

KeyDefaultAction
confirm'INPUT_FRONTEND_ACCEPT' (Enter)Commit the placement
cancel'INPUT_FRONTEND_CANCEL' (Backspace)Revert and exit
forward / back'INPUT_MOVE_UP_ONLY' / 'INPUT_MOVE_DOWN_ONLY'W / S
left / right'INPUT_MOVE_LEFT_ONLY' / 'INPUT_MOVE_RIGHT_ONLY'A / D
rotLeft / rotRight'INPUT_FRONTEND_LEFT' / 'INPUT_FRONTEND_RIGHT'Arrow Left / Right
up / down'INPUT_FRONTEND_UP' / 'INPUT_FRONTEND_DOWN'Arrow Up / Down

Do not delete individual entries from RM.Adjust.keys

Each is read without a fallback, so a missing entry reaches the control check as nil at runtime. Change the value instead of removing the line.

Setting RM.Adjust.command to an empty string is not an off switch. '' is truthy in Lua, so command = '' still passes the registration guard and registers a nameless command plus a bare / chat suggestion. Use false. This differs from RM.Webhook and RM.Adjust.ghostModel, where '' is the documented off value.

INPUT_ATTACK and INPUT_AIM are disabled for the whole adjust session and are not configurable. Gizmo drag sensitivity, ring geometry and HUD text are likewise fixed.

Showcase mannequin

KeyDefaultWhat it does
RM.Showcase.enabledtrueMaster switch. Also gates the per-player Preview toggle
RM.Showcase.inMenufalsefalse floats the mannequin in front of the player's view. true projects it into the menu's framed pane
RM.Showcase.levelCamerafalseCreates a scripted level camera behind the character
RM.Showcase.cameraBack3.0Metres behind the character for that camera
RM.Showcase.clonePlayerfalseBroken for VORP and RSG MP characters — leave false
RM.Showcase.opacity2200–255
RM.Showcase.scale0.6Mannequin size
RM.Showcase.distance1.5Metres in front of the camera
RM.Showcase.heightOffset1.0Lowers the mannequin. Raise this if it sits too high
RM.Showcase.models.male'rcsp_native_americanfathers_males_01'Also the default adjust-mode ghost for male characters
RM.Showcase.models.female're_deadjohn_females_01'Same, and the fallback when the gender lookup misses

Notes that matter:

  • opacity, scale and distance are read without a fallback. Deleting one produces a runtime error when the mannequin spawns.
  • The framed in-menu pane is drawn as soon as inMenu = true. Set levelCamera = true alongside it — without the scripted camera the mannequin is projected through the gameplay camera and is not held at eye level. With the shipped defaults the pane is never drawn.
  • cameraBack has no effect until levelCamera = true — the camera routine returns immediately while it is off.
  • levelCamera is tested with == false, so removing the key turns the camera on.
  • inMenu is also tested with ~= false. Removing the key, or commenting it out, turns the framed pane on. Write false explicitly.
  • Ambient models stream reliably at runtime; story models (player_zero, cs_*) often fail on some RedM builds.
  • Walk styles and shared emotes never hover-preview by design — a walk is only visible in motion and a shared emote needs a partner.

Shared emotes

KeyDefaultWhat it does
RM.Shared.range3.0Metres searched for the closest partner. Players, NPCs and animals all qualify
RM.Shared.timeout8000Milliseconds before an unanswered request auto-declines

The accept and decline keys (E and R) are hard-coded and are not part of RM.Shared. The definitions themselves live in shared/extras.lua.

What you will look for and not find

Fixed in code: the length of the Recent tab (24), the list of keys that can never be bound, the number of SHIFT hotkeys (5), the menu close sound, notification duration (3000 ms), the reserved /e words, and the database table names.

Chair models, sitting styles, prop emotes and shared emotes are configurable — they just live in shared/seats.lua and shared/extras.lua rather than here.

One key is conditionally inert: RM.Showcase.cameraBack does nothing until levelCamera = true. Otherwise every key in the shipped file is read by the code — there are no dead settings.

Recipes

Change the key that opens the menu

Numeric hash only; names are not resolved here.

lua
RM.Keys = {
    open   = { active = true, control = 0x80F28E95 },                   -- INPUT_PLAYER_MENU
    cancel = { active = true, control = 0x7DA48D2A, forceStop = true }, -- X / INPUT_FRONTEND_RS
}

A control hash is GetHashKey('INPUT_NAME'). The only two hashes verified in this resource are 0x4CC0E2FE (INPUT_OPEN_SATCHEL_MENU, B) and 0x7DA48D2A (INPUT_FRONTEND_RS, X), both from config.lua.

Not certain of a hash? Leave the key off and use /emotes, or the next recipe.

Hand the keys to your own keybinding resource

Turn off both threads but keep the table in place:

lua
RM.Keys = {
    open   = { active = false, control = 0x4CC0E2FE },
    cancel = { active = false, control = 0x7DA48D2A, forceStop = true },
}
lua
-- your own client script
RegisterCommand('myemotesbind', function()
    TriggerEvent('rm_emotes:client:open')
end, false)

rm_emotes:client:open is provided for exactly this. See the Developer API.

Run without any framework

lua
RM.Framework  = 'standalone'
RM.NotifyMode = 'text'

The job lookup returns nothing, so restricted becomes an ACE-only check. Express job gating as ACE principals:

cfg
add_ace group.sheriff lawman allow

Then set restricted to lawman on the emote row. Favourites and key binds keep working — they key off the player's identifier, not the framework.

Restrict the admin editor to one person

lua
RM.AdminAce = 'rm_emotes.admin'
RM.Admins   = {}
cfg
add_ace identifier.license:0000000000000000000000000000000000000000 rm_emotes.admin allow

Turn off adjust mode, or just the gizmo

lua
RM.Adjust = {
    enabled = false,
    -- leave the rest of the table as shipped
}

Keep adjust mode but drop the mouse gizmo:

lua
    gizmo    = false,
    gizmoKey = 'G',

Write false explicitly — both are tested against literal false, so removing the line switches the feature back on.

Disable the showcase mannequin

lua
RM.Showcase = {
    enabled      = false,
    inMenu       = false,
    levelCamera  = false,
    -- leave every remaining key in place: opacity, scale and distance
    -- are read without a fallback and removing them errors at runtime
    cameraBack   = 3.0,
    clonePlayer  = false,
    opacity      = 220,
    scale        = 0.6,
    distance     = 1.5,
    heightOffset = 1.0,
    models       = {
        male   = 'rcsp_native_americanfathers_males_01',
        female = 're_deadjohn_females_01',
    },
}

Switch to the framed in-menu preview pane

The pane is drawn as soon as inMenu = true. Set levelCamera = true alongside it, or the mannequin is projected through the gameplay camera and does not sit at eye level.

lua
    enabled     = true,
    inMenu      = true,
    levelCamera = true,
    cameraBack  = 3.0,

If the mannequin sits too high or low inside the pane, adjust heightOffset: a larger value lowers it.

Enable the Discord audit log

lua
RM.Webhook = 'https://discord.com/api/webhooks/000000000000000000/replace-with-your-own-token'

The webhook URL is a credential

It sits in plain text in config.lua. Anyone you send your config to for support can post arbitrary messages into that channel. Redact it before sharing. Delivery failures are silent — the response is never inspected.

Three events are logged: emote created, updated and deleted through the editor. Player usage, favourites and rejected attempts are not.

Change the menu language and rename the commands

lua
RM.Lang = 'Spanish'

RM.Command      = { enabled = true, name = 'gestos' }
RM.SlashCommand = { enabled = true, name = 'g' }

Several player-visible strings stay English regardless of RM.Lang: the /e and /adjustanim chat suggestions, the gizmo key-binding label RM Emotes: toggle adjust gizmo shown in RedM's Settings > Key Bindings screen, the mannequin load-failure notice and the RM Emotes title it is sent with, the [rm_emotes] console lines, and the Discord webhook embed titles. None are editable in the escrow edition. Emote names come from the database, so import the matching seed as well — see Languages.

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