# Wert Advanced HUD

### 📦 Dependencies

Before installing, please ensure you have the following resource installed and running:

* [ox\_lib](https://github.com/overextended/ox_lib)

## 🛠️ Installation

#### Step 1: Resource Placement

* Download the script from your CFX portal.
* Place the `wert_hud` folder into your server's `resources` directory.

#### Step 2: Configuration

* Open `config/main.lua` and select your framework:

```lua
Framework = 'qbx', -- 'qb' - 'qbx' - 'esx'
```

#### Step 3: Ensure Resource

* Add the following line to your `server.cfg`:

`ensure wert_hud`

{% hint style="info" %}
Note : If you wish to make changes before starting, you can make custom adjustments to the configuration file or any other open file.
{% endhint %}

## ⚙️ Language Editor (JSON)

{% hint style="success" %}
All text and messages are stored in JSON format. You can easily translate the script into your preferred language or edit the text directly within the `locale.json`
{% endhint %}

Harika, bu kodlar dökümantasyonun "Developer API" kısmını çok daha profesyonel gösterecek. Geliştiricilerin kendi scriptlerinden senin HUD'ına veri göndermesini sağlayacak bu

### 💻 Developer API

Use these exports and events to integrate your custom scripts with **Wert HUD**.

#### **1. Update Status Values**

You can manually update the status bars (Hunger, Thirst, Stress) using either the export or the client-side event.

**Export Usage**

```lua
-- Usage: exports['wert_hud']:SetStatus(statusName, value)
-- statusName: 'hunger', 'thirst', 'stress'
-- value: 0 to 100

exports['wert_hud']:SetStatus('hunger', 85)

```

**Client Event Usage**

```lua
-- TriggerEvent('wert_hud:client:setstatus', statusName, value)

TriggerEvent('wert_hud:client:setstatus', 'thirst', 50)

```

***

#### **2. Toggle HUD Display**

You can completely hide or show the HUD (e.g., for cutscenes, photo modes, or specific UI interactions).

**Export Usage**

```lua
-- Usage: exports['wert_hud']:HudDisplay(state)
-- state: true (Visible) / false (Hidden)

exports['wert_hud']:HudDisplay(false) -- Hides the HUD

```

**Client Event Usage**

```lua
-- TriggerEvent('wert_hud:client:hud-display', state)

TriggerEvent('wert_hud:client:hud-display', true) -- Shows the HUD

```

***

#### **3. Character Creation State**

To prevent the HUD from overlapping during character creation or multi-character menus, use this event to set the creation state.

**Client Event Usage**

```lua
-- TriggerEvent('wert_hud:client:new-character-status', state)
-- state: true (In Creator) / false (Finished)

TriggerEvent('wert_hud:client:new-character-status', true)

```

***

#### 💡 **Pro Tip for Developers**

> All values sent to `SetStatus` are automatically sanitized and converted to numbers. Ensure you are sending values between **0 and 100** for the best visual representation.

## <mark style="color:orange;">OPEN FILES</mark>

<details>

<summary>config/main.lua</summary>

```lua
return {
    Framework = 'qbx', -- 'qb' - 'qbx' - 'esx'
    Menu = {
        command = 'hud',
        key = { enable = true, key = 'I' }
    },
    DisableHudComponents = {
        enable = true,
        disableAmmoFrame = true,
        comps = { 2, 3, 4, 6, 7, 8, 9, 20 }
    },
    lowFuelAlert = {
        enable = true,
        value = 20
    },
    DefaultSettings = {
        isShow = true,
        isCinematic = false,
        currentPreset = 'hexagon',
        currentVehiclePreset = 'minimal',
        speedUnit = 'kmh',
        isLocationVisible = true,
        locationOnlyInVehicle = false,
        compassShow = false,
        compassOnlyInVehicle = false,
        isInfoBoxVisible = true,
        isMinimapAlwaysVisible = false,
        currentInfoPreset = 'classic',
        mapShape = 'square',
        scales = {
            status = 1.0,
            vehicle = 1.0,
            location = 1.0,
            info = 0.7
        },
        dynamics = {
            health = false,
            armor = true,
            hunger = true,
            thirst = true,
            stress = true,
            oxygen = true
        }
    },
    stress = {
        enableStress = true, -- If false, it will disable stress for everyone
        disableForLEO = true,
        chance = 0.1, -- Percentage stress chance when shooting (0-1)
        minForShaking = 50, -- Minimum stress level for screen shaking
        minForSpeeding = 1000, -- Minimum stress level for speeding while buckled
        minForSpeedingUnbuckled = 50, -- Minimum stress level for speeding while unbuckled
        whitelistedWeapons = { -- Weapons which don't give stress
            `weapon_petrolcan`,
            `weapon_hazardcan`,
            `weapon_fireextinguisher`,
        },
        blurIntensity = { -- Blur intensity for different stress levels
            [1] = {min = 50, max = 60, intensity = 1500},
            [2] = {min = 60, max = 70, intensity = 2000},
            [3] = {min = 70, max = 80, intensity = 2500},
            [4] = {min = 80, max = 90, intensity = 2700},
            [5] = {min = 90, max = 100, intensity = 3000},
        },
        effectInterval = { -- Effect interval for different stress levels
            [1] = {min = 50, max = 60, timeout = math.random(50000, 60000)},
            [2] = {min = 60, max = 70, timeout = math.random(40000, 50000)},
            [3] = {min = 70, max = 80, timeout = math.random(30000, 40000)},
            [4] = {min = 80, max = 90, timeout = math.random(20000, 30000)},
            [5] = {min = 90, max = 100, timeout = math.random(15000, 20000)},
        },
    },
}
```

</details>

<details>

<summary>config/shared.lua</summary>

```lua
LANG = lib.loadJson('locale')

function locale(key)
    local data = LANG.lua
    for part in string.gmatch(key, "[^%.]+") do
        if data[part] == nil then
            return key -- çeviri yoksa key'i döndür
        end
        data = data[part]
    end
    return data
end
```

</details>

<details>

<summary>client/utils.lua</summary>

```lua
local config = require 'config.main'
Framework = nil
PlayerData = {}

if config.Framework == 'qb' or config.Framework == 'qbx' then
    Framework = exports['qb-core']:GetCoreObject()
else
    Framework = exports["es_extended"]:getSharedObject()
end

seatbeltOn = false
lastFuelUpdate = 0
lastFuelCheck = 0

function CustomNotify(title, text, style)
    lib.notify({ title = title, description = text, type = style })
end

function getFuelLevel(vehicle) -- # U can update with ur fuel export!
    local updateTick = GetGameTimer()
    if (updateTick - lastFuelUpdate) > 2000 then
        lastFuelUpdate = updateTick
        local fuelExp = 30
        if GetResourceState('lc_fuel') == 'started' then
            fuelExp = exports['lc_fuel']:GetFuel(vehicle)
        elseif GetResourceState('LegacyFuel') == 'started' then
            fuelExp = exports['LegacyFuel']:GetFuel(vehicle)
        elseif GetResourceState('cdn_fuel') == 'started' then
            fuelExp = exports['cdn_fuel']:GetFuel(vehicle)
        elseif GetResourceState('ox_fuel') == 'started' then
            fuelExp = GetVehicleFuelLevel(vehicle)
        end
        lastFuelCheck = math.floor(fuelExp)
    end
    return lastFuelCheck
end

function GetCharacterData()
    local data = {}
    if config.Framework == 'qbx' then
        data = {
            stress = QBX.PlayerData.metadata.stress, hunger = QBX.PlayerData.metadata.hunger,
            thirst = QBX.PlayerData.metadata.thirst,
            firstname = QBX.PlayerData.charinfo.firstname, lastname = QBX.PlayerData.charinfo.lastname,
            jobLabel = QBX.PlayerData.job.label, jobGrade = QBX.PlayerData.job.grade.name,
            cash = QBX.PlayerData.money.cash, bank = QBX.PlayerData.money.bank,
        }
    elseif config.Framework == 'qb' then
        PlayerData = Framework.Functions.GetPlayerData()
        data = {
            stress = PlayerData.metadata.stress, hunger = PlayerData.metadata.hunger,
            thirst = PlayerData.metadata.thirst,
            firstname = PlayerData.charinfo.firstname, lastname = PlayerData.charinfo.lastname,
            jobLabel = PlayerData.job.label, jobGrade = PlayerData.job.grade.name,
            cash = PlayerData.money.cash, bank = PlayerData.money.bank,
        }
    elseif config.Framework == 'esx' then
        PlayerData = Framework.GetPlayerData()
        local esxPlayerData = lib.callback.await('wert_hud:server:esxGetPlayerData', false)
        data = {
            stress = 0, hunger = 50, thirst = 50,
            firstname = esxPlayerData.firstname or '', lastname = esxPlayerData.lastname or '',
            jobLabel = esxPlayerData.jobLabel or '', jobGrade = esxPlayerData.jobGrade or '',
            cash = esxPlayerData.cash or 0, bank = esxPlayerData.bank or 0,
        }
    end
    return data
end

function PlayerIsDead()
    -- # If u want u can edit dead getter
    if config.Framework == 'qbx' then
        return IsEntityDead(cache.ped) or QBX.PlayerData.metadata.inlaststand or QBX.PlayerData.metadata.isdead
    elseif config.Framework == 'qb' then
        return IsEntityDead(cache.ped) or PlayerData.metadata.inlaststand or PlayerData.metadata.isdead
    else
        return IsEntityDead(cache.ped)
    end
end

function GetVoiceHudData()
    -- # Setted for pma-voice. If u use another voice system u can edit this codes.
    local plyState = LocalPlayer.state
    return {
        isTalking = NetworkIsPlayerTalking(cache.playerId),
        isRadioTalking = plyState.radioActive,
        proximity = plyState.proximity or { index = 1 }
    }
end

function GetSeatBelt()
    -- # If u want manage belt getter
    if config.Framework == 'qbx' then
        return LocalPlayer.state.seatbelt
    else
        return seatbeltOn
    end
end

RegisterNetEvent('seatbelt:client:ToggleSeatbelt', function()
    seatbeltOn = not seatbeltOn
end)

RegisterNetEvent('ox_inventory:currentWeapon', function(weapon)
    -- Data must be table or nil : nil is unholster
    -- data : { name: string, label: string, image: string, hash: number }
    if weapon then
        weapon.image = 'nui://ox_inventory/web/images/'..weapon.name..'.png'
    end
    TriggerEvent('wert_hud:client:updateWeapon', weapon)
end)

RegisterNetEvent('QBCore:Client:OnPlayerUnload', function()
    PlayerData = {}
end)

RegisterNetEvent('QBCore:Player:SetPlayerData', function(val)
    PlayerData = val
end)

if config.DisableHudComponents.enable then
    CreateThread(function()
        while true do
            for i = 1, #config.DisableHudComponents.comps do
                local row = config.DisableHudComponents.comps[i]
                HideHudComponentThisFrame(row)
            end
            if config.DisableHudComponents.disableAmmoFrame then
                DisplayAmmoThisFrame(false)
            end
            Wait(1)
        end
    end)
end

if config.lowFuelAlert.enable then
    CreateThread(function()
        while true do
            if LocalPlayer.state.isLoggedIn then
                if cache.vehicle and not IsThisModelABicycle(GetEntityModel(cache.vehicle)) then
                    if getFuelLevel(cache.vehicle) <= config.lowFuelAlert.value then
                        CustomNotify(locale('notify.low_fuel_title'), locale('notify.low_fuel'), 'error')
                        Wait(60000)
                    end
                end
            end
            Wait(10000)
        end
    end)
end

RegisterNetEvent('wert_hud:client:custom-notif', CustomNotify)
```

</details>

<details>

<summary>client/stress.lua</summary>

```lua
local hasWeapon = false
local config = require 'config.main'

if config.stress.enableStress then

    local function isWhitelistedWeaponStress(weapon)
        if weapon then
            for _, v in pairs(config.stress.whitelistedWeapons) do
                if weapon == v then return true end
            end
        end
        return false
    end

    local function getBlurIntensity(stresslevel)
        for _, v in pairs(config.stress.blurIntensity) do
            if stresslevel >= v.min and stresslevel <= v.max then return v.intensity end
        end
        return 1500
    end

    local function getEffectInterval(stresslevel)
        for _, v in pairs(config.stress.effectInterval) do
            if stresslevel >= v.min and stresslevel <= v.max then return v.timeout end
        end
        return 60000
    end

    local function startWeaponStressThread(weapon)
    if isWhitelistedWeaponStress(weapon) then return end
        hasWeapon = true
        CreateThread(function()
            while hasWeapon do
                if IsPedShooting(cache.ped) then
                    if math.random() <= config.stress.chance then
                        TriggerServerEvent('hud:server:GainStress', math.random(1, 5))
                    end
                end
                Wait(0)
            end
        end)
    end

    CreateThread(function()
        while true do
            if LocalPlayer.state.isLoggedIn then
                if cache.vehicle then
                    local vehClass = GetVehicleClass(cache.vehicle)
                    local speed = GetEntitySpeed(cache.vehicle)
                    if vehClass ~= 13 and vehClass ~= 14 and vehClass ~= 15 and vehClass ~= 16 and vehClass ~= 21 then
                        local stressSpeed
                        if vehClass == 8 then
                            stressSpeed = config.stress.minForSpeeding
                        else
                            stressSpeed = LocalPlayer.state?.seatbelt and config.stress.minForSpeeding or config.stress.minForSpeedingUnbuckled
                        end
                        if speed >= stressSpeed then
                            TriggerServerEvent('hud:server:GainStress', math.random(1, 3))
                        end
                    end
                end
            end
            Wait(10000)
        end
    end)

    CreateThread(function()
        while true do
            local effectInterval = getEffectInterval(stress)
            if stress >= 100 then
                local blurIntensity = getBlurIntensity(stress)
                TriggerScreenblurFadeIn(1000.0)
                Wait(blurIntensity)
                TriggerScreenblurFadeOut(1000.0)
                if not IsPedRagdoll(cache.ped) and IsPedOnFoot(cache.ped) and not IsPedSwimming(cache.ped) then
                    local forwardVector = GetEntityForwardVector(cache.ped)
                    SetPedToRagdollWithFall(cache.ped, 3000, 3000, 1, forwardVector.x, forwardVector.y, forwardVector.z, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
                end
            elseif stress >= config.stress.minForShaking then
                local blurIntensity = getBlurIntensity(stress)
                TriggerScreenblurFadeIn(1000.0)
                Wait(blurIntensity)
                TriggerScreenblurFadeOut(1000.0)
            end
            Wait(effectInterval)
        end
    end)

    RegisterNetEvent('wert_hud:client:updateWeapon', function(currentWeapon)
        hasWeapon = false
        Wait(0)
        if not currentWeapon then return end
        startWeaponStressThread(currentWeapon.hash)
    end)
end

```

</details>

<details>

<summary>server/editable_server.lua</summary>

```lua
local config = require 'config.main'
local qbType = false
Framework = nil
Bridge = {}
if config.Framework == 'qb' or config.Framework == 'qbx' then
    qbType = true
    Framework = exports['qb-core']:GetCoreObject()
else
    Framework = exports["es_extended"]:getSharedObject()
end

-- # Functions

function Bridge.GetPlayer(src)
    if not src then return nil end
    if qbType then
        return Framework.Functions.GetPlayer(src)
    else
        return Framework.GetPlayerFromId(src)
    end
end

function Bridge.GetIdentifier(src, ply)
    if not ply then return nil end
    if qbType then
        return ply.PlayerData.citizenid
    else
        return ply.getIdentifier()
    end
end

function LeoControl(player)
    if not player then return true end
    if qbType then
        return player.PlayerData.job.type == 'leo'
    else
        return ply.job.name == 'police' or ply.job.name == 'ambulance'
    end
end

-- # Stress

RegisterNetEvent('hud:server:GainStress', function(amount)
    if not config.stress.enableStress then return end
    local src = source
    local player = Bridge.GetPlayer(src)
    local newStress
    if not player or (config.stress.disableForLEO and LeoControl(player)) then return end
    if not resetStress then
        if qbType then
            if not player.PlayerData.metadata.stress then
                player.PlayerData.metadata.stress = 0
            end
            newStress = player.PlayerData.metadata.stress + amount
        else
            -- Esx save logic!
        end
        if newStress <= 0 then newStress = 0 end
    else newStress = 0 end
    if newStress > 100 then newStress = 100 end
    if qbType then
        player.Functions.SetMetaData('stress', newStress)
    else
        -- Esx save logic!
    end
    TriggerClientEvent('hud:client:UpdateStress', src, newStress)
    TriggerClientEvent('wert_hud:client:custom-notif', src, '', locale('notify.stress_gain'), 'inform')
end)

RegisterNetEvent('hud:server:RelieveStress', function(amount)
    if not config.stress.enableStress then return end
    local src = source
    local player = Bridge.GetPlayer(src)
    local newStress
    if not player then return end
    if not resetStress then
        if qbType then
            if not player.PlayerData.metadata.stress then
                player.PlayerData.metadata.stress = 0
            end
            newStress = player.PlayerData.metadata.stress - amount
        else
            -- Esx logic please!
        end
        if newStress <= 0 then newStress = 0 end
    else newStress = 0 end
    if newStress > 100 then newStress = 100 end
    if qbType then
        player.Functions.SetMetaData('stress', newStress)
    else
        -- Esx save logic!
    end
    TriggerClientEvent('hud:client:UpdateStress', src, newStress)
    TriggerClientEvent('wert_hud:client:custom-notif', src, '', locale('notify.stress_removed'), 'inform')
end)

if config.Framework == 'esx' then
    lib.callback.register('wert_hud:server:esxGetPlayerData', function()
        local src = source
        local Player = Bridge.GetPlayer(src)
        if not Player then return {} end
        local userData = {}
        local jobData = Player.getJob()
        local plyAccounts = Player.getAccounts()
        local row = MySQL.single.await('SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ? LIMIT 1', { Player.getIdentifier() })
        userData.firstname = row and row.firstname or ''
        userData.lastname = row and row.lastname or ''
        userData.jobLabel = jobData.label
        userData.jobGrade = jobData.grade_label
        userData.cash = Player.getMoney() or 0
        userData.bank = plyAccounts and plyAccounts.bank and plyAccounts.bank.money or 0
    end)
end
```

</details>
