Загрузка данных
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