Every docs URL answers with Markdown when you add .md. Site map: llms.txt
Variables, constants and enums
Naming that survives review, where to declare things, why locals beat globals in Lua, and replacing piles of booleans with a single state value.
Esta documentación está en inglés por ahora.
Naming and placement are not style points. A variable you cannot find the declaration of is a variable you will re-declare somewhere else, and two booleans that describe one thing will eventually disagree.
Naming
| Kind | Style | Example |
|---|---|---|
| Constant (local or global) | ALL_CAPS |
local MAX_DISTANCE = 20.0 |
| Local variable | camelCase | local shopStock = {} |
| Resource global | PascalCase | PlayerData = {} |
| Unused but required | _ |
for _, v in pairs(t) do |
Lua has no const, so ALL_CAPS is the whole enforcement mechanism. That is fine as long as you are consistent: seeing MAX_DISTANCE in the middle of a function tells the reader it was set once at the top of the file and nothing changes it.
Use _ for values you have to accept and do not use. for _, shop in pairs(shops) says “I do not care about the key” without inventing a name like k that a reader has to check.
Avoid single letters outside loop counters, and avoid Hungarian style prefixes (strName, bIsDead). The type is visible from the value and your editor knows it.
Where to declare
- File level locals at the top, grouped, with a blank line under them. Anyone opening the file sees the whole state of it in one screen.
- Function level locals right before first use, not at the top of the function. If you have to scroll to see what a name holds, it is declared too far away.
- Resource globals at the top of the file that owns them, and only in the file that owns them. A global assigned in three files has no owner and no one can change it safely.
local MAX_DISTANCE <const> = 20.0
local INTERACT_KEY <const> = 38 -- E
local shops = {}
local nearestShop = nilCfxLua is Lua 5.4, so <const> works and the compiler enforces it: assigning to a <const> local is a compile error, not a runtime surprise. When the value is a literal the compiler folds it into the code that uses it, so it costs nothing.
Note
Lua allows 200 local variables per function and per chunk. Declaring every single config entry as its own file level local is a real way to hit “too many local variables”. Group related values into one table instead.
Locals beat globals
A local lives in a register. A global is a key in the _ENV table, so every read and every write is a hash lookup. The difference per access is small, but it is free to avoid:
-- Three hash lookups per iteration
for i = 1, 1000 do
Config.total = Config.total + Config.prices[i]
end
-- One lookup each, then registers
local prices = Config.prices
local total = 0
for i = 1, 1000 do
total = total + prices[i]
end
Config.total = totalThe same trick works for functions you call in a loop, including globals from the standard library:
local floor = math.floor
local insert = table.insert -- when you genuinely need insertDo this inside hot code, not at the top of every file out of habit. A file with 30 hoisted stdlib names at the top is harder to read than the lookups it saves.
Booleans that should be a state
The pattern to watch for is two or more booleans describing one thing:
local isWalking = false
local isRunning = false
local isSwimming = falseEvery combination is representable, including the ones that make no sense. What does it mean when isWalking and isSwimming are both true? What does it mean when all three are false: standing still, or nobody has set them yet? Now every reader of that code has to defend against combinations you never intended, and every writer has to remember to clear the other two.
One value with named options removes the whole problem:
local MOVEMENT <const> = {
UNKNOWN = 0,
IDLE = 1,
WALKING = 2,
RUNNING = 3,
SWIMMING = 4,
}
local movement = MOVEMENT.UNKNOWN
local function setMovement(state)
if movement == state then return end
movement = state
TriggerEvent('my_resource:client:movementChanged', state)
endYou get an impossible state for free (UNKNOWN), adding CLIMBING is one line, and comparisons read as English. It also fits state bags and network payloads better: one small number instead of three fields that can arrive out of step. See State bags and OneSync.
Lua has no real enum type, so the table is the convention. Keep it <const>, keep the values numbers if they will be sent over the network, and keep it in a shared file if both sides need it.
Magic numbers
A number nobody can explain is a bug waiting to be copied:
TaskPlayAnim(ped, dict, clip, 8.0, -8.0, 3000, 49, 0, false, false, false)49 is an animation flag set, and the only way to know which is to look it up. Name it:
local ANIM_FLAGS <const> = 49 -- loop + upper body + allow player controlOur Animation Flags Calculator builds that number from named options, and the Driving Style Calculator does the same for TaskVehicleDriveToCoord. Control indexes like 38 have names in the Controls Reference.
