Přejít na obsah
Variables, constants and enums

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.

Tato dokumentace je zatím v angličtině.

Last updated

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.
client/shops.lua
local MAX_DISTANCE <const> = 20.0
local INTERACT_KEY <const> = 38  -- E

local shops = {}
local nearestShop = nil

CfxLua 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:

Lua
-- 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 = total

The same trick works for functions you call in a loop, including globals from the standard library:

Lua
local floor = math.floor
local insert = table.insert   -- when you genuinely need insert

Do 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:

client.lua (ambiguous)
local isWalking = false
local isRunning = false
local isSwimming = false

Every 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:

client.lua (one state)
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)
end

You 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:

Lua
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:

Lua
local ANIM_FLAGS <const> = 49  -- loop + upper body + allow player control

Our 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.

Next: Conditionals and early returns.