# Config

#### ⚙️ Configuration

All customization is done in **two files only**:

**🧩 `config.lua`**

This is where **99% of setup happens**:

* **Command names** (`/me`, `/do`, `/pm`, `/n`, `/g`, etc.)
* **Chat colours** (RP, OOC, PM, Newbie)
* **Distances** (local chat, RP range, ID overlay range)
* **Keybinds** (default key to hold IDs/names)
* **Staff ACE groups & colours**
* **Feature toggles** (enable/disable systems)
* **Notification system**
  * Default: `ox_lib`
  * Optional: ESX Notify or op-hud (can add custom)

No code knowledge required, everything is clearly commented.

***

**🔔 `server/discord.lua`**

Used **only for logging**:

* Paste your Discord webhook URLs here
* Enable/disable specific log categories
* Control whether message content is logged

***

Config File Preview:

{% code lineNumbers="true" fullWidth="false" %}

```lua
--========================================================
-- Zeno RP Chat + ID System
-- You should find most things are editble here
-- If not then please open a ticket on Discord!
-- https://discord.gg/ut4z2vRpvD
-- Docs: https://zeno-scripts.gitbook.io/docs
--========================================================

Config = {}

--========================================================
-- Notifications
-- type: 'error' | 'success' | 'info'
--========================================================

Config.Notify = function(source, nType, timeMs, message)
    timeMs = timeMs or 3500
    message = message or ''

    -- DEFAULT: ox_lib notify (comment to disable)
    if nType ~= 'error' and nType ~= 'success' and nType ~= 'info' then nType = 'inform' end --if not one of the 3 then make it inform
    if nType == 'info' then nType = 'inform' end

    TriggerClientEvent('ox_lib:notify', source, {
        type = nType,
        description = message,
        duration = timeMs
    })

    -- OPTION 1: ESX Notify (uncomment to use)
    -- TriggerClientEvent('esx:showNotification', source, message)

    -- OPTION 2: op-hud Notify (uncomment to use)
    -- TriggerClientEvent('op-hud:showNotification', source, nType, timeMs, message)

    -- OPTION 3: okokNotify (uncomment to use)
    -- if nType ~= 'error' and nType ~= 'success' and nType ~= 'info' then nType = 'info' end
    -- TriggerClientEvent('okokNotify:Alert', source, '', message, timeMs, nType, false) --Set true if you want notify sound to play

    -- Or add your own here
end
-- Dont touch this below
Config.NotifyAll = function(nType, timeMs, message)
    for _, pid in ipairs(GetPlayers()) do
        Config.Notify(tonumber(pid), nType, timeMs, message)
    end
end

--========================================================
-- General
--========================================================
Config.Locale = 'en'

-- If you want to disable entire systems quickly:
Config.Features = {
    IdOverlay = true,         -- Right-Alt hold overlay (IDs / greeted names / masked)
    StaffSName = true,        -- /sname staff name overhead + in /g /b /n /pm chats
    Greeting = true,          -- ox_target greet system (sharing and saving RP names)
    RPCommands = true,        -- /me /do /ame /ado /ldo
    OOCChats = true,          -- /b /o /g (b and o are the same, just 2 ways of doing it)
    PM = true,                -- /pm
    NewbieChat = true,        -- /n + /tognewbie + /nmute /nunmute (can config perms below)
    Stats = true,             -- /stats (shows the player some basic stats of their character)
    Timestamps = true,        -- /timestamps (allow players to see timestamps in chat, saved per player)
    DiscordLogs = true,       -- requires server/discord.lua to be configured
    JobChat = true,           -- /j job chat + /jmute /junmute /jtoggleall /togj
}

--========================================================
-- Keybinds / Overlay
--========================================================
Config.Keybinds = {
    -- Default hold key for the ID/Name overlay (rebindable in FiveM settings)
    HoldOverlayDefault = 'RMENU', -- Right Alt

    -- Allow users to toggle overlay on/off entirely with /ids (recommend false)
    AllowIdsToggle = false,

    -- Allow users to toggle showing their own overhead info with /selfid (recommend true)
    AllowSelfIdToggle = true,
}

--========================================================
-- Distances & UI behaviour
--========================================================
Config.Distances = {
    DrawDistance = 18.0,       -- max distance for ID/name overlay rendering
    GreetDistance = 2.5,       -- must be within this distance to greet (server-validated)
    LocalOOC = 20.0,           -- /b /o range
    RP = 20.0,                 -- /me /do /ldo range
    AOnly = 20.0,              -- /ame /ado range
}

Config.Overhead = {
    TextScale = 0.35,
    -- You can tune these if your server uses different ped scales/camera FOV etc.
    HeadZOffset = 0.28,
    LineSpacing = 0.17,
}

--========================================================
-- Colours (Chat formatting only; overhead uses its own styles)
--========================================================
Config.Colours = {
    OOC = '89888a',        -- /b /o /g
    RP  = 'bda9d8',        -- /me /do & overhead RP emotes
    PM  = 'eba924',        -- /pm
    NEWBIE = 'fbc849',     -- /n
}

--========================================================
-- Commands (Options to rename any command)
--========================================================
Config.Commands = {
    -- Overlay toggles
    ids = 'ids',
    selfid = 'selfid',

    -- Staff / admin
    sname = 'sname',
    gtoggle = 'gtoggle',

    -- Known names
    forget = 'forget',

    -- RP
    me  = 'me',
    do_ = 'do',
    ame = 'ame',
    ado = 'ado',
    ldo = 'ldo',
    flipcoin = 'flipcoin',
    dice = 'dice',

    -- OOC / PM
    pm = 'pm',
    b  = 'b',
    o  = 'o',
    g  = 'g',

    -- Newbie
    n = 'n',
    tognewbie = 'tognewbie',
    nmute = 'nmute',
    nunmute = 'nunmute',

    -- QoL
    timestamps = 'timestamps',
    stats = 'stats',

    -- Job chat
    j = 'j',
    jmute = 'jmute',
    junmute = 'junmute',
    jtoggleall = 'jtoggleall',
    togj = 'togj',

}


--========================================================
-- Rate Limits (seconds)
--========================================================
Config.RateLimits = {
    greetRequest = 3,
    greetPrompt  = 2,

    localChat    = 1,  -- /me /do /ame /ado /b /o
    globalChat   = 2,  -- /g
    pm           = 2,  -- /pm
    ldo          = 2,  -- /ldo
    newbieChat   = 2,  -- /n
}

--========================================================
-- Job Chat (OOC job-only chat)
--========================================================
Config.JobChat = {
    Prefix = '[JOB]',
    Color = '3b8dff', -- blue
    DefaultPublic = true, -- true = everyone in job can use /j, false = grade restricted until toggled
    MuteMinAce = nil, -- optional ACE required for /jmute /junmute (e.g. 'group.mod')

    -- Configure which ESX jobs have job chat and their grade thresholds
    -- Grade numbers follow ESX job grade (0 = lowest)
    Jobs = {
        police = {
            Label = 'Police',
            SpeakMinGrade = 0, -- used when DefaultPublic=false or /jtoggleall set to restricted
            MuteMinGrade = 3,  -- /jmute /junmute
            ToggleMinGrade = 4 -- /jtoggleall
        },
        ambulance = {
            Label = 'Ambulance',
            SpeakMinGrade = 0,
            MuteMinGrade = 2,
            ToggleMinGrade = 3
        },
        government = {
            Label = 'Government',
            SpeakMinGrade = 0,
            MuteMinGrade = 2,
            ToggleMinGrade = 3
        }
    }
}

--========================================================
-- Staff groups (ACE)
-- IMPORTANT: order is HIGH -> LOW so highest rank wins.
-- These are the ones I use in my server as example, 
-- you need to change these to your own Ace Perms groups!
--========================================================
Config.StaffGroups = { 
    { ace = 'group.admin', label = 'Director',    color = 'ff0038' },
    { ace = 'group.lead',  label = 'Lead Admin',  color = 'c71313' },
    { ace = 'group.adm',   label = 'Admin',       color = 'e67e22' },
    { ace = 'group.srmod', label = 'Senior Mod',  color = '1f8b4c' },
    { ace = 'group.mod',   label = 'Mod',         color = '2ecc71' },
    { ace = 'group.supp',  label = 'Support',     color = '9b59b6' },
}

-- Minimum ACE group required to toggle /g public mode (/gtoggle)
Config.GlobalToggleMinAce = 'group.srmod'

--========================================================
-- ESX Integration
--========================================================
-- Return character first/last name.
-- This uses ESX Identity naming conventions by default.
Config.GetCharacterName = function(source, ESX)
    local xPlayer = ESX.GetPlayerFromId(source)
    if not xPlayer then
        local pn = GetPlayerName(source) or ('Player %d'):format(source)
        local first, last = pn:match('^(%S+)%s+(.+)$')
        if first then return first, last end
        return pn, ''
    end

    local rpName = (xPlayer.getName and xPlayer.getName()) or (GetPlayerName(source) or ('Player %d'):format(source))
    local first, last = rpName:match('^(%S+)%s+(.+)$')
    if first then return first, last end
    return rpName, ''
end

--========================================================
-- Mask detection
-- Server uses statebag rp_masked (client sets it), client uses native drawable.
-- If you use a different masking logic, customize here.
--========================================================
Config.IsPlayerMasked = function(source)
    if IsDuplicityVersion() then
        local st = Player(source).state
        return (st and st.rp_masked == true) or false
    end

    local ped = PlayerPedId()
    if not ped or ped == 0 then return false end
    return GetPedDrawableVariation(ped, 1) ~= 0
end

--========================================================
-- Stats command options
--========================================================
Config.Stats = {
    Enabled = true,
    Title = '------ Stats ------',
    ShowBank = true,
    ShowCash = true,
    ShowDOB = true,
    ShowGender = true,

    -- Output labels
    Labels = {
        Name = 'Name',
        DOB = 'DOB',
        Gender = 'Gender',
        Bank = 'Bank',
        Cash = 'Cash',
    },

    -- Gender formatting
    GenderFormat = function(v)
        v = tostring(v or ''):lower()
        if v == 'm' or v == 'male' then return 'Male' end
        if v == 'f' or v == 'female' then return 'Female' end
        return 'N/A'
    end,
}

--========================================================
-- Newbie Chat options
--========================================================
Config.Newbie = {
    Prefix = '[NEWBIE]',
    -- Name colour for /n (hex without #)
    NameColor = '43aef5',
    -- Minimum Permission level for /nmute and /nunmute
    MuteMinAce = 'group.mod',
    -- If true, /n logs message content (still controlled by DiscordLogs.logMessageContent in discord.lua)
    LogMessages = true,
}

--========================================================
-- PM options
--========================================================
Config.PM = {
    PrefixTo = 'PM to',
    PrefixFrom = 'PM from',
    AllowSelfPM = false, -- useful for dev servers
}

--========================================================
-- Greeting options
--========================================================
Config.Greet = {
    TargetLabel = 'Greet',
    TargetDistance = 2.0, -- ox_target interaction distance
}

--========================================================
-- /g options
--========================================================
Config.GlobalOOC = {
    Prefix = '[GLOBAL]',
    DefaultPublic = false, --false = staff only on restarts (/gtoggle turn on for everyone), true = everyone can use on restart
}

--========================================================
-- Logging options
-- Note: webhooks live in server/discord.lua, not here.
--========================================================
Config.Logging = {
    --Detailed discord logs with character name, id, player name, license, command used, message content, etc...
    EnableLogs = true,
}

```

{% endcode %}
