A character creation screen
A player’s look is four parts of the game’s characters: the skin, the face, the hair with its color and the
beard. The server owns it - the mode sets it with SetPlayerLook, everyone
sees it, the player’s own game too - and the mode decides how a player gets to choose one. A character creation screen is
the mode’s own, in two halves:
- the server half (the game mode) sends the parts it offers, opens the screen, takes the look the player settles on in
OnPlayerLookChangeand saves it; - the client half (a client script the server sends with the mode) draws the menu,
shows every change on a preview body in front of the player that nobody else sees
(
ShowLookPreview), and asks for the look (RequestLookChange).
The shipped BasicRP mode has a complete one: an account’s first login without a look goes through it - in a virtual world
of the player’s own at the spawn, so nobody sees a half-made character - and into the shared world when it is done; /look
opens it again any time, and the look is saved with the account. Its files are the worked example of this page -
gamemodes/basicrp.lua and gamemodes/basicrp/client/creator.lua in the server folder; basicrp/client/account.lua next to
it is the password box a player registers and logs in with (a text field of the script’s own: OnTextInput,
and QuitGame for its Exit).
1. The parts
Section titled “1. The parts”GetLookParts(kind [, pattern]) lists what a man’s look may name, in the game’s
order, for "head" (the face), "hair", "beard" and "body" (the skin); the look parts are
the same lists on paper. Each entry has its name, its style (what it derives from), its traits (the words the game
matches faces, skins and hair by: Pale, Suntanned, Posh, YoungAdult …) and generic - true for the plain parts
the game gives its crowds, not a named character’s, not a wound’s.
A few rules make good lists:
-
faces: the generic heads whose
styleis""- the heads, not their variants; -
hair: a hair model (
x_hair_05) derives its colors (m_hair_005_black,m_hair_005_blonde…) - offer the model as a style and its generic colors as a second row;x_hair_20is a shaved head,m_hair_barber_01to_16the barber’s cuts; -
beards:
m_beard_00is none, the genericm_beard_*, andUC_beard_barber_01to_08the barber’s; -
skins: the generic
m_body_*tones (the pale, the tanned and the dark ones, young and old). -
a woman:
GetLookParts(kind, pattern, "female")lists her faces, hair and skins (the genericf_body_*andf_roma_*tones; she has no beard). Send both lists and let the player pick the body first; the look the creator asks for saysgender = "female". The preview turns into a woman’s body with it, and once the mode takes the look the player’s own game asks them to restart on her body - it loads with the level - and remembers it for the next join. She can do everything he can: the single-player game’s rules that keep women off horses, grindstones and alchemy tables do not apply to players, so a mode needs nothing extra for her to ride, sharpen or brew.
2. The server half
Section titled “2. The server half”local creating = {} -- pid -> true while the creator is open
function openCreator(pid, first) creating[pid] = true SendClientEvent(pid, "creator_parts", partsLine) -- the lists, once (a plain string of your own format) SendClientEvent(pid, "creator", (first and "new;" or "edit;") .. LookToString(GetPlayerLook(pid)))end
-- the player's "done": asked only after the server checked every partfunction OnPlayerLookChange(pid, look) if not creating[pid] then return false end -- no creator open: no look from this player creating[pid] = nil db:Execute("UPDATE players SET look = @l WHERE name = @n", { l = LookToString(look), n = GetPlayerName(pid):lower() }) SendClientEvent(pid, "creator", "close") return true -- the look is the player's, on every screenendOnPlayerLookChange is the only door: a mode without it takes no look from a player, so a player cannot give themselves a
look on a server that offers no way to. The server has already checked the parts (a part of that kind, all of them a man’s
or all a woman’s; one ask a second); the mode checks what it cares about - here, that its creator is open.
The look travels as one line of text, "body=m_body_tan_02;head=m_head_004;hair=m_hair_005_blonde;beard=m_beard_03"
(LookToString; a woman’s starts with gender=female;), which fits a TEXT
column. At the next visit it goes straight back:
-- at the login, with the row read from the databaseif not (row.look and row.look ~= "" and SetPlayerLook(pid, row.look)) then openCreator(pid, true) -- no look yet (or a part a game update removed): make oneendThe server itself remembers no look past a visit: without the mode’s database a look lasts until the player leaves.
3. The client half
Section titled “3. The client half”A file in the mode’s client/ folder (gamemodes/<mode>/client/*.lua, or gamemodes/<mode>/client/ next to a single-file
gamemodes/<mode>.lua); the server sends it to every player who joins.
local parts, look, row = nil, {}, 1
function OnServerEvent(name, payload) if name == "creator_parts" then parts = parseParts(payload) end if name == "creator" then if payload == "close" then return close() end local kind, line = payload:match("^(%a+);(.*)$") look = LookFromString(line) -- the player's look to start from ({} for a new character) SetKeyboardCapture(true) -- the menu has the keys; the player stands while choosing drawMenu() -- CreateUiClip / CreateUiRect / CreateUiText under one clip ShowLookPreview(look, {distance = 1.6}) -- a body in front of the player, dressed as they are endend
function OnKey(key, pressed) if not pressed then return end if key == "d" then look.hair = nextHair(look.hair) ShowLookPreview(look) redrawMenu() end if key == "q" then ShowLookPreview(look, {turn = -30}) end if key == "space" then RequestLookChange(look) end -- the mode's OnPlayerLookChange decidesend
function close() HideLookPreview() DestroyUiElement("menu") SetKeyboardCapture(false)endWhat each piece does:
SetKeyboardCapture(true)gives every key toOnKeyand the game takes no input meanwhile - the player neither walks nor turns away from the preview, Enter does not open the chat, Esc is yours to close the menu with. It ends withfalse, a reload of the scripts or the end of the session; a script whoseOnKeyfails gives it back by itself.ShowLookPreview(look, options)makes the body the first time - where the player looks,distancemetres ahead on their floor, facing them - and changes it in place afterwards: the look, thedistance(0.9is the face,2.5the whole body), theturnin degrees. Only this player sees it.RequestLookChange(look)asks the server; when the mode takes the look,OnLookChangetells the script, andGetPlayerLookanswers with it from then on. A mode that confirms with a script event of its own (“close” above) also covers the player who kept the look they had.
4. Where the player stands
Section titled “4. Where the player stands”The preview body is placed where the player looks when the creator opens. Open it where there is room in front of them - the
spawn of a mode is usually such a place; a mode that wants a set stage moves the player first
(SetPlayerPos with a yaw that faces the open side) and opens the creator a moment
later. A player who should not be seen while choosing goes into a virtual world
of their own and back into 0 when done.
5. Checklist
Section titled “5. Checklist”- The server has the tables export (
[combat] tablesin server.toml): without itGetLookPartsis empty and the parts pass unchecked - the tables guide makes the file. - The mode defines
OnPlayerLookChangeand takes a look only while its creator is open. - The look is saved as
LookToString’s line and given back withSetPlayerLookat the login. - The client half gives the keyboard back and hides the preview on every way out: done, cancel, the mode’s close.
