Skip to content

Multicharacter Selector Configuration ​

The selection screen reads config_selector.lua, which is a shared script — the client and the server both load it. Restart the resource after editing:

cfg
restart vorp_character

With Config.DevMode = true in config.lua you can restart the resource without restarting the server, which is how you want to work while you are tuning any of this.

Nothing in this resource is escrowed. Every .lua file is readable and editable, including the ones below.

The config file map ​

Eight files, each with a single job. Every setting carries a one-line comment in place.

FileWhat it controls
config.luaOriginal VORP settings: Lang, the character creator, Config.SpawnPosition (11 locations), Config.DevMode, Config.ReloadCharCommand (default rc)
config_selector.luaThe selection screen. Everything on this page
config_scenes.luaLocations exported from the Scene Creator. 2 ship; ConfigScenes.Enabled = true
config_scenecreator.luaThe Scene Creator: who may open it, freecam, placement, limits, seats
animations.luaThe poses players cycle with E, and the duo poses for a partner
config_emotes.luaThe Scene Creator's own pose browser — 305 poses, separate from animations.lua
config_shops.luaClothing stores (original VORP)
logs.luaThe Discord webhook for deleted characters

Your work lives in config files, not in code

Everything a server owner changes sits in config*.lua, animations.lua and logs.lua, plus partner_poses.json if you used the pose editor. The database holds scenes, partners and playtime, and an update does not touch it. So updating is a matter of keeping those files and replacing the rest — there is no code merge to do.

The load order matters and is already set in fxmanifest.lua: config_scenes.lua loads straight after config.lua because it appends to Config.SpawnPosition, and animations.lua loads after config_selector.lua because it writes into ConfigSelector.

General ​

SettingDefault
ConfigSelector.Enabledtruefalse brings back the original vorp_menu character list. Also remove the ui_page line from fxmanifest.lua
ConfigSelector.DebugfalseExtra client and server console output. Leave off on a live server
ConfigSelector.ScenariosOnlyfalsetrue hides every animation clip and leaves only scenario poses, solo and partner

ScenariosOnly is the blunt fix for floating characters. Scenarios are grounded by the engine and cannot float; raw clips carry the height of the set they were authored on. The distinction is explained on Emote library.

Playtime, delete, starter items and webhooks ​

SettingDefault
Playtime.enabledtrueTrack playtime and show it on the character cards
Playtime.saveInterval300Seconds between writes to character_playtime
Playtime.newLabel"New"Shown instead of a time on a character that has never been played
Delete.enabledtrueAllow deleting characters at all
Delete.requireNameConfirmtrueThe player has to type the character's first name
Delete.cascade14 tablesRows removed alongside the character
StarterItemswater ×2, bread ×2New characters only. Needs vorp_inventory. false disables it
StarterWeaponsfalseSame, for weapons, e.g. { "WEAPON_MELEE_KNIFE" }
Webhooksemptycreate, login and logout URLs. An empty URL turns that log off

Delete.cascade ships pointing at fourteen common VORP tables — character_playtime, outfits, loadout, character_inventories, character_weapons, banking, housing, stables, mailbox, billing, walkanim, metabolism, vorp_mining and vorp_fishing. A table you do not have is skipped rather than throwing an error, so you can leave rows for resources you have not installed. Add your own as { table = "mytable", column = "charidentifier" }.

A webhook URL is a credential

ConfigSelector.Webhooks holds them in plain text. Anyone with the URL can post into that channel. Redact them before sending your config to anyone, point them at a staff-only channel, and rotate one that leaks. The delete webhook is separate and lives in logs.lua.

Scene and camera ​

ConfigSelector.Scene controls what the screen looks like and how the camera moves between characters.

SettingDefault
Scene.allowLocationCycletruePlayers may change location with J
Scene.showLocationBadgefalseThe small "Location N" chip in the corner. Independent of cycling
Scene.allowPoseCycletruePlayers may change pose with E
Scene.showOnlySelectedtrueOnly the highlighted character stands in the scene, with its partner. false is stock behaviour — every character on screen at once
Scene.interpDuration1200Milliseconds the camera takes to glide to the next character
Scene.keepPhotoFilterfalsetrue keeps the sepia RDO photo filter over the whole selector
Scene.openEffect"RespawnPulse01"Flash played as the screen appears. false disables it
Scene.focusDistance4.0Focus distance of the close-up camera
Scene.motionBlur30.0Motion blur on the close-up camera. 0 disables it

showOnlySelected is worth knowing about on a server where players keep six characters. With it on, only one ped is spawned and posed at a time, which is both cheaper and tidier than a row of characters standing in frame.

Depth of field ​

lua
dof = {
    enabled   = false,
    nearStart = 0.2,
    nearEnd   = 1.4,
    farStart  = 4.5,
    farEnd    = 9.0,
    strength  = 1.0,
}

Test depth of field on your own build before you ship it

It is off by default for a reason. On some RedM builds enabling it turns the character camera completely black. If that happens, set enabled back to false — the five numbers underneath are not the problem.

What the player is allowed to move ​

ConfigSelector.Camera is a set of permission switches, each one controlling whether a slider appears in the Settings panel. All five default to true.

SwitchTurns offRelated keys
allowZoomBackground zoom, the field-of-view sliderminFovScale 0.60, maxFovScale 1.40
allowCharZoomCharacter zoom, the camera-distance sliderminDistScale 0.40, maxDistScale 1.60
allowRotateThe rotation sliderrotateStep 15 degrees per step
allowOrbitShift + arrow keys and the orbit and height slidersorbitStep 5, orbitSpeed 60, heightSpeed 0.6
allowFramingThe framing presetsframing list, below

On both zoom sliders, the middle position is whatever the scene itself configured — 50% on the background slider is the fov from config.lua, and 50% on the character slider is the framing preset's own distance. The scales multiply that, so the ranges above mean the background goes from 40% tighter to 40% wider.

heightMin is -0.6 and heightMax is 2.0, both in metres relative to the framing's own camera height, not to the ground. That is the clamp on Shift + Up and Shift + Down. Widen heightMax if you have built a scene where the interesting angle is from above; tighten both if players keep finding a view through the terrain.

UI defaults, and what players may change ​

ConfigSelector.UI is the starting state for a player who has never opened Settings. Once they change something it is saved on their own machine, in client KVP, so these values only ever apply to newcomers.

SettingDefault
theme"dark""dark" or "light"
menuStyle"cinematic""cinematic" is the left rail, "classic" the bottom bar
buttonSkin"blk_slate"An id from ConfigSelector.ButtonSkins. Classic bar only
cinematicBarstrueThe letterbox bars, toggled in play with B
showKeybindstrueThe on-screen key hints
soundEnabledtrueInterface click and hover sounds
soundVolume0.40.0 to 1.0, interface sounds only
musicEnabledtrueThe music cue the scene plays
languagenilnil follows Lang in config.lua
creditstwo rowsShown on the Credits page

The shipped credits table literally reads name = "Change me in config_selector.lua" on its second row. Put your server's name there.

lua
credits = {
    { role = "Framework", name = "VORP Core" },
    { role = "Server",    name = "My Server Name" },
},

ConfigSelector.Keys holds the rebindable keys as JavaScript key codes with the label the screen draws: pose KeyE, location KeyJ, cinematicBar KeyB, hideUI KeyH, settings KeyO and partner KeyP. Change the code and the label together — the hints follow the table, so a mismatch leaves players reading the wrong letter.

Weather and time ​

ConfigSelector.Environment decides where the selector's weather and time come from. The order of precedence is the player's choice in Settings, then the scene's own options, then the defaults in this table.

SettingDefault
allowPlayerOverridetruefalse makes every location always use its own weather and time
enableWeather / enableTimetrueWhether the selector touches weather and time at all
defaultWeather"sunny"Used when a scene defines nothing
defaultTime12:00{ hour = 12, minute = 0 }
weatherTransition / timeTransition2.0 / 2How long the change takes
weatherOptions22 entriesThe dropdown: "scene" plus 21 weathersync types
snowTypes5 typesWeathers that also lay permanent snow

Weather and time need weathersync. Trim weatherOptions if you would rather players did not put a blizzard behind their character.

The lists you can extend ​

Four option lists in this file are meant to be added to. Each behaves slightly differently, and one of them will mislead you if you only read the literal table.

38 screen filters ​

ConfigSelector.Filters.enabled is true, and the literal list holds exactly one row:

lua
ConfigSelector.Filters = {
    enabled = true,
    list = {
        { id = "none", label = "None" },
    },
}

The other 38 are appended by a loop immediately below it, from three groups — Game ×18, Modern ×10 and Vintage ×10 — each row built into a photo mode effect name like PhotoMode_FilterGame01:

lua
local groups = {
    { prefix = "Game",    label = "Game",    count = 18 },
    { prefix = "Modern",  label = "Modern",  count = 10 },
    { prefix = "Vintage", label = "Vintage", count = 10 },
}

Reading the literal table undercounts the filters

There are 38 filters in the dropdown and one none entry, but only the none entry is typed out. To change how many of a group appear, edit its count; to add one of your own, append a row after the loop as { id = "mine", label = "My look", fx = "SomeAnimpostfxName" }.

Filters are applied on the selection screen only and cleared when the player leaves it.

7 music options, 5 of which are real events ​

ConfigSelector.Music.tracks has seven rows, but two of them are not music:

Labelevent
Scene defaultnilFalls through to the location's own options.music
FrontierREHR_START
CharacterMP_CHARACTER_CREATION_START
LobbyMP_CO_OP_LOBBIES_LOW_START
CelebrationMP_CELEB_SCREEN_MUSIC
DriftingMP_CLOUDS_TRANSITION_MUSIC
SilencefalsePlays nothing

So five RDR3 music events, plus "whatever the scene asked for" and "nothing at all". The distinction matters when you add a row: an event of nil means defer to the scene and false means stay quiet, so a new track needs a real event name. stopEvent is MC_MUSIC_STOP and is what gets triggered on the way out.

2 button skins, and why Slate ships dimmed ​

lua
ConfigSelector.ButtonSkins = {
    { id = "blk_slate", label = "Dark slate", file = "blk_slate.webp" },
    { id = "slate",     label = "Slate",      file = "slate.webp", dim = 0.55 },
}

These are the plates behind the buttons of the classic bottom bar; the cinematic layout does not use them. file is relative to html/img/btn/, which contains exactly these two files.

dim is optional and defaults to 1. Slate ships at 0.55 because at full strength the plate drops the PLAY accent's contrast against it to roughly 2.0:1, which is not readable. If you add a light plate of your own, expect to need a dim value too.

A new plate needs two lines, not one

A row in ConfigSelector.ButtonSkins and a line in the files block of fxmanifest.lua. Without the second, the image is never sent to the client and the button renders bare. Use uniform materials — badge-shaped images stretch badly across the different button widths.

5 camera framing presets ​

ConfigSelector.Camera.framing is the Framing dropdown. Each row aims the camera at a skeleton bone, from in front of the character.

LabelbonelookdistcamLift
Scene default————
Full body144101.002.600.25
Upper body144131.351.400.02
Head210301.620.850.00
Legs551200.451.600.35

bone is an RDR3 skeleton bone id — skel_spine0 at the waist, skel_spine3 at the upper chest, skel_head, skel_l_calf at the shin. dist is how far in front of the character the camera sits in metres, camLift is the camera's height relative to the aim point, and look is a fallback height above the feet used when the bone cannot be read. Scene default carries no values at all: it leaves the camera exactly where the location configured it.

For a paired character the camera moves side-on instead, so both people are in frame.

Why Logout ships off ​

lua
ConfigSelector.Logout = {
    enabled = false,
    command = "logout",
    cooldown = 60,
    canLogout = function(source)
        return true
    end,
}

Switching character without reconnecting is the one feature in this file that is disabled by default, and it is not a performance decision.

Logout needs a patch to vorp_core, not just a true

It calls a logoutCharacter() function in vorp_core/server/class/user.lua. Stock vorp_core does not have that function. Setting enabled = true without adding it registers the command and then fails when a player runs it. Leave it off unless you have patched vorp_core yourself and understand that your patch has to be reapplied every time you update the framework.

If you do run it, the two settings around it are worth setting properly. cooldown is the number of seconds a player has to be in-game before they may log out again — 60 stops slot-hopping being used to dodge something. canLogout(source) runs on the server and blocks the logout if it returns false, which is where you check whether the player is in jail, in combat or cuffed:

lua
canLogout = function(source)
    if exports.my_jail:isJailed(source) then return false end
    return true
end,

command = false registers no command at all, leaving the logout button in the interface as the only route in.

Covered elsewhere ​

Three parts of this file are large enough to have their own page, and they are not repeated here.

  • Locations — Config.SpawnPosition, ConfigScenes.List, and the 13 locations that ship.
  • Poses — animations.lua, the source modes, perLocation, and the 20 duo poses.
  • Languages — ConfigSelector.Locales, the 7 locales at 89 keys each, and how to add an eighth.

If something in the interface is not behaving the way this page says it should, turn ConfigSelector.Debug on, reproduce it, and read the console before changing anything else. Failing that, there is a Discord.

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