Skip to content

The Combat Moves Menu ​

PGUP, or /combatmoves. The panel opens over the game and takes your input while it is up — SetNuiFocus(true, ...) hands the keyboard to the page, so your character stands still until you close it.

Driving it ​

Tabs along the topone per feature — click to jump straight there
Searchfilters the current page as you type; Tab focuses it
Arrow keysmove, and change the value on a slider or options row
Enterselect
Backspacego back a page
Escapeclose
Mousehover to highlight, click to activate, click a ◀ ▶ to nudge a value, wheel to scroll, click outside to close
Back / closethe two buttons top right; Back hides itself on the root page

Every change applies immediately. Nothing needs reloading and you do not have to close the panel to see the effect.

Menu changes are not saved

They last until the resource restarts, are not written to config.lua, and are not sent to anyone else. To make a value permanent, set it in config.lua. To make one unreachable, use a convar or hide the row.

The pages ​

PageTabSubtitle
Combat Moves—the root list
Combat SlideSlideHold C while running
Combat RollRollLALT, direction from your movement keys
Gun TricksTricksSpinning / Idle, plus a Settings sub-page
Dive, Crawl & GunDiveG to dive, hold INPUT_DUCK to go prone

The subtitles are built from your actual key settings, so they stay right after you change a key. The values are interpolated raw, with nothing mapping a control name to a friendly word — which is why the dive subtitle reads INPUT_DUCK rather than "crouch".

The root list ​

SectionRows
(no heading)Combat Slide >, Combat Roll >, Gun Tricks >, Dive, Crawl & Gun >
MenuMouse cursor
PagesCombat Slide, Combat Roll, Gun Tricks, Dive, Crawl & Gun
SessionSliding enabled, Rolling enabled, Close

The per-feature rows are covered on their own pages: Combat Slide, Combat Roll, Gun Tricks, Dive, Crawl & Gun.

Close is appended after the two Session toggles with no heading of its own in between, so it belongs to that section: its id is menu.session.close, and Config.Menu.sections['menu.session'] = false takes Close away along with the toggles.

The "Sliding enabled" row is a switch, not an indicator

It always reads as on, whatever state sliding is actually in, because its getter is hard-coded. Toggling it does call SetEnabled and does work — the tick just does not follow. There is no way to read the real state from outside either: SetEnabled writes a module-local enabled flag that has no getter and no export. IsSliding() — and cs_watch, which prints the same export — only tells you whether a slide is in progress at that moment.

The mouse cursor ​

Config.MenuMouse ships true. It is the cursor half of SetNuiFocus(true, hasCursor).

With it false there is no cursor at all, so the panel's hover and click handlers exist but can never fire — the menu becomes keyboard-only.

The trade-off

A cursor takes mouse look away from the player for as long as the panel is up. The keyboard bindings keep working either way, so a server that would rather players kept their camera can turn it off:

lua
Config.MenuMouse = false

Combat Moves → Menu → Mouse cursor toggles it live, and it applies straight away rather than on the next open.

Choosing what the menu shows ​

Config.Menu gates the panel at four levels, coarse to fine.

Hiding something does not disable it

It only takes it off the panel — the feature keeps running on whatever config.lua says. To turn a feature off, use its own enabled flag. See Server setup.

Config.Menu.enabledthe master switch. false and the menu never opens: the key is ignored, /combatmoves says so once, and the OpenMenu export returns false
Config.Menu.pagesa whole page. It loses its row on the front list and its tab, and the strip closes up rather than leaving a gap
Config.Menu.sectionsa grey heading inside a page, and every row under it down to the next heading
Config.Menu.optionsone row

Ids are page.section.row, lower case with dashes for spaces. A row above the first heading has no section part.

pages and sections list every one there is, so they read as a contents page. options is the opposite — anything absent is shown, so it only needs the rows you want gone:

lua
Config.Menu.pages.tricks = false                         -- no Gun Tricks page
Config.Menu.sections['dive.dive-camera'] = false         -- no dive camera block
Config.Menu.options['slide.behaviour.mud-decal'] = false -- just that one row

Every section id ​

PageSections
slideslide.behaviour, slide.combat, slide.camera
rollroll.try-it
trickstricks.tricks, tricks.more
divedive.diving, dive.crawling, dive.prone-camera, dive.dive-camera, dive.stealth, dive.fix
the root listmenu.menu, menu.pages, menu.session

Find ids with cs_menuids, not by hand ​

Do not derive an id yourself

cs_menuids

It prints every id the menu can switch with its current state, read off the pages as they are really built — so what it prints is exactly what config.lua will match. Two filters:

cs_menuids diveonly ids with dive in them. It is a substring match anywhere in the id, not a prefix, so root-list ids such as menu.dive-crawl-gun and rows such as dive.diving.invincible-mid-dive come through too
cs_menuids offonly what is currently hidden

The ids are generated from your headings and labels by the same code path the live menu uses, so the listing and the menu cannot disagree.

The GetMenuIds() export returns the same data as a table.

Behaviour worth knowing ​

  • A heading left with nothing under it is dropped, so hiding rows one at a time never strands a title.
  • Hidden rows are not reachable by id either — the front end cannot drive a row that was filtered out.
  • No tab number is written down anywhere. The live index is worked out from the tabs that are on, so hiding a page cannot leave a tab highlighting the wrong thing.
  • /guntricks checks the page switch as well as the master one, so it is not a way round a hidden Tricks page.

The Pages section, and locking it ​

The root list's Pages section writes to Config.Menu.pages live. It is the way back if a player switches all four pages off — no page switch can hide it.

To lock the page set to whatever config.lua says:

lua
Config.Menu.sections['menu.pages'] = false

That is the only way back

With menu.pages hidden, nothing in game can turn a page on again. It is what a server wants, but set your pages deliberately before you hide it.

Common setups ​

Read-only panel. Hide the sections that contain settings and leave the action rows:

lua
Config.Menu.sections = {
    ['slide.behaviour'] = false,
    ['slide.combat']    = false,
    ['slide.camera']    = false,
    ['dive.diving']     = false,
    ['dive.crawling']   = false,
}

No menu at all.

lua
Config.Menu.enabled = false

Every feature still works on your values.

Only two of the four features visible.

lua
Config.Menu.pages = { slide = true, roll = true, tricks = false, dive = false }

The tabs close up — no gap, no dead tab.

The front end ​

Three plain files in nui/ plus the artwork in nui/ASSETS/. No frameworks, no CDN requests and no webfont requests, so it works on a server with no outbound internet.

It is styled after the RDR2 in-game panels using that artwork: modal_bg.png for the torn black plate, item_bg.png and tab-item.png for the painted strips, border.png inverted for the header buttons, and the two Rockstar faces — RDR Catalogue for headings and row names, RDRLino for body copy. click.mp3, toggle.mp3 and turn-on.mp3 are wired to selection, value changes and opening.

Anything the page loads must be listed in files{}

If it is not in fxmanifest.lua, RedM will not serve it and the panel falls back to unstyled text. The globs for nui/ASSETS/best/*.png, nui/ASSETS/*.ttf and nui/ASSETS/*.mp3 are there for exactly that reason — if you add artwork, add it to the manifest. Note that every sprite the panel draws is referenced as ASSETS/best/... in style.css, so the live set is the one in nui/ASSETS/best/; the plain nui/ASSETS/*.png glob serves only the originals kept for comparison, and replacing art there changes nothing on screen. The fonts and the sounds do sit directly in nui/ASSETS/.

Renaming the resource ​

The NUI fetch origin is https://rm_combat_moves/<callback>, built from the RESOURCE constant in nui/app.js. If you rename the folder, that constant has to change too or the panel will open and then do nothing. The full rename checklist is on Developer API.

Exports ​

lua
exports.rm_combat_moves:OpenMenu()            -- false if disabled, or it was open (it closes)
exports.rm_combat_moves:CloseMenu()
exports.rm_combat_moves:IsMenuOpen()
exports.rm_combat_moves:IsMenuEnabled()
exports.rm_combat_moves:SetMenuEnabled(false) -- closes it if it is up
exports.rm_combat_moves:ForceCloseMenu()      -- recovery; releases NUI focus unconditionally
exports.rm_combat_moves:GetMenuIds()

ForceCloseMenu() is the one to call from a jail or cutscene script if you need to be certain the player does not keep NUI focus.

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