local ffi = require 'ffi'

-- Struktur CVector pouzity ve funkci
ffi.cdef[[
typedef struct {
    float x;
    float y;
    float z;
} CVector;
]]

ffi.cdef[[
typedef struct {
    long cx;
    long cy;
} SIZE;

void* GetModuleHandleA(const char* moduleName);
]]

--HOOKS
local hook = {hooks = {}}
addEventHandler('onScriptTerminate', function(scr)
    if scr == script.this then
        for i, hook in ipairs(hook.hooks) do
            if hook.status then
                hook.stop()
            end
        end
    end
end)
ffi.cdef [[
    int VirtualProtect(void* lpAddress, unsigned long dwSize, unsigned long flNewProtect, unsigned long* lpflOldProtect);
]]
function hook.new(cast, callback, hook_addr, size)
    jit.off(callback, true) --off jit compilation | thx FYP
    local size = size or 5
    local new_hook = {}
    local detour_addr = tonumber(ffi.cast('intptr_t', ffi.cast('void*', ffi.cast(cast, callback))))
    local void_addr = ffi.cast('void*', hook_addr)
    local old_prot = ffi.new('unsigned long[1]')
    local org_bytes = ffi.new('uint8_t[?]', size)
    ffi.copy(org_bytes, void_addr, size)
    local hook_bytes = ffi.new('uint8_t[?]', size, 0x90)
    hook_bytes[0] = 0xE9
    ffi.cast('uint32_t*', hook_bytes + 1)[0] = detour_addr - hook_addr - 5
    new_hook.call = ffi.cast(cast, hook_addr)
    new_hook.status = false
    local function set_status(bool)
        new_hook.status = bool
        ffi.C.VirtualProtect(void_addr, size, 0x40, old_prot)
        ffi.copy(void_addr, bool and hook_bytes or org_bytes, size)
        ffi.C.VirtualProtect(void_addr, size, old_prot[0], old_prot)
    end
    new_hook.stop = function() set_status(false) end
    new_hook.start = function() set_status(true) end
    new_hook.start()
    table.insert(hook.hooks, new_hook)
    return setmetatable(new_hook, {
        __call = function(self, ...)
            self.stop()
            local res = self.call(...)
            self.start()
            return res
        end
    })
end
--HOOKS


-- Definice pro operace souvisejici s textovym merenim
local OFFSET_CFONTS = 0x21A0FC  -- Predpokladany ofset
local OFFSET_MEASURE_TEXT_FUNC = 0x66BD0  -- Predpokladany ofset

local SAMP_BASE = getModuleHandle('samp.dll')

-- Definice funkci, ktere se vyuzivaji v ruznych castech skriptu
local drawHealthBarFuncAddress = SAMP_BASE + 0x689C0
local drawHealthBarFunc = ffi.cast("void(__thiscall*)(void*, CVector*, float, float, float)", drawHealthBarFuncAddress)

local size = ffi.new("SIZE")
local measureTextFunc = ffi.cast("void(__thiscall*)(void*, SIZE*, const char*, int)", SAMP_BASE + OFFSET_MEASURE_TEXT_FUNC) -- вынес из sampGetMeasuredTextSize ибо крашило как раз на этом моменте

local getBonePositionFunc = ffi.cast("int (__thiscall*)(void*, float*, int, bool)", 0x5E4280)


local function sampGetMeasuredTextSize(text)
--    print("Вошли в функцию sampGetMeasuredTextSize(text)")
    
    local base = SAMP_BASE
--    print("Получено значение базы:", base)
    
    if base == 0 then
    --    print("Ошибка: Nie udalo sie pobrac bazowego adresu modulu SAMP.")
        return nil -- Краткое завершение, если база не получена
    end

--    print("Адрес базы корректен:", SAMP_BASE)

    local g_fonts_address = ffi.cast("void**", base + OFFSET_CFONTS)
--    print("g_fonts_address рассчитан:", g_fonts_address)

    local g_fonts = ffi.cast("void*", g_fonts_address[0])
--    print("g_fonts получен:", g_fonts)

    -- Проверка указателя шрифтов
    if g_fonts == nil then
    --    print("Ошибка: g_fonts is nil")
        return nil
    end

--    print("проверка  g_fonts проверена ептыть")

    -- Подготовка размера для измерения
    local size = ffi.new("SIZE")
--    print("Структура SIZE инициализирована:", size.cx, size.cy)

    measureTextFunc(g_fonts, size, text, bit.bor(0x0000, 0x400)) -- DT_LEFT | DT_NOCLIP
--    print("Измерение текста выполнено. Размер:", size.cx, size.cy)

--    print("Вышли из функции sampGetMeasuredTextSize(text)")
    return size
end

-- Funkce pro ziskani polohy casti tela
local function getBodyPartCoordinates(boneId, pedHandle)
    local pedPointer = getCharPointer(pedHandle)
    local coordinates = ffi.new("float[3]")
    getBonePositionFunc(ffi.cast("void*", pedPointer), coordinates, boneId, true)
    return coordinates[0], coordinates[1], coordinates[2]
end

-- Funkce vypoctu offsetu
function f(x)
    return 0.84 + 0.0216625 * (x - 10)
end

-- Константы и переменные
local textId = 2014
local position = ffi.new("CVector")


-- Hook pro upraveni chovani EndHealthBar
function EndHealthBarHook(playerTags)
--    print("Starting EndHealthBarHook")

    local handle = playerPed
--    print("Player handle obtained:", handle)
    
    if sampIs3dTextDefined(textId) then
    --    print("3D text with ID", textId, "is defined, destroying it.")
        sampDestroy3dText(textId)
    end

    if sampIs3dTextDefined(textId + 1) then
    --    print("3D text with ID", textId + 1, "is defined, destroying it.")
        sampDestroy3dText(textId + 1)
    end

--    print"do poluchenia coord"
    local charX, charY, charZ = getCharCoordinates(playerPed)
--    print("Character coordinates:", charX, charY, charZ)

    local limbX, limbY, limbZ = getBodyPartCoordinates(8, playerPed)
--    print("Body part coordinates:", limbX, limbY, limbZ)

    local cameraX, cameraY, cameraZ = getActiveCameraCoordinates()
--    print("Camera coordinates:", cameraX, cameraY, cameraZ)

    local cameraDistance = getDistanceBetweenCoords3d(charX, charY, charZ, cameraX, cameraY, cameraZ)
--    print("Camera distance:", cameraDistance)

    local text = "{ffffff}pururin (30)"
--    print("Text to display:", text)

    local measuredSize = sampGetMeasuredTextSize(text)
--    print("Measured text size:", measuredSize.cx, measuredSize.cy)

    local offse = f(measuredSize.cy)
--    print("Offset calculated:", offse)

    position.x, position.y, position.z = limbX, limbY, limbZ
--    print("Position set:", position.x, position.y, position.z)

--    print("Setting render state")
    renderSetRenderState(19, 2) -- Nastaveni rendering 
    renderSetRenderState(20, 3)
    renderSetRenderState(27, 1)

--    print("Drawing health bar")
    drawHealthBarFunc(playerTags, position, 56.0, 0.0, cameraDistance)

    limbZ = limbZ + 0.21 + ((cameraDistance * 0.047499999) * offse)
--    print("Updated limbZ:", limbZ)

    limbZ2 = limbZ + ((cameraDistance * 0.047499999) * offse) / 2
--    print("Calculated limbZ2:", limbZ2)

--    print("Creating 3D text")
    sampCreate3dTextEx(textId, text, 0xFFFFFFFF, limbX, limbY, limbZ, 600, false, -1, -1)
    sampCreate3dTextEx(textId + 1, "{d0aeeb} * patrzcie, umiem grac roleplay", 0xFFFFFFFF, limbX, limbY, limbZ2, 600, false, -1, -1)

--    print("Ending EndHealthBarHook")
    return EndHealthBar(playerTags)
end

-- Main funkce pro spusteni hooku
function main()
    EndHealthBarFuncAddress = SAMP_BASE + 0x68670
    EndHealthBar = hook.new(
        'void* (__thiscall *)(void *this)',
        EndHealthBarHook,
        EndHealthBarFuncAddress
    )
end


почисти код, хочу видеть логичный порядок объявления функций и переменных, хорошо структурированный и упорядоченный