Полезные сниппеты и функции

ARMOR

011110000111100101101001
Модератор
4,969
6,890
Описание: Устанавливает модель и текстуру одежды CJ по хендлу
Lua:
local ffi = require("ffi")
local memory = require("memory")

function rebuildPlayer(nHandle, bIgnoreFatAndMuscle)
    local pPed = getCharPointer(nHandle);
    ffi.cast("void(__cdecl*)(void*, bool)", 0x5A82C0)(ffi.cast("void*", pPed), bIgnoreFatAndMuscle);
end

function setPlayerClothes(nHandle, szTexture, szModel, nClothesValue)
    local pPed = getCharPointer(nHandle);
    local pPlayerData = memory.getuint32(pPed + 0x480, true);
    local pClothes = memory.getuint32(pPlayerData + 0x04, true);
    ffi.cast("void(__thiscall*)(void*, char*, char*, int)", 0x5A8080)(ffi.cast("void*", pClothes), ffi.cast("char*", szTexture), ffi.cast("char*", szModel), nClothesValue);
    rebuildPlayer(nHandle, 0);
end

Пример использования:
Lua:
setPlayerClothes(playerPed, "VESTBLACK", "VEST", 0);

Чем отличается от givePlayerClothesOutsideShop: givePlayerClothesOutsideShop работает через playerHandle, поэтому нельзя было устанавливать другим игрокам одежду, тут же через playerPed можно устанавливать.

Скриншот:
1717688271227.jpeg
 

cord

Хватит спать на мне
Проверенный
571
470
Описание: Возвращает координаты точки, параллельно вектору скорости, на заданном расстоянии от автомобиля.
Код:
Lua:
function getPointInFrontOfCar(carHandle, distance)
    distance = -distance
    local carSpeedVector = {getCarSpeedVector(carHandle)}
    local carPos = {getCarCoordinates(carHandle)}
    local pointInFront = {carPos[1] + carSpeedVector[1], carPos[2] + carSpeedVector[2], carPos[3] + carSpeedVector[3]}
    local speedLength = math.sqrt(carSpeedVector[1] * carSpeedVector[1] + carSpeedVector[2] * carSpeedVector[2] + carSpeedVector[3] * carSpeedVector[3])
    local normalizedSpeedVector = {carSpeedVector[1] / speedLength, carSpeedVector[2] / speedLength, carSpeedVector[3] / speedLength}
    local scaledVector = {normalizedSpeedVector[1] * distance, normalizedSpeedVector[2] * distance, normalizedSpeedVector[3] * distance}
    return {pointInFront[1] - scaledVector[1], pointInFront[2] - scaledVector[2], pointInFront[3] - scaledVector[3]}
end

Пример использования:
Lua:
function main()
    repeat wait(100) until isSampAvailable()
    while true do
        wait(0)
        if isCharSittingInAnyCar(PLAYER_PED) then
            local carHandle =  storeCarCharIsInNoSave(PLAYER_PED)
            if getCarSpeed(carHandle) ~= 0 then
                local pointInFront = getPointInFrontOfCar(carHandle, 5)
                local carSpeedVector = {getCarSpeedVector(carHandle)}
                local carPos = {getCarCoordinates(carHandle)}
                local screenCarPos = {convert3DCoordsToScreen(carPos[1], carPos[2], carPos[3])}
                local screenPointInFront = {convert3DCoordsToScreen(pointInFront[1] - carSpeedVector[1], pointInFront[2] - carSpeedVector[2], pointInFront[3] - carSpeedVector[3])}
                renderDrawLine(screenCarPos[1], screenCarPos[2], screenPointInFront[1], screenPointInFront[2], 2, 0xFFffffff)
            end
        end
    end
end
1717858252601.png
 

imring

Ride the Lightning
Всефорумный модератор
2,365
2,552
Описание: Возвращает координаты точки, параллельно вектору скорости, на заданном расстоянии от автомобиля.
Код:
Lua:
function getPointInFrontOfCar(carHandle, distance)
    distance = -distance
    local carSpeedVector = {getCarSpeedVector(carHandle)}
    local carPos = {getCarCoordinates(carHandle)}
    local pointInFront = {carPos[1] + carSpeedVector[1], carPos[2] + carSpeedVector[2], carPos[3] + carSpeedVector[3]}
    local speedLength = math.sqrt(carSpeedVector[1] * carSpeedVector[1] + carSpeedVector[2] * carSpeedVector[2] + carSpeedVector[3] * carSpeedVector[3])
    local normalizedSpeedVector = {carSpeedVector[1] / speedLength, carSpeedVector[2] / speedLength, carSpeedVector[3] / speedLength}
    local scaledVector = {normalizedSpeedVector[1] * distance, normalizedSpeedVector[2] * distance, normalizedSpeedVector[3] * distance}
    return {pointInFront[1] - scaledVector[1], pointInFront[2] - scaledVector[2], pointInFront[3] - scaledVector[3]}
end

Пример использования:
Lua:
function main()
    repeat wait(100) until isSampAvailable()
    while true do
        wait(0)
        if isCharSittingInAnyCar(PLAYER_PED) then
            local carHandle =  storeCarCharIsInNoSave(PLAYER_PED)
            if getCarSpeed(carHandle) ~= 0 then
                local pointInFront = getPointInFrontOfCar(carHandle, 5)
                local carSpeedVector = {getCarSpeedVector(carHandle)}
                local carPos = {getCarCoordinates(carHandle)}
                local screenCarPos = {convert3DCoordsToScreen(carPos[1], carPos[2], carPos[3])}
                local screenPointInFront = {convert3DCoordsToScreen(pointInFront[1] - carSpeedVector[1], pointInFront[2] - carSpeedVector[2], pointInFront[3] - carSpeedVector[3])}
                renderDrawLine(screenCarPos[1], screenCarPos[2], screenPointInFront[1], screenPointInFront[2], 2, 0xFFffffff)
            end
        end
    end
end
пришлось пару минут потратить, чтобы переварить этот код.
в getPointInFrontOfCar ты прибавляешь вектор скорости, а в main отнимаешь, и по итогу остается лишь прибавка normalize вектора. зачем?
для читабельности используй Vector3D
Lua:
local Vector3D = require 'vector3d'

local Vector2D = function (x, y)
    return Vector3D(x, y, 0)
end

local function getPointInFrontOfCar(carHandle, distance)
    local normalizedCarVelocity = Vector3D(getCarSpeedVector(carHandle))
    normalizedCarVelocity:normalize()
    local carPos = Vector3D(getCarCoordinates(carHandle))
    local pointInFront = carPos + normalizedCarVelocity * distance
    return pointInFront
end

local function renderVehicleVelocity()
    if not isCharSittingInAnyCar(PLAYER_PED) then
        return
    end

    local EPSILON = 0.0001
    local carHandle = storeCarCharIsInNoSave(PLAYER_PED)
    if getCarSpeed(carHandle) < EPSILON then
        return
    end

    local distance = 5
    local carPos = Vector3D(getCarCoordinates(carHandle))
    local pointInFront = getPointInFrontOfCar(carHandle, distance)

    local screenCarPos = Vector2D(convert3DCoordsToScreen(carPos:get()))
    local screenPointInFront = Vector2D(convert3DCoordsToScreen(pointInFront:get()))
    renderDrawLine(screenCarPos.x, screenCarPos.y, screenPointInFront.x, screenPointInFront.y, 2, 0xFFFFFFFF)
end

function main()
    while true do
        wait(0)
        renderVehicleVelocity()
    end
end
 
  • Нравится
  • Влюблен
Реакции: whyega52, Willy4ka и cord

Gorskin

♥ Love Lua ♥
Проверенный
1,339
1,178
Описание: Функция заменяет оригинальный метод создания скриншота, т.к стандартный делает это долго, больно и мучительно.
Данный сниппет предоставлен в виде готового скрипта чтобы вы могли CTRL+C CTRL+V в свой код.




p.s Лютый колхоз через bmp.

Lua:
local ffi = require("ffi")

local sampBase = getModuleHandle("samp.dll")

ffi.cdef[[
    typedef struct tagBITMAPINFOHEADER {
        uint32_t      biSize;
        int32_t       biWidth;
        int32_t       biHeight;
        uint16_t      biPlanes;
        uint16_t      biBitCount;
        uint32_t      biCompression;
        uint32_t      biSizeImage;
        int32_t       biXPelsPerMeter;
        int32_t       biYPelsPerMeter;
        uint32_t      biClrUsed;
        uint32_t      biClrImportant;
    } BITMAPINFOHEADER;

    typedef struct tagRGBQUAD {
        uint8_t    rgbBlue;
        uint8_t    rgbGreen;
        uint8_t    rgbRed;
        uint8_t    rgbReserved;
    } RGBQUAD;

    typedef struct tagBITMAPINFO {
        BITMAPINFOHEADER    bmiHeader;
        RGBQUAD             bmiColors[1];
    } BITMAPINFO;

    typedef struct HDC__ HDC;
    HDC* GetDC(void* hWnd);
    int ReleaseDC(void* hWnd, HDC* hDC);
    int GetDeviceCaps(HDC* hdc, int nIndex);
    HDC* CreateCompatibleDC(HDC* hdc);
    void* CreateCompatibleBitmap(HDC* hdc, int32_t nWidth, int32_t nHeight);
    void* SelectObject(HDC* hdc, void* h);
    int BitBlt(HDC* hdcDest, int32_t nXDest, int32_t nYDest, int32_t nWidth, int32_t nHeight, HDC* hdcSrc, int32_t nXSrc, int32_t nYSrc, uint32_t dwRop);
    int GetDIBits(HDC* hdc, void* hbmp, uint32_t uStartScan, uint32_t cScanLines, void* lpvBits, BITMAPINFO* lpbi, uint32_t uUsage);
    int DeleteDC(HDC* hdc);
    int DeleteObject(void* hObject);
]]

local function saveBMP(filename, buffer, width, height)
    local file = io.open(filename, "wb")
    if file then
        local header = string.char(0x42, 0x4D)
        local fileSize = 14 + 40 + width * height * 3
        local offset = 14 + 40

        file:write(header)
        file:write(string.char(fileSize % 256, math.floor(fileSize / 256) % 256, math.floor(fileSize / 65536) % 256, math.floor(fileSize / 16777216)))
        file:write(string.rep("\0", 4))
        file:write(string.char(offset % 256, math.floor(offset / 256) % 256, math.floor(offset / 65536) % 256, math.floor(offset / 16777216)))
        file:write(string.char(40, 0, 0, 0)) -- BITMAPINFOHEADER size
        file:write(string.char(width % 256, math.floor(width / 256) % 256, math.floor(width / 65536) % 256, math.floor(width / 16777216)))
        file:write(string.char(height % 256, math.floor(height / 256) % 256, math.floor(height / 65536) % 256, math.floor(height / 16777216)))
        file:write(string.char(1, 0))
        file:write(string.char(24, 0))
        file:write(string.rep("\0", 24))
        file:write(buffer)
        file:close()
        return true
    else
        return false
    end
end

local function takeScreenshot()
    local desktopDC = ffi.C.GetDC(nil)
    local screenWidth = ffi.C.GetDeviceCaps(desktopDC, 8) 
    local screenHeight = ffi.C.GetDeviceCaps(desktopDC, 10)

    local compatibleDC = ffi.C.CreateCompatibleDC(desktopDC)
    local bitmap = ffi.C.CreateCompatibleBitmap(desktopDC, screenWidth, screenHeight)
    ffi.C.SelectObject(compatibleDC, bitmap)
    ffi.C.BitBlt(compatibleDC, 0, 0, screenWidth, screenHeight, desktopDC, 0, 0, 13369376)

    local bi = ffi.new("BITMAPINFO")
    bi.bmiHeader.biSize = ffi.sizeof("BITMAPINFOHEADER")
    bi.bmiHeader.biWidth = screenWidth
    bi.bmiHeader.biHeight = screenHeight
    bi.bmiHeader.biPlanes = 1
    bi.bmiHeader.biBitCount = 24
    bi.bmiHeader.biCompression = 0
    bi.bmiHeader.biSizeImage = 0
    bi.bmiHeader.biXPelsPerMeter = 0
    bi.bmiHeader.biYPelsPerMeter = 0
    bi.bmiHeader.biClrUsed = 0
    bi.bmiHeader.biClrImportant = 0

    local buffer = ffi.new("uint8_t[?]", screenWidth * screenHeight * 3)
    ffi.C.GetDIBits(desktopDC, bitmap, 0, screenHeight, buffer, bi, 0)

    local documentsPath = os.getenv("USERPROFILE") .. "\\Documents\\GTA San Andreas User Files\\SAMP\\screens\\"
    local fileNameBase = "sa-mp-%03i.png"
    local fileName = ""
    local index = 0
    repeat
        index = index + 1
        fileName = documentsPath .. string.format(fileNameBase, index)
    until not doesFileExist(fileName)

    if saveBMP(fileName, ffi.string(buffer, screenWidth * screenHeight * 3), screenWidth, screenHeight) then
        sampAddChatMessage("Screenshot Taken: "..string.format(fileNameBase, index), -1)
    else
        sampAddChatMessage("Failed to save screenshot.", -1)
    end

    ffi.C.DeleteObject(bitmap)
    ffi.C.DeleteDC(compatibleDC)
    ffi.C.ReleaseDC(nil, desktopDC)
end

local function doesFileExist(fileName)
    local file = io.open(fileName, "r")
    if file then
        file:close()
        return true
    end
    return false
end

function main()
    while not isSampAvailable() do wait(100) end
    --nop original take screenshot
    if get_samp_version() == "r1" then
        writeMemory(sampBase+0x7115D, 4, 0x90909090, true)
        writeMemory(sampBase+0x7115D+4, 1, 0x90, true)
    elseif get_samp_version() == "r3" then
        writeMemory(sampBase+0x7504D, 4, 0x90909090, true)
        writeMemory(sampBase+0x7504D+4, 1, 0x90, true)
    end
    while true do
        wait(0)
        if isKeyJustPressed(0x77) then
            takeScreenshot()
        end
    end
end

addEventHandler('onScriptTerminate', function(scr)
    if scr == thisScript() then
        --return original take screenshot
        if get_samp_version() == "r1" then
            writeMemory(sampBase+0x7115D, 4, 0xFFFE5EE8, true)
            writeMemory(sampBase+0x7115D+4, 1, 0xFF, true)
        elseif get_samp_version() == "r3" then
            writeMemory(sampBase+0x7504D, 4, 0xFFFE5EE8, true)
            writeMemory(sampBase+0x7504D+4, 1, 0xFF, true)
        end
    end
end)

function get_samp_version()
    local e_lfanew = ffi.cast("long*", sampBase + 60)[0]
    local nt_header = sampBase + e_lfanew
    local entry_point_addr = ffi.cast("unsigned int*", nt_header + 40)[0]
    local versions = {
        [0x31DF13] = "r1",
        [0x3195DD] = "r2",
        [0xCC4D0] = "r3",
        [0xCBCB0] = "r4",
        [0xFDB60] = "dl"
    }
    return versions[entry_point_addr] or "unknown"

end
 

whyega52

Гений, миллионер, плейбой, долбаеб
Модератор
2,798
2,659
p.s Лютый колхоз через bmp.
Если я правильно помню, то только с PNG такая проблема. Поэтому для нормального сохранения достаточно смены 2 параметра у функции D3DXSaveSurfaceToFileA
1718638606881.png
Перечисление всех форматов можно посмотреть тут, в SAMP используется D3DXIFF_PNG, следовательно передается 3.
Для изменения формата нам достаточно перезаписать один байт
1718638667964.png
Lua:
local memory = require("memory")

local format = 1 -- JPG
memory.setint8(getModuleHandle("samp.dll") + 0x74FBA, format, true)
 

Вложения

  • 1718638665669.png
    1718638665669.png
    9.8 KB · Просмотры: 160
  • 1718638589364.png
    1718638589364.png
    54.5 KB · Просмотры: 157

PanSeek

t.me/dailypanseek
Всефорумный модератор
908
1,776
Описание: Когда-то нужно было сделать собственные события. Сделал простое решение, возможно кому-то тоже понадобятся. Вы можете сделать свои названия обработчиков и функции к ним, на все что хотите. Не проверял на большой нагрузке с большими данными.
Методы:
new - создает экземпляр класса;
add - добавляет событие;
on - вешает слушатель событий;
once - вешает единоразовый слушатель событий (сработает один раз);
off - убирает слушатель событий.

Код:
Lua:
local CustomEvent = {}
CustomEvent.__index = CustomEvent

function CustomEvent:new()
    local instance = {
        events = {}
    }
    setmetatable(instance, CustomEvent)
    return instance
end

function CustomEvent:add(event, ...)
    if not self.events[event] then return end
    for _, listener in ipairs(self.events[event]) do
        listener(...)
    end
end

function CustomEvent:on(event, listener)
    if not self.events[event] then
        self.events[event] = {}
    end
    table.insert(self.events[event], listener)
end

function CustomEvent:once(event, listener)
    local function wrapper(...)
        listener(...)
        self:off(event, wrapper)
    end
    self:on(event, wrapper)
end

function CustomEvent:off(event, listener)
    if not self.events[event] then return end
    for i, l in ipairs(self.events[event]) do
        if l == listener then
            table.remove(self.events[event], i)
            return
        end
    end
end
Minify версия:
Lua:
local CustomEvent={}CustomEvent.__index=CustomEvent;function CustomEvent:new()local b={events={}}setmetatable(b,a)return b end;function CustomEvent:add(c,...)if not self.events[c]then return end;for d,e in ipairs(self.events[c])do e(...)end end;function CustomEvent:on(c,e)if not self.events[c]then self.events[c]={}end;table.insert(self.events[c],e)end;function CustomEvent:once(c,e)local function f(...)e(...)self:off(c,f)end;self:on(c,f)end;function CustomEvent:off(c,e)if not self.events[c]then return end;for g,h in ipairs(self.events[c])do if h==e then table.remove(self.events[c],g)return end end end

Пример использования:
Lua:
local event = CustomEvent:new()

function main()
    repeat wait(0) until isSampAvailable()
    event:add('loaded')
end

event:once('loaded', function()
    sampAddChatMessage('samp loaded', -1)
end)
Lua:
local event = CustomEvent:new()
local status = false

function main()
    repeat wait(0) until isSampAvailable()
    sampRegisterChatCommand('tl', function()
        status = not status
        event:add('status', status)
    end)
    wait(-1)
end

event:on('status', function(s)
    sampAddChatMessage('status: ' .. tostring(s), -1)
end)
Lua:
local event = CustomEvent:new()

function main()
    repeat wait(0) until isSampAvailable()

    local vehicleHealth = nil
    local oldDoorStatus = nil
    while true do
        if isCharInAnyCar(playerPed) then
            local veh = storeCarCharIsInNoSave(playerPed)
            local currentVehicleHealth = getCarHealth(veh)
            local doorStatus = getCarDoorLockStatus(veh)
            if currentVehicleHealth ~= vehicleHealth or oldDoorStatus ~= doorStatus then
                event:add('vehinfo', { current = currentVehicleHealth, old = vehicleHealth }, { current = doorStatus, old = oldDoorStatus })
                vehicleHealth = currentVehicleHealth
                oldDoorStatus = doorStatus
            end
        end
        wait(0)
    end
end

event:on('vehinfo', function(health, door)
    sampAddChatMessage('Current vehicle health: ' .. tostring(health.current) .. ' | Old: ' .. tostring(health.old), 0xAABBAA)
end)
gta_sa_EmJuV0gayd.png
Lua:
local imgui = require('mimgui')

local event = CustomEvent:new()

local win = imgui.new.bool(false)
local testCheck = imgui.new.bool(false)
local testCheck2 = imgui.new.bool(false)

imgui.OnInitialize(function()
    imgui.GetIO().IniFilename = nil
end)

local mainFrame = imgui.OnFrame(function() return win[0] end, function(self)
    imgui.SetNextWindowPos(imgui.ImVec2(1000, 500), imgui.Cond.FirstUseEver, imgui.ImVec2(0.5, 0.5))
    imgui.Begin('#MainFrame', win, imgui.WindowFlags.NoTitleBar + imgui.WindowFlags.AlwaysAutoResize)

    imgui.Checkbox('test checkbox', testCheck)
    imgui.Checkbox('test checkbox 2', testCheck2)

    imgui.End()
end)

CheckboxOrig = imgui.Checkbox
function imgui.Checkbox(...)
    local checkbox = CheckboxOrig(...)
    if checkbox then
        event:add('checkbox', ...)
    end
    return checkbox
end

event:on('checkbox', function(name, variable)
    sampAddChatMessage('CHECKBOX | {FFFFFF}' .. name .. ': ' .. tostring(variable[0]), 0xAA1188)
end)
gta_sa_rTaZiK6kiX.png
Можно добавить метод _get, для получения таблиц событий:
Lua:
function CustomEvent:_get()
    return self.events
end
Далее отображаем в gui:
Lua:
local imgui = require('mimgui')

local event = CustomEvent:new()

local win = imgui.new.bool(false)

imgui.OnInitialize(function()
    imgui.GetIO().IniFilename = nil
end)

local mainFrame = imgui.OnFrame(function() return win[0] end, function(self)
    imgui.SetNextWindowPos(imgui.ImVec2(1000, 500), imgui.Cond.FirstUseEver, imgui.ImVec2(0.5, 0.5))
    imgui.Begin('Settings', win, imgui.WindowFlags.NoTitleBar + imgui.WindowFlags.AlwaysAutoResize)

    for k, v in pairs(event:_get()) do
        imgui.Text(tostring(k) .. ' | ' .. tostring(v))
        for k2, v2 in ipairs(v) do
            imgui.Text('- ' .. tostring(k2) .. ' | ' .. tostring(v2))
        end
    end

    imgui.End()
end)
gta_sa_rRmevcVwBI.png
 

g305noobo

Известный
Модератор
307
477
описание: возвращает координаты точки перед игроком, куда смотрит камера
код:
Lua:
function get_camera_look_point(distance)
    local cam_x, cam_y, cam_z = getActiveCameraCoordinates()
    local at_x, at_y, at_z = getActiveCameraPointAt()

    local dir_x, dir_y, dir_z = at_x - cam_x, at_y - cam_y, at_z - cam_z
    local length = math.sqrt(dir_x^2 + dir_y^2 + dir_z^2)

    local norm_dir_x, norm_dir_y, norm_dir_z = dir_x / length, dir_y / length, dir_z / length

    return cam_x + distance * norm_dir_x, cam_y + distance * norm_dir_y, cam_z + distance * norm_dir_z
end
пример:
Lua:
local target_x, target_y, target_z = get_camera_look_point(5)
-- создаем диванчик перед лицом игрока
createObject(11685, target_x, target_y, target_z)
 
  • Эм
  • Нравится
Реакции: XRLM, Corenale и ChаtGPT

Corenale

луашер типа
Проверенный
167
339
описание: возвращает координаты точки перед игроком, куда смотрит камера
код:
Lua:
function get_camera_look_point(distance)
    local cam_x, cam_y, cam_z = getActiveCameraCoordinates()
    local at_x, at_y, at_z = getActiveCameraPointAt()

    local dir_x, dir_y, dir_z = at_x - cam_x, at_y - cam_y, at_z - cam_z
    local length = math.sqrt(dir_x^2 + dir_y^2 + dir_z^2)

    local norm_dir_x, norm_dir_y, norm_dir_z = dir_x / length, dir_y / length, dir_z / length

    return cam_x + distance * norm_dir_x, cam_y + distance * norm_dir_y, cam_z + distance * norm_dir_z
end
пример:
Lua:
local target_x, target_y, target_z = get_camera_look_point(5)
-- создаем диванчик перед лицом игрока
createObject(11685, target_x, target_y, target_z)
окда
Lua:
function govnaPacket(dist)
  local w, h = getScreenResolution()
  return convertScreenCoordsToWorld3D(w/2, h/2, dist)
end
 

Cosmo

Известный
Друг
656
2,744
Описание:
Рендер картинки по ссылке (Imgui). Помимо этого она кэшируется и скрипт не будет скачивать её каждую сессию, а только в том случае, если она была изменена на хосте.

Код:
Зависимости:
1) Функция асинхронных запросов (effil)
2) Библиотека MD5 (если есть рациональный способ обойтись без неё, то можете поделиться)

Синтаксис функции:
Код:
int status, [string error] = imgui.ImageURL(string url, imvec2 size, bool preload, imvec2 uv0, imvec2 uv1, imvec4 tint_col, imvec4 border_col)

Возвращаемые статусы:
0 = Инициализация загрузки
1 = Изображение загружается
2 = Ошибка загрузки изображения
3 = Изображение загружено с сервера и его кэш обновлён/сохранён,
4 = Изображение загружено из кэша (Он актуальный и обновлять его не требуется)
5 = Ошибка загрузки изображения, оно было взято из кэша (только при preload == true)

Функция (можно вставить в любое место вашего кода):
Lua:
local md5 = require "md5"

imgui.ImageURL = {
    cache_dir = getWorkingDirectory() .. "/resource/cache",
    download_statuses = {
        INIT = 0,
        DOWNLOADING = 1,
        ERROR = 2,
        SAVED = 3,
        NOT_MODIFIED = 4,
        CACHE_ONLY = 5
    },
    pool = {}
}

function imgui.ImageURL:set_cache(url, image_data, headers)
    if not doesDirectoryExist(self.cache_dir) then
        createDirectory(self.cache_dir)
    end

    local path = ("%s/%s"):format(self.cache_dir, md5.sumhexa(url))
    local file, err = io.open(path, "wb")
    if not file then
        return nil
    end

    local data = { Data = tostring(image_data) }
    if headers["etag"] then
        data["Etag"] = headers["etag"]
    end
    if headers["last-modified"] then
        data["Last-Modified"] = headers["last-modified"]
    end

    file:write(encodeJson(data))
    file:close()
    return path
end

function imgui.ImageURL:get_cache(url)
    local path = ("%s/%s"):format(self.cache_dir, md5.sumhexa(url))
    if not doesFileExist(path) then
        return nil, nil
    end

    local image_data = nil
    local cached_headers = {}

    local file, err = io.open(path, "rb")
    if file then
        local cache = decodeJson(file:read("*a"))
        if type(cache) ~= "table" then
            return nil, nil
        end

        if cache["Data"] ~= nil then
               image_data = cache["Data"]
           end
           if cache["Last-Modified"] ~= nil then
               cached_headers["If-Modified-Since"] = cache["Last-Modified"]
           end
           if cache["Etag"] ~= nil then
               cached_headers["If-None-Match"] = cache["Etag"]
           end

        file:close()
    end
    return image_data, cached_headers
end

function imgui.ImageURL:download(url, preload_cache)
    local st = self.download_statuses
    self.pool[url] = {
        status = st.DOWNLOADING,
        image = nil,
        error = nil
    }
    local cached_image, cached_headers = imgui.ImageURL:get_cache(url)
    local img = self.pool[url]

    if preload_cache and cached_image ~= nil then
        img.image = imgui.CreateTextureFromMemory(memory.strptr(cached_image), #cached_image)
    end

    asyncHttpRequest("GET", url, { headers = cached_headers },
        function(result)
            if result.status_code == 200 then
                img.image = imgui.CreateTextureFromMemory(memory.strptr(result.text), #result.text)
                img.status = st.SAVED
                imgui.ImageURL:set_cache(url, result.text, result.headers)
            elseif result.status_code == 304 then
                img.image = img.image or imgui.CreateTextureFromMemory(memory.strptr(cached_image), #cached_image)
                img.status = st.NOT_MODIFIED
            else
                img.status = img.image and st.CACHE_ONLY or st.ERROR
                img.error = ("Error #%s"):format(result.status_code)
            end
        end,
        function(error)
            img.status = img.image and st.CACHE_ONLY or st.ERROR
            img.error = error
        end
    )
end

function imgui.ImageURL:render(url, size, preload, ...)
    local st = self.download_statuses
    local img = self.pool[url]

    if img == nil then
        self.pool[url] = {
            status = st.INIT,
            error = nil,
            image = nil
        }
        img = self.pool[url]
    end

    if img.status == st.INIT then
        imgui.ImageURL:download(url, preload)
    end
        
    if img.image ~= nil then
        imgui.Image(img.image, size, ...)
    else
        imgui.Dummy(size)
    end
    return img.status, img.error
end

setmetatable(imgui.ImageURL, {
    __call = imgui.ImageURL.render
})
Возможны костыли, впервые работаю с кэшированием

Пример использования:
Lua:
imgui.ImageURL("https://items.shinoa.tech/images/model/2296.png", imgui.ImVec2(256, 256), true)

1720561109218.png


P.s. Аргумент true отвечает за предзагрузку изображения из кэша (если он есть) не дожидаясь ответа от сервера. То есть сначала загрузится картинка из кэша, затем отправится запрос, и если изображение на сервере уже другое, то подгрузит его

P.s.s. Третий аргумент не является обязательным, если его нет или он false, то функция всё равно сравнит картинку из кэша (если есть) с той, что хранится на сервере и обновит её кэш если она отличается

P.s.s.s. Если картинка в кэше актуальная, то скрипт ничего загружать не будет, тем самым экономя трафик
 

Corenale

луашер типа
Проверенный
167
339
Описание: Получает максимальную дистанцию, на которую может выстрелить оружие

Код:
code:
local getWeapInfo = ffi.cast("int32_t (__cdecl*)(int weaponID, int skill)", 0x743C60) -- не пихай меня в цикл пж
local getWeaponSkill = ffi.cast("uint8_t (__thiscall*)(int CPed)", 0x5E6580) -- и меня тоже не надо
function getGunDistance(gun) -- а мне похуй :)
    local currSkill = (gun<33) and getWeaponSkill(ffi.cast("int32_t*", 0xB7CD98)[0]) or 1
    return gun == 34 and 999 or ffi.cast("float*", getWeapInfo(gun, currSkill))[2] ~= 0 and ffi.cast("float*", getWeapInfo(gun, currSkill))[2] or ffi.cast("float*", getWeapInfo(gun, 1))[2]
end
-- бонус в виде получения скилла на гане, код редачните сами :))
getWeapInfo(gun, (gun<33) and getWeaponSkill(ffi.cast("int32_t*", 0xB7CD98)[0]) or 1)
 
Последнее редактирование:

Parazitas

Известный
20
9
Описание функции:
Отключает\Включает только чат, а не: чат, худ, миникарту как при двойном нажатии F7
Lua:
local memory = require 'memory'
local enable = false

function main()
    if not isSampfuncsLoaded() or not isSampLoaded() then return end
    while not isSampAvailable() do wait(100) end

    while true do
        wait(0)
        if testCheat("OO") then
            RadarAndHudControlOnF7()
        end
    end
end

function RadarAndHudControlOnF7()
    enable = not enable
    local version = getSampVersion()
    samp = getModuleHandle('samp.dll')
    if version == 1 -- R1
    then
        if enable 
        then
            memory.fill(samp + 0x7140F, 1, 1, true)
            sampAddChatMessage("Управление чатом включен!", -1)
        else
            memory.fill(samp + 0x7140F, 0, 1, true)
            sampAddChatMessage("Управление чатом отключен!", -1)
        end
    end
    if version == 2 -- R2
    then
        if enable 
        then
            memory.fill(samp + 0x714AF, 1, 1, true)
            sampAddChatMessage("Управление чатом включен!", -1)
        else
            memory.fill(samp + 0x714AF, 0, 1, true)
            sampAddChatMessage("Управление чатом отключен!", -1)
        end
    end
    if version == 3 -- 0.3DL
    then
        if enable 
        then
            memory.fill(samp + 0x7548F, 1, 1, true)
            sampAddChatMessage("Управление чатом включен!", -1)
        else
            memory.fill(samp + 0x7548F, 0, 1, true)
            sampAddChatMessage("Управление чатом отключен!", -1)
        end
    end
    if version == 4 -- R3
    then
        if enable 
        then
            memory.fill(samp + 0x752FF, 1, 1, true)
            sampAddChatMessage("Управление чатом включен!", -1)
        else
            memory.fill(samp + 0x752FF, 0, 1, true)
            sampAddChatMessage("Управление чатом отключен!", -1)
        end
    end
    if version == 5 -- R4
    then
        if enable 
        then
            memory.fill(samp + 0x75A3F, 1, 1, true)
            sampAddChatMessage("Управление чатом включен!", -1)
        else
            memory.fill(samp + 0x75A3F, 0, 1, true)
            sampAddChatMessage("Управление чатом отключен!", -1)
        end
    end
    if version == 6 -- R4-2
    then
        if enable 
        then
            memory.fill(samp + 0x75A6E, 1, 1, true)
            sampAddChatMessage("Управление чатом включен!", -1)
        else
            memory.fill(samp + 0x75A6E, 0, 1, true)
            sampAddChatMessage("Управление чатом отключен!", -1)
        end
    end
    if version == 7 -- R5
    then
        if enable 
        then
            memory.fill(samp + 0x75A0E, 1, 1, true)
            sampAddChatMessage("Управление чатом включен!", -1)
        else
            memory.fill(samp + 0x75A0E, 0, 1, true)
            sampAddChatMessage("Управление чатом отключен!", -1)
        end
    end
end

function get_samp_version_id()
    local versionid = 0
    samp10 = getModuleHandle('samp.dll')
    samp10 = samp10 + 0x128
    samp9 = readMemory(samp10, 4, true)
    if samp9 == 0x5542F47A then
        versionid = 1 -- r1
    end
    if samp9 == 0x59C30C94 then
        versionid = 2 -- r2
    end
    if samp9 == 0x5542F47A then
        versionid = 3 -- 0.3DL
    end

    samp10 = samp10 - 8
    samp9 = readMemory(samp10, 4, true)
    if samp9 == 0x5C0B4243 then
        versionid = 4 -- r3
    end
    if samp9 == 0x5DD606CD then
        versionid = 5 -- R4
    end
    if samp9 == 0x6094ACAB then
        versionid = 6 -- R4-2
   end
   if samp9 == 0x6372C39E then
        versionid = 7 --R5
    end
   return versionid
end
 

Gorskin

♥ Love Lua ♥
Проверенный
1,339
1,178
Описание: Проверяет смотрит ли камера на игрока.

Вопрос: Для чего это нужно?

Пояснение:
В сф есть проверка sampIsLocalPlayerSpawned()
Но в некоторых ситуация она плохо работает или не работает вовсе из-за другой версии сампа.
А данная функция может пригодиться когда допустим вам не нужно что-то отображать в реконе или иначе говоря до того пока камера не окажется за вашим персонажем.
Мне данная функция нужна для того чтобы не рисовать хп худ при подключении на сервер и в реконе.
Зайдя в idb базу гта са я нарыл данную проверку.
Screenshot_1.png

Код:
Lua:
function isLookingAtPlayer()
    return readMemory(0xB6F028+0x2B, 1, true) == 1
end
Пример использования:
Lua:
function main()
    while not isSampAvailable() do wait(0) end
    sampAddChatMessage("Проверка isSampAvailable() прошла", -1)
    while not isLookingAtPlayer() do wait(0) end
    sampAddChatMessage("Проверка isLookingAtPlayer() прошла", -1)
    wait(-1)
end

function isLookingAtPlayer()
    return readMemory(0xB6F028+0x2B, 1, true) == 1
end
 

Gorskin

♥ Love Lua ♥
Проверенный
1,339
1,178
Описание: Правильное отключение зеркал в игре. Не будет бага с черным небом если отключить зеркала в зеркальном интерьере а после выйти на улицу.
Lua:
if ini.settings.reflections then
    writeMemory(0xC7C724,4, 0x0, true)
    memory.fill(0x555854, 0x90, 5, true) --InterioRreflections
else
    memory.hex2bin('E897151D00', 0x555854, 5)
    writeMemory(0x7230C6, 2, 0x9090, true)--nop call    _ZN4Fx_c12GetFxQualityEv
end
 

Willy4ka

вилличка
Модератор
460
772
отображение времени в разных форматах
ФорматРезультат
t0:20
T0:20:30
d21.04.2021
D21 апреля 2021 г.
f *21 апреля 2021 г., 0:20
Fсреда, 21 апреля 2021 г., 0:20
R3 года назад
Lua:
os.setlocale('ru', 'time')
local function nForm(num, v1, v2, v3) if num < 11 or num > 19 then if num % 10 == 1 then return v1 elseif num % 10 >= 2 and num % 10 <= 4 then return v2 else return v3 end else return v3 end end
local function formatUnixTime(time, format) local month = {'января','февраля','марта','апреля','мая','июня','июля','августа','сентября','октября','ноября','декабря'} local forms = { { 'sec', 'секунду', 'секунды', 'секунд' }, { 'min', 'минуту', 'минуты', 'минут' }, { 'hour', 'час', 'часа', 'часов' }, { 'day', 'день', 'дня', 'дней' }, { 'month', 'месяц', 'месяца', 'месяцев' }, { 'year', 'год', 'года', 'лет' } } local formats = { ['t'] = function() return os.date('%H:%M', time) end, ['T'] = function() return os.date('%H:%M:%S', time) end, ['d'] = function() return os.date('%x', time) end, ['D'] = function() local table_time = os.date('*t', time) return ('%02d %s %d г.'):format(table_time.day, month[table_time.month], table_time.year) end, ['f*'] = function() return ('%s, %s'):format(formatUnixTime(time, 'D'), formatUnixTime(time, 't')) end, ['F'] = function() return ('%s, %s'):format(os.date('%A', time), formatUnixTime(time, 'f*')) end, ['R'] = function() local diff = os.difftime(os.time(), time) local diff_str = '' local table_diff = os.date('*t', math.abs(diff)) local table_default = os.date('*t', 0) for i = #forms, 1, -1 do local form = forms[i] local value = table_diff[form[1]] - table_default[form[1]] if value > 0 then local form_str = nForm(value, form[2], form[3], form[4]) diff_str = ('%d %s'):format(value, form_str) break end end if diff < 0 then return ('через %s'):format(diff_str) elseif diff > 0 then return ('%s назад'):format(diff_str) end return 'сейчас' end } return formats[format] and formats[format]() or nil end

Использование:
Lua:
print(formatUnixTime(os.time()+120, "t"))
print(formatUnixTime(os.time()+120, "T"))
print(formatUnixTime(os.time()+120, "d"))
print(formatUnixTime(os.time()+120, "D"))
print(formatUnixTime(os.time()+120, "f*"))
print(formatUnixTime(os.time()+120, "F"))
print(formatUnixTime(os.time()+120, "R"))

--[[
output:
15:46
15:46:07
20.07.2024
20 июля 2024 г.
20 июля 2024 г., 15:46
суббота, 20 июля 2024 г., 15:46
через 2 минуты
]]
 
Последнее редактирование:

imring

Ride the Lightning
Всефорумный модератор
2,365
2,552
local lastnum = tonumber(tostring(t):sub(#tostring(t), #tostring(t)))
local maxdayinmonth = {31,datetime.year%4==0 and 29 or 28,31,30,31,30,31,31,30,31,30,31}
s = (t < 11 or t > 20) and (lastnum == 1 and 'месяц' or ((lastnum == 2 or lastnum == 3 or lastnum == 4) and 'месяца' or 'месяцев')) or 'месяцев'
???

Lua:
-- https://www.blast.hk/threads/13380/post-173238
local function nForm(num, v1, v2, v3)
    if num % 10 == 1 then return v1
    elseif num % 10 >= 2 and num % 10 <= 4 then return v2
    else return v3 end
end

os.setlocale('ru', 'time')
local function formatUnixTime(time, format)
    -- or use %B, but needs string.lower & change the ending
    local month = {'января','февраля','марта','апреля','мая','июня','июля','августа','сентября','октября','ноября','декабря'}
    local forms = {
        { 'sec', 'секунду', 'секунды', 'секунд' },
        { 'min', 'минуту', 'минуты', 'минут' },
        { 'hour', 'час', 'часа', 'часов' },
        { 'day', 'день', 'дня', 'дней' },
        { 'month', 'месяц', 'месяца', 'месяцев' },
        { 'year', 'год', 'года', 'лет' }
    }

    local formats = {
        ['t'] = function()
            return os.date('%H:%M', time)
        end,
        ['T'] = function()
            return os.date('%H:%M:%S', time)
        end,
        ['d'] = function()
            return os.date('%x', time)
        end,
        ['D'] = function()
            local table_time = os.date('*t', time)
            return ('%02d %s %d г.'):format(table_time.day, month[table_time.month], table_time.year)
        end,
        ['f*'] = function()
            return ('%s, %s'):format(formatUnixTime(time, 'D'), formatUnixTime(time, 't'))
        end,
        ['F'] = function()
            return ('%s, %s'):format(os.date('%A', time), formatUnixTime(time, 'f*'))
        end,
        ['R'] = function()
            local diff = os.difftime(os.time(), time)

            local diff_str = ''
            local table_diff = os.date('*t', math.abs(diff))
            local table_default = os.date('*t', 0)
            for i = #forms, 1, -1 do
                local form = forms[i]
                local value = table_diff[form[1]] - table_default[form[1]]
                if value > 0 then
                    local form_str = nForm(value, form[2], form[3], form[4])
                    diff_str = ('%d %s'):format(value, form_str)
                    break
                end
            end

            if diff < 0 then
                return ('через %s'):format(diff_str)
            elseif diff > 0 then
                return ('%s назад'):format(diff_str)
            end
            return 'сейчас'
        end
    }
    return formats[format] and formats[format]() or nil
end

function main()
    local time = os.time() + 1

    print(formatUnixTime(time, 't'))
    print(formatUnixTime(time, 'T'))
    print(formatUnixTime(time, 'd'))
    print(formatUnixTime(time, 'D'))
    print(formatUnixTime(time, 'f*'))
    print(formatUnixTime(time, 'F'))
    print(formatUnixTime(time, 'R'))
end