Загрузка данных


script_name('HUD Bars')
script_author('Claude')
script_version('1.0')

require 'lib.moonloader'
local inicfg   = require 'inicfg'
local encoding = require 'encoding'
local imgui    = require 'mimgui'
local sampev   = require 'samp.events'

-- Файл должен быть сохранён в UTF-8
encoding.default = 'CP1251'
local u8 = encoding.UTF8
local function t(s) return u8:decode(s) end

local CFG_NAME = 'hudbars.ini'
local cfg = inicfg.load({
    main = { enabled = true, interval = 45, low = 20 }
}, CFG_NAME)

-- размеры полосок
local BAR_W, BAR_H, GAP, BOTTOM = 230, 16, 14, 40

local hunger = nil          -- 0..100, пока не получили ответ сервера: nil
local waitUntil = 0         -- до какого момента ждём ответ на /satiety
local nextPoll = 0
local debugMode = false
local lowWarned = false

local function save() inicfg.save(cfg, CFG_NAME) end

local function msg(text)
    sampAddChatMessage(t('{00BFFF}[HUD]{FFFFFF} ' .. text), -1)
end

-- ===== Разбор ответа на /satiety =====
local function parseHunger(raw)
    local clean = raw:gsub('{%x%x%x%x%x%x}', '')
    local text = u8:encode(clean)   -- cp1251 -> utf8 для поиска слов

    local hasWord = text:find('сыт', 1, true) or text:find('Сыт', 1, true)
        or text:find('голод', 1, true) or text:find('Голод', 1, true)
        or text:find('СЫТ', 1, true) or text:find('ГОЛОД', 1, true)
    if not hasWord then return nil end

    local a, b = clean:match('(%d+)%s*/%s*(%d+)')
    local value
    if a and b and tonumber(b) > 0 then
        value = tonumber(a) / tonumber(b) * 100
    else
        local n = clean:match('(%d+[%.,]?%d*)')
        value = n and tonumber((n:gsub(',', '.')))
    end
    if not value then return nil end
    return math.max(0, math.min(100, value))
end

function sampev.onServerMessage(color, text)
    if os.clock() > waitUntil then return end

    local value = parseHunger(text)
    if value then
        hunger = value
        waitUntil = 0
        if debugMode then
            msg(('Поймал ответ: {FFD700}%d%%'):format(math.floor(value)))
        end
        return false    -- прячем ответ сервера из чата
    elseif debugMode then
        msg('Сообщение в окне ожидания (не подошло): ' .. text)
    end
end

-- ===== Отрисовка =====
local function col(r, g, b, a)
    return imgui.ColorConvertFloat4ToU32(imgui.ImVec4(r, g, b, a or 1))
end

local function drawBar(dl, x, y, frac, color, label)
    local p1 = imgui.ImVec2(x, y)
    local p2 = imgui.ImVec2(x + BAR_W, y + BAR_H)
    dl:AddRectFilled(p1, p2, col(0, 0, 0, 0.55), 4)
    if frac and frac > 0 then
        local fx = x + BAR_W * math.max(0, math.min(1, frac))
        dl:AddRectFilled(p1, imgui.ImVec2(fx, y + BAR_H), color, 4)
    end
    dl:AddRect(p1, p2, col(0, 0, 0, 0.9), 4)

    local size = imgui.CalcTextSize(label)
    dl:AddText(imgui.ImVec2(x + (BAR_W - size.x) / 2, y + (BAR_H - size.y) / 2),
        col(1, 1, 1, 1), label)
end

imgui.OnFrame(
    function() return cfg.main.enabled and isSampAvailable() end,
    function(self)
        self.HideCursor = true
        local dl = imgui.GetBackgroundDrawList()
        local sw, sh = getScreenResolution()
        local x = (sw - (BAR_W * 2 + GAP)) / 2
        local y = sh - BOTTOM - BAR_H

        -- голод: оранжевая, мигает красным при низком уровне
        local hungerColor = col(1.0, 0.55, 0.0, 0.95)
        local hungerLabel = '--'
        if hunger then
            hungerLabel = math.floor(hunger) .. '%'
            if hunger <= cfg.main.low and math.floor(os.clock() * 2) % 2 == 0 then
                hungerColor = col(0.9, 0.1, 0.1, 0.95)
            end
        end
        drawBar(dl, x, y, hunger and hunger / 100 or 0, hungerColor, hungerLabel)

        -- броня: серая
        local armour = getCharArmour(PLAYER_PED)
        drawBar(dl, x + BAR_W + GAP, y, armour / 100,
            col(0.7, 0.7, 0.7, 0.95), math.floor(armour) .. '%')
    end
)

-- ===== Основной цикл =====
function main()
    while not isSampAvailable() do wait(100) end

    sampRegisterChatCommand('hudbars', function(arg)
        arg = (arg or ''):match('^%s*(.-)%s*$')

        if arg == 'on' then
            cfg.main.enabled = true
            save()
            msg('Полоски включены.')
        elseif arg == 'off' then
            cfg.main.enabled = false
            save()
            msg('Полоски выключены.')
        elseif arg == 'now' then
            nextPoll = 0
            msg('Обновляю голод...')
        elseif arg == 'debug' then
            debugMode = not debugMode
            msg(debugMode and 'Отладка включена.' or 'Отладка выключена.')
        elseif arg:match('^low%s+%d+$') then
            cfg.main.low = tonumber(arg:match('(%d+)'))
            save()
            msg('Порог предупреждения о голоде: {FFD700}' .. cfg.main.low .. '%')
        elseif arg:match('^time%s+%d+$') then
            local sec = tonumber(arg:match('(%d+)'))
            if sec < 15 then
                msg('Не чаще раза в 15 секунд, иначе будет флуд.')
            else
                cfg.main.interval = sec
                save()
                msg('Интервал проверки голода: {FFD700}' .. sec .. ' сек.')
            end
        else
            msg('Команды: /hudbars on | off | now | debug | low [число] | time [сек]')
        end
    end)

    msg('Загружен. Настройки: {FFD700}/hudbars')

    while true do
        wait(0)

        if cfg.main.enabled and sampIsLocalPlayerSpawned() and os.clock() >= nextPoll then
            nextPoll = os.clock() + cfg.main.interval
            waitUntil = os.clock() + 4
            sampSendChat('/satiety')
        end

        -- предупреждение в чат, когда голод упал ниже порога
        if hunger then
            if hunger <= cfg.main.low and not lowWarned then
                lowWarned = true
                msg('{FF4444}Голод ниже ' .. cfg.main.low .. '%! Пора покормить персонажа.')
            elseif hunger > cfg.main.low + 5 then
                lowWarned = false
            end
        end
    end
end