en-multicharacter is the character selection screen. After a player joins, the screen fades to a staged scene (by default the Sandy Shores motel lot) with a scripted camera and a preview ped.
Players see a card for each survivor: name, birth date, nationality, job, group tag, cash and bank, when they last played, and their condition (health, hunger, thirst, radiation, infection, immunity, dead). They can pick a survivor, create a new one (first and last name, date of birth, gender, nationality) or delete one, optionally confirmed by typing the first name. When en-clothing is running, the preview ped wears the character's saved look. /logout from en-core, or an admin unload, drops the player back into selection.
At a glance
| Depends on | en-core, en-ui (the creation dialog uses encore.inputDialog) |
| Optional | en-clothing (preview look), en-loading |
| Database tables | None of its own. Characters live in en-core's tables |
| Config files | config.lua |
| Key binds | None |
How it works
- The selector opens automatically 500 ms after
NetworkIsSessionStarted(). - Once the roster is on screen it fires
en-multicharacter:client:openedand callsShutdownLoadingScreen()andShutdownLoadingScreenNui(), which closes en-loading. - The number of character slots comes from en-core's
GetCharacterSlots(license). - When the player selects or creates a character, en-core logs them in and the client spawns: fade out, stream the scene around the destination, wait for collision (up to 10 seconds), fade in, then fire
en-multicharacter:client:spawned.
New characters spawn at Config.newCharacterSpawn, or at en-core's default spawn when that is nil.
Security
- A server-side "selecting" set gates every callback. A client that is already in the world cannot reopen selection and load a second character.
- Ownership is re-checked with en-core's
LicenseOwnsCharacterbefore select, delete and look requests. - Names, ages and nationalities are validated on the server with
Config.validation. Unknown nationalities become'Unknown'. - Character cards leave out metadata and last position, apart from survival condition.
Configuration
config.lua:
| Key | Default | What it does |
|---|---|---|
Config.scene.ped | vec4(1972.24, 3051.32, 47.21, 152.0) | Preview ped position |
Config.scene.camera | vec3(1970.28, 3048.94, 47.55) | Camera position |
Config.scene.lookAt | vec3(1972.24, 3051.32, 47.45) | Camera target |
Config.scene.fov | 42.0 | Camera field of view |
Config.previewPeds | [0] = 'mp_m_freemode_01', [1] = 'mp_f_freemode_01' | Preview model by stored gender |
Config.newCharacterSpawn | nil | vec4 for new survivors; nil uses en-core's default spawn |
Config.confirmDeleteByName | true | Require typing the first name to delete |
Config.validation | nameMinLength 2, nameMaxLength 24, namePattern "^[%a][%a%s'%-]*$", minAge 18, maxAge 90 | Enforced server-side |
Config.nationalities | 32 entries (American ... Unknown) | Choices in the creation dialog |
Moving the scene and giving new survivors their own spawn:
-- config.lua
Config.scene = {
ped = vec4(1972.24, 3051.32, 47.21, 152.0),
camera = vec3(1970.28, 3048.94, 47.55),
lookAt = vec3(1972.24, 3051.32, 47.45),
fov = 42.0,
}
Config.newCharacterSpawn = vec4(1853.8, 3689.4, 34.3, 210.0)
Config.validation.minAge = 16Callbacks
en-multicharacter has no exports or commands. Its UI talks to the server through these encore.callback names:
| Name | Arguments | Returns |
|---|---|---|
en-multicharacter:getCharacters | { characters, slots }; empty if already logged in | |
en-multicharacter:selectCharacter | citizenid | { ok, position, isNew = false } or { ok = false, error } |
en-multicharacter:createCharacter | { firstname, lastname, birthdate = 'YYYY-MM-DD', gender = 0 or 1, nationality } | { ok, position, isNew = true } |
en-multicharacter:deleteCharacter | citizenid | { ok } |
en-multicharacter:getLook | citizenid | en-clothing look or nil |
Events
| Event | Side | Payload |
|---|---|---|
en-multicharacter:client:opened | Client local | The selector is on screen |
en-multicharacter:client:spawned | Client local | position = { x, y, z, heading }. The player is placed in the world; run post-spawn logic here |
It listens to encore:client:playerUnloaded (reopens the selector), and on the server to encore:server:onPlayerUnload and playerDropped.
Examples
Greet a survivor once they are in the world:
-- client
AddEventHandler('en-multicharacter:client:spawned', function(position)
local data = exports['en-core']:GetPlayerData()
encore.notify({ title = 'Welcome back', description = data.charinfo.firstname, type = 'inform' })
end)If you replace en-multicharacter with your own selector, it must call ShutdownLoadingScreen() and ShutdownLoadingScreenNui(), or the loading screen never closes.