local ffi = require 'ffi'

-- Structure definition for CVector, as it is used in the function
ffi.cdef[[
typedef struct {
    float x;
    float y;
    float z;
} CVector;
]]


--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


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

-- Получить координаты части тела
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



-- Define the function address for CPlayerTags::DrawHealthBar
local drawHealthBarFuncAddress = getModuleHandle('samp.dll') + 0x689C0
local drawHealthBarFunc = ffi.cast("void(__thiscall*)(void*, CVector*, float, float, float)", drawHealthBarFuncAddress)

-- Define the CPlayerTags instance
local CPlayerTags = ffi.cast("void*", 0x043c9000)

-- Main execution loop
local position = ffi.new("CVector")
position.x, position.y, position.z = 0.0, 0.0, 2.0

		
		
		
		
		
		
		
		
		-- Предположим, у нас есть следующие оффсеты из структуры samp_addressess
local OFFSET_CFONTS = 0x21A0FC -- замените реальным значением
local OFFSET_MEASURE_TEXT_FUNC = 0x66BD0 -- замените реальным значением

-- Основная функция, аналог sampGetMeasuredTextSize в C++
function sampGetMeasuredTextSize(text)
    local base = sampGetBase()
    if base == 0 then
        error("Не удалось получить базовый адрес модуля SAMP.")
    end

    -- Получаем указатель на глобальную переменную CFonts
    local g_fonts_address = ffi.cast("void**", base + OFFSET_CFONTS)
    local g_fonts = ffi.cast("void*", g_fonts_address[0])

    -- Определяем функцию измерения текста
    local measureTextFunc = ffi.cast("void(__thiscall*)(void*, SIZE*, const char*, int)", base + OFFSET_MEASURE_TEXT_FUNC)

    -- Структура для размера текста
    local size = ffi.new("SIZE")

    -- Вызов функции
    measureTextFunc(g_fonts, size, text, bit.bor(0x0000, 0x400)) -- DT_LEFT | DT_NOCLIP

    return size
end

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

		
		
		
		
		
		
function CPedRenderHook(obj)
	local charPointer = tonumber(ffi.cast("uintptr_t", obj))
	
	local handle	= getCharPointerHandle(charPointer)
	local res, id	= sampGetPlayerIdByCharHandle(handle)
	
	if id == 28 then -- == playerPed then 	
		if sampIs3dTextDefined(textIdentifier) then
			sampDestroy3dText(textIdentifier)
		end
	
		local characterId = handle
		
		local charX, charY, charZ = getCharCoordinates(characterId)
		local limbX, limbY, limbZ = getBodyPartCoordinates(8, characterId)

		local cameraX, cameraY, cameraZ = getActiveCameraCoordinates()
		local cameraDistance = getDistanceBetweenCoords3d(charX, charY, charZ, cameraX, cameraY, cameraZ)
		
		local text = "{ff70ff}shgaw (30)"
		local measuredSize = sampGetMeasuredTextSize(text)
		local offse = f(measuredSize.cy)
		
		--limbZ1 = limbZ + 0.21 + ((cameraDistance * 0.047499999))
		limbZ = limbZ + 0.21 + ((cameraDistance * 0.047499999) * offse)
		
		sampCreate3dTextEx(textIdentifier, text, 0xFFFFFFFF, limbX, limbY, limbZ, 600, false, -1, -1)
		
		local position = ffi.new("CVector")
		position.x, position.y, position.z = charX, charY, charZ
		drawHealthBarFunc(CPlayerTags, position, 100.0, 50.0, 15.0)
	end
	
	return CPedRender(obj)
end

function main()
    local renderFuncAddress = 0x5E7680 -- Address for CPed::Render

    CPedRender = hook.new(
        'void(__thiscall *)(void *obj)', 
        CPedRenderHook, 
        renderFuncAddress
    )
end


какие у мя в коде есть ошибки?