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

chromiusj

Известный
Модератор
6,068
4,414
Описание:
Рендер линии в 3D пространстве без артефактов, когда одна из точек линии уходит за камеру.

Принцип работы:
1) Обе точки видны на экране: линия рисуется напрямую через renderDrawLine
2) Ключевой случай: одна точка впереди, другая сзади. Нарисовать такую линию нельзя: проекция точки за камерой даёт «вывернутые» координаты и линию будет косоёбить уводить в разные стороны. Поэтому функция используя бинарный поиск (12 итераций, точность ~1/4096) ищет вдоль отрезка ту точку, которая лежит прямо на границе видимости (z > 0) и использует её вместо настоящей
3) Обе точки за экраном: ничего не рисуем, тут всё логично

Код:
Lua:
local function clipToNearPlane(fx, fy, fz, bx, by, bz)
    local sx, sy
    local left, right = 0.0, 1.0
    for _ = 1, 12 do
        local mid = (left + right) * 0.5
        local _, cx, cy, cz = convert3DCoordsToScreenEx(
            fx + (bx - fx) * mid,
            fy + (by - fy) * mid,
            fz + (bz - fz) * mid
        )
        if cz > 0 then
            left, sx, sy = mid, cx, cy
        else
            right = mid
        end
    end
    return sx, sy
end

function renderDrawLine3D(x1, y1, z1, x2, y2, z2, thickness, color)
    local _, sx1, sy1, sz1 = convert3DCoordsToScreenEx(x1, y1, z1)
    local _, sx2, sy2, sz2 = convert3DCoordsToScreenEx(x2, y2, z2)
    local v1, v2 = sz1 > 0, sz2 > 0

    if not v1 and not v2 then
        return false
    end

    if not v2 then
        sx2, sy2 = clipToNearPlane(x1, y1, z1, x2, y2, z2)
    elseif not v1 then
        sx1, sy1 = clipToNearPlane(x2, y2, z2, x1, y1, z1)
    end

    if not sx1 or not sx2 then
        return false
    end

    renderDrawLine(sx1, sy1, sx2, sy2, thickness, color)
    return true, sx1, sy1, sx2, sy2
end

Пример:
Lua:
function onD3DPresent()
    local x1, y1, z1 = 1806.59, -2547.78, 14
    local x2, y2, z2 = 1806.59, -2567.78, 14

    -- Улучшенный рендер
    renderDrawLine3D(x1, y1, z1, x2, y2, z2, 5, 0xFF00FF00)

    x1 = x1 + 1
    x2 = x2 + 1

    -- Обычный рендер
    local _, sx1, sy1, sz1 = convert3DCoordsToScreenEx(x1, y1, z1)
    local _, sx2, sy2, sz2 = convert3DCoordsToScreenEx(x2, y2, z2)
    if sz1 > 0 and sz2 > 0 then
        renderDrawLine(sx1, sy1, sx2, sy2, 5, 0xFFFF0000)
    end
end

Демонстрация:
Красная линия - обычный рендер
Зелёная линия - улучшенный рендер

Можно заметить, как зелёная линия стабильно отрисовывается под разными углами камеры, в то время как красная нет

Lua:
local NEAR_PLANE_EPSILON = 1e-3
local function clipToNearPlane(fx, fy, fz, wf, bx, by, bz, wb)
    local dw = wf - wb
    if dw == 0 then return nil, nil end
    local t = math.max(0.0, math.min(1.0, (wf - NEAR_PLANE_EPSILON) / dw))
    local _, sx, sy = convert3DCoordsToScreenEx(
        fx + (bx - fx) * t,
        fy + (by - fy) * t,
        fz + (bz - fz) * t
    )
    return sx, sy
end

local function renderDrawLine3D(x1, y1, z1, x2, y2, z2, thickness, color)
    local _, sx1, sy1, sz1 = convert3DCoordsToScreenEx(x1, y1, z1)
    local _, sx2, sy2, sz2 = convert3DCoordsToScreenEx(x2, y2, z2)

    if sz1 <= 0 and sz2 <= 0 then return false end

    if sz2 <= 0 then
        sx2, sy2 = clipToNearPlane(x1, y1, z1, sz1, x2, y2, z2, sz2)
    elseif sz1 <= 0 then
        sx1, sy1 = clipToNearPlane(x2, y2, z2, sz2, x1, y1, z1, sz1)
    end

    if not sx1 or not sx2 then return false end

    renderDrawLine(sx1, sy1, sx2, sy2, thickness, color)
    return true, sx1, sy1, sx2, sy2
end
 

kyrtion

Проверенный
1,463
589
Lua:
local NEAR_PLANE_EPSILON = 1e-3
local function clipToNearPlane(fx, fy, fz, wf, bx, by, bz, wb)
    local dw = wf - wb
    if dw == 0 then return nil, nil end
    local t = math.max(0.0, math.min(1.0, (wf - NEAR_PLANE_EPSILON) / dw))
    local _, sx, sy = convert3DCoordsToScreenEx(
        fx + (bx - fx) * t,
        fy + (by - fy) * t,
        fz + (bz - fz) * t
    )
    return sx, sy
end

local function renderDrawLine3D(x1, y1, z1, x2, y2, z2, thickness, color)
    local _, sx1, sy1, sz1 = convert3DCoordsToScreenEx(x1, y1, z1)
    local _, sx2, sy2, sz2 = convert3DCoordsToScreenEx(x2, y2, z2)

    if sz1 <= 0 and sz2 <= 0 then return false end

    if sz2 <= 0 then
        sx2, sy2 = clipToNearPlane(x1, y1, z1, sz1, x2, y2, z2, sz2)
    elseif sz1 <= 0 then
        sx1, sy1 = clipToNearPlane(x2, y2, z2, sz2, x1, y1, z1, sz1)
    end

    if not sx1 or not sx2 then return false end

    renderDrawLine(sx1, sy1, sx2, sy2, thickness, color)
    return true, sx1, sy1, sx2, sy2
end
Предлагаю кэшировать функции ради +производительность
(или я переборщил)

Lua:
local math_min, math_max = math.min, math.max
local convert3DCoordsToScreenEx = convert3DCoordsToScreenEx
local renderDrawLine = renderDrawLine
local NEAR_PLANE_EPSILON = 1e-3

local function clipToNearPlane(fx, fy, fz, wf, bx, by, bz, wb)
    local dw = wf - wb
    if dw == 0 then return nil, nil end
    local t = math_max(0.0, math_min(1.0, (wf - NEAR_PLANE_EPSILON) / dw))
    local _, sx, sy = convert3DCoordsToScreenEx(
        fx + (bx - fx) * t,
        fy + (by - fy) * t,
        fz + (bz - fz) * t
    )
    return sx, sy
end

local function renderDrawLine3D(x1, y1, z1, x2, y2, z2, thickness, color)
    local _, sx1, sy1, sz1 = convert3DCoordsToScreenEx(x1, y1, z1)
    local _, sx2, sy2, sz2 = convert3DCoordsToScreenEx(x2, y2, z2)

    if sz1 <= 0 and sz2 <= 0 then return false end

    if sz2 <= 0 then
        sx2, sy2 = clipToNearPlane(x1, y1, z1, sz1, x2, y2, z2, sz2)
    elseif sz1 <= 0 then
        sx1, sy1 = clipToNearPlane(x2, y2, z2, sz2, x1, y1, z1, sz1)
    end

    if not sx1 or not sx2 then return false end

    renderDrawLine(sx1, sy1, sx2, sy2, thickness, color)
    return true, sx1, sy1, sx2, sy2
end
 

vegas

Известный
Проверенный
564
540
Описание: Позволяет создавать несколько одинаковых хуков для samp/arizona events
Lua:
local handler = (function()
    local this = {
        list = {}
    }
    function this:update_require(lib, hook)
        require(lib)[hook] = function(...)
            local arguments = {...}
            local index = 0
            for _, execute in pairs(self.list[lib][hook].handlers) do
                index = index + 1
                local return_arguments = execute(table.unpack(arguments))
                if return_arguments == false then
                    return false
                end
                if return_arguments ~= nil and return_arguments ~= true then
                    arguments = return_arguments
                end
                if index == self.list[lib][hook].count then
                    return arguments
                end
            end
        end
    end
    function this:new(lib, hook, execute)
        if not self.list[lib] then
            self.list[lib] = {}
        end
    
        if not self.list[lib][hook] then
            self.list[lib][hook] = {handlers = {}, last_id = 0, count = 0}
        end
        self.list[lib][hook].count = self.list[lib][hook].count + 1
        self.list[lib][hook].last_id = self.list[lib][hook].last_id + 1
        self.list[lib][hook].handlers[self.list[lib][hook].last_id] = execute
        self:update_require(lib, hook)
        return (function()
            local this_ = {
                index = self.list[lib][hook].last_id,
            }
            function this_:remove()
                this.list[lib][hook].count = this.list[lib][hook].count - 1
                this.list[lib][hook].handlers[self.index] = nil
                this:update_require(lib, hook)
            end
            return this_
        end)()
    end
    
    return this
end)()
Пример: Первые 2 хука увидят событие и изменят значение, 3 хук увидит, ничего не изменив но удалит 4 хук который не должен был отправить клиенту событие,, 5 хук создается внутри 3 и возвращает значение с его изменением
Lua:
handler1 = handler:new('samp.events', 'onGivePlayerMoney', function(money)
    print('handler1')
    return {money + 1}
end)
handler2 = handler:new('samp.events', 'onGivePlayerMoney', function(money)
    print('handler2')
    return {money + 1}
end)
handler3 = handler:new('samp.events', 'onGivePlayerMoney', function(money)
    print('handler3')
    handler4:remove()
    handler5 = handler:new('samp.events', 'onGivePlayerMoney', function(money)
        print('handler5')
        return {money + 200}
    end)
end)
handler4 = handler:new('samp.events', 'onGivePlayerMoney', function(money)
    print('handler4')
    return false
end)
 
Последнее редактирование:

pathtohell

Участник
10
67
Описание: Обёртка над imgui.Begin, которая плавно меняет прозрачность окна в зависимости от расстояния курсора. При наведении — полная непрозрачность, при удалении — плавное затухание по smoothstep-кривой. Все параметры опциональны.

demo.gif


Код:
Lua:
local states = {}

--- @param name string
--- @param open userdata|nil imgui.new.bool pointer, nil = no close button
--- @param flags number|nil
--- @param minAlpha number|nil default 0.25
--- @param maxAlpha number|nil default 1.0
--- @param fadeDist number|nil default 200.0
--- @param fadeIn number|nil default 14.0
--- @param fadeOut number|nil default 6.0
--- @return boolean visible, boolean show
function imgui.BeginAutoOpacity(name, open, flags, minAlpha, maxAlpha, fadeDist, fadeIn, fadeOut)
    minAlpha = minAlpha or 0.25
    maxAlpha = maxAlpha or 1.0
    fadeDist = fadeDist or 200.0
    fadeIn = fadeIn or 14.0
    fadeOut = fadeOut or 6.0

    local s = states[name] or { alpha = maxAlpha }
    states[name] = s

    local style = imgui.GetStyle()
    local prevAlpha = style.Alpha
    style.Alpha = s.alpha

    local visible, show = imgui.Begin(name, open, flags)

    if not visible then
        style.Alpha = prevAlpha
        imgui.End()
        if open and not open[0] then states[name] = nil end
        return false, show
    end

    local io = imgui.GetIO()
    local wp = imgui.GetWindowPos()
    local ws = imgui.GetWindowSize()
    local mx = io.MousePos.x
    local my = io.MousePos.y
    local dx = math.max(wp.x - mx, 0, mx - (wp.x + ws.x))
    local dy = math.max(wp.y - my, 0, my - (wp.y + ws.y))
    local dist = math.sqrt(dx * dx + dy * dy)

    local target = maxAlpha
    if not imgui.IsWindowHovered() then
        local t = math.min(dist / fadeDist, 1.0)
        target = maxAlpha - (maxAlpha - minAlpha) * (t * t * (3 - 2 * t))
    end

    local speed = (target > s.alpha) and fadeIn or fadeOut
    local delta = math.min(io.DeltaTime * speed, 1.0)
    s.alpha = math.max(minAlpha, math.min(maxAlpha, s.alpha + (target - s.alpha) * delta))

    return true, show
end

function imgui.EndAutoOpacity()
    imgui.GetStyle().Alpha = 1.0
    imgui.End()
end

Пример использования:
Lua:
local mainWindowOpen = imgui.new.bool(true)

imgui.OnFrame(function() return mainWindowOpen[0] end, function()
    if imgui.BeginAutoOpacity("Main Window", mainWindowOpen) then
        imgui.Text("Hello!")
    end
    imgui.EndAutoOpacity()
end)
 

Niourozi

Участник
20
27
Description: Detects the top speed of the current vehicle automatically by sampling velocity over time. Resets automatically when switching vehicles. Usage: local top = getVehicleTopSpeed(veh) — call every tick. Returns top speed in km/h or nil while calibrating.

Code:
code:
local memory = require 'memory'

local _s = { samples={}, sampleClock=0, detectedTop=nil, lastVeh=nil }

local function getVehicleTopSpeed(veh)
    if veh ~= _s.lastVeh then
        _s.samples     = {}
        _s.sampleClock = 0
        _s.detectedTop = nil
        _s.lastVeh     = veh
    end
    local ptr = getCarPointer(veh)
    if not ptr or ptr == 0 then return nil end
    local now = os.clock()
    if now - _s.sampleClock < 0.05 then return _s.detectedTop end
    _s.sampleClock = now
    local vx = memory.getfloat(ptr + 0x44)
    local vy = memory.getfloat(ptr + 0x48)
    local vz = memory.getfloat(ptr + 0x4C)
    local speed = math.sqrt(vx*vx + vy*vy + vz*vz) * 180
    table.insert(_s.samples, speed)
    if #_s.samples > 8 then table.remove(_s.samples, 1) end
    if #_s.samples < 5 then return nil end
    local maxV, minV = 0, math.huge
    for _, v in ipairs(_s.samples) do
        if v > maxV then maxV = v end
        if v < minV then minV = v end
    end
    if (maxV - minV) <= 1 and maxV > 30 then
        _s.detectedTop = maxV
    end
    return _s.detectedTop
end

Example:
example:
local memory = require 'memory'

local _s = { samples={}, sampleClock=0, detectedTop=nil, lastVeh=nil }

local function getVehicleTopSpeed(veh)
    if veh ~= _s.lastVeh then
        _s.samples     = {}
        _s.sampleClock = 0
        _s.detectedTop = nil
        _s.lastVeh     = veh
    end
    local ptr = getCarPointer(veh)
    if not ptr or ptr == 0 then return nil end
    local now = os.clock()
    if now - _s.sampleClock < 0.05 then return _s.detectedTop end
    _s.sampleClock = now
    local vx = memory.getfloat(ptr + 0x44)
    local vy = memory.getfloat(ptr + 0x48)
    local vz = memory.getfloat(ptr + 0x4C)
    local speed = math.sqrt(vx*vx + vy*vy + vz*vz) * 180
    table.insert(_s.samples, speed)
    if #_s.samples > 8 then table.remove(_s.samples, 1) end
    if #_s.samples < 5 then return nil end
    local maxV, minV = 0, math.huge
    for _, v in ipairs(_s.samples) do
        if v > maxV then maxV = v end
        if v < minV then minV = v end
    end
    if (maxV - minV) <= 1 and maxV > 30 then
        _s.detectedTop = maxV
    end
    return _s.detectedTop
end

function main()
    repeat wait(500) until isSampAvailable()
    local lastReported = nil
    while true do
        wait(25)
        if isCharInAnyCar(PLAYER_PED) then
            local veh = storeCarCharIsInNoSave(PLAYER_PED)
            local top = getVehicleTopSpeed(veh)
            if top and top ~= lastReported then
                lastReported = top
                sampAddChatMessage('Top speed: ' .. math.floor(top) .. ' km/h', -1)
            end
        else
            lastReported = nil
        end
    end
end
 
  • Эм
Реакции: Cosmo и Corenale

Baklaxa

Участник
53
21
Получает проценты заряда батареи

LUA:
local ffi   = require "ffi"
local kernel = ffi.load "kernel32"

ffi.cdef [[
typedef struct {
    unsigned char ACLineStatus;
    unsigned char BatteryFlag;
    unsigned char BatteryLifePercent;
    unsigned char SystemStatusFlag;
    unsigned long  BatteryLifeTime;
    unsigned long  BatteryFullLifeTime;
} SYSTEM_POWER_STATUS;
int GetSystemPowerStatus(SYSTEM_POWER_STATUS* lpSystemPowerStatus);
]]


local function batareya()
    local s = ffi.new "SYSTEM_POWER_STATUS"
    if kernel.GetSystemPowerStatus(s) ~= 0 and s.BatteryLifePercent ~= 255 then
        return s.BatteryLifePercent
    end
    return nil
end

function main()
    while not isSampAvailable() do wait(100) end

    sampRegisterChatCommand("btr", function()
        local p = batareya()
        sampAddChatMessage(p and tostring(p) or "none", -1)
    end)

    while true do wait(0) end
end
 

kyrtion

Проверенный
1,463
589
Описание: Замораживает только движение, а камера не затронет.
Объяснение: Проблема том, что некоторые функции замораживает на всех режимов и мышки, камеры. Например, freezeCharPosition фризит на позиции, если вас столкнется или админ слапнет - забанят за AirBrake или подозрение на бот. setSampCursorMode как там, не всегда работает если в других версиях. Поэтому, я решил выпустить новый сниппет, который блокирует на действие игрока, то есть от нажатий или от актива, а самом игрок просто "стоит" и не поступит знаки чтобы двигаться, словно настоящий фриз с свободной камерой. Сниппет работает на разных SA:MP с условием чтобы стоял чистый GTA:SA 1.0 US. Нашел баг? Пиши в тг `@kyrtion`. В арз и другие самповские проекта с лаунчером не протестировано. Протестировано только на Advance Launcher
Пример использования:
Lua:
updateMovementLock(true/false) -- в цикл
Код:
Lua:
-- Author: DK22Pac
-- Source: plugin-sdk
-- Path: plugin-sdk/plugin_sa/game_sa/CPad.h
-- URL: https://github.com/DK22Pac/plugin-sdk

local ffi = require("ffi")

ffi.cdef [[
    typedef struct CControllerState {
        int16_t LeftStickX;
        int16_t LeftStickY;
        int16_t RightStickX;
        int16_t RightStickY;
        int16_t LeftShoulder1;
        int16_t LeftShoulder2;
        int16_t RightShoulder1;
        int16_t RightShoulder2;
        int16_t DPadUp;
        int16_t DPadDown;
        int16_t DPadLeft;
        int16_t DPadRight;
        int16_t Start;
        int16_t Select;
        int16_t ButtonSquare;
        int16_t ButtonTriangle;
        int16_t ButtonCross;
        int16_t ButtonCircle;
        int16_t ShockButtonL;
        int16_t ShockButtonR;
        int16_t m_bChatIndicated;
        int16_t m_bPedWalk;
        int16_t m_bVehicleMouseLook;
        int16_t m_bRadioTrackSkip;
    } CControllerState;

    typedef struct CMouseControllerState {
        uint8_t lmb;
        uint8_t rmb;
        uint8_t mmb;
        uint8_t wheelUp;
        uint8_t wheelDown;
        uint8_t bmx1;
        uint8_t bmx2;
        int8_t _pad7;
        float z;
        float x;
        float y;
    } CMouseControllerState;

    typedef struct CPad {
        CControllerState NewState;
        CControllerState OldState;
        int16_t SteeringLeftRightBuffer[10];
        int32_t DrunkDrivingBufferUsed;
        CControllerState PCTempKeyState;
        CControllerState PCTempJoyState;
        CControllerState PCTempMouseState;
        int8_t Phase;
        int8_t _pad109;
        int16_t Mode;
        int16_t ShakeDur;
        uint16_t DisablePlayerControls;
        uint8_t ShakeFreq;
        int8_t bHornHistory[5];
        int8_t iCurrHornHistory;
        int8_t JustOutOfFrontEnd;
        int8_t bApplyBrakes;
        int8_t bDisablePlayerEnterCar;
        int8_t bDisablePlayerDuck;
        int8_t bDisablePlayerFireWeapon;
        int8_t bDisablePlayerFireWeaponWithL1;
        int8_t bDisablePlayerCycleWeapon;
        int8_t bDisablePlayerJump;
        int8_t bDisablePlayerDisplayVitalStats;
        int32_t LastTimeTouched;
        int32_t AverageWeapon;
        int32_t AverageEntries;
        int32_t NoShakeBeforeThis;
        int8_t NoShakeFreq;
        int8_t _pad131[3];
    } CPad;
]]

assert(ffi.sizeof("CControllerState") == 0x30)
assert(ffi.sizeof("CMouseControllerState") == 0x14)
assert(ffi.sizeof("CPad") == 0x134)

local pads = ffi.cast("CPad* (__cdecl*)(int)", 0x53FB70)
local mouse = ffi.cast("CMouseControllerState*", 0xB73418)

local enabled, previous_cycle_weapon = false, 0

local function updateMovementLock(state)
    local result, pad = pcall(pads, 0)

    if not result or pad == nil then
        return
    end

    if state and not enabled then
        enabled = true
        previous_cycle_weapon = pad.bDisablePlayerCycleWeapon
    elseif not state and enabled then
        pad.bDisablePlayerCycleWeapon = previous_cycle_weapon
        enabled = false
        return
    elseif not state then
        return
    end

    pad.bDisablePlayerCycleWeapon = 1

    pad.NewState.LeftStickX = 0
    pad.NewState.LeftStickY = 0
    pad.NewState.DPadUp = 0
    pad.NewState.DPadDown = 0
    pad.NewState.DPadLeft = 0
    pad.NewState.DPadRight = 0
    pad.NewState.Select = 0
    pad.NewState.ButtonSquare = 0
    pad.NewState.ButtonTriangle = 0
    pad.NewState.ButtonCross = 0
    pad.NewState.ButtonCircle = 0
    pad.NewState.LeftShoulder1 = 0
    pad.NewState.LeftShoulder2 = 0
    pad.NewState.RightShoulder1 = 0
    pad.NewState.RightShoulder2 = 0
    pad.NewState.ShockButtonL = 0
    pad.NewState.ShockButtonR = 0
    pad.NewState.m_bPedWalk = 0
    pad.NewState.m_bRadioTrackSkip = 0

    mouse.lmb = 0
    mouse.rmb = 0
    mouse.mmb = 0
    mouse.wheelUp = 0
    mouse.wheelDown = 0
    mouse.bmx1 = 0
    mouse.bmx2 = 0
end

assert(ffi.sizeof("CControllerState") == 0x30)
assert(ffi.sizeof("CMouseControllerState") == 0x14)
assert(ffi.sizeof("CPad") == 0x134)

pads = ffi.cast("CPad* (__cdecl*)(int)", 0x53FB70)
mouse = ffi.cast("CMouseControllerState*", 0xB73418)

function onScriptTerminate(scr)
    if scr == script.this then
        updateMovementLock(false)
    end
end
 
Последнее редактирование:
  • Нравится
Реакции: Vespan

Joce

Известный
87
31
Описание: Прикрепление текста к транспорту через смещение (аналог кватерниона)
Пример использования:
code:
local sx, sy = getVehicle3DTextCoords(vehicle, -2.2, 0.0, 0.2)
if sx and sy then
    renderFontDrawText(font, "Hello World", sx, sy, 0xFFFFFFFF)
end
Код:
code:
local function getVehicle3DTextCoords(vehicle, offsetX, offsetY, offsetZ)
    -- координаты точки смещения (аналогично кватерниону)
    local wx, wy, wz = getOffsetFromCarInWorldCoords(vehicle, offsetX, offsetY, offsetZ)
    if wx == nil then
        return nil, nil
    end
    return convert3DCoordsToScreen(wx, wy, wz)
end
Пример реализации в скрипте:
code:
script_name("3D km/h Render")
script_author("joce")

local imgui = require("mimgui")
local cfg = { offset = { x = -2.2, y = 0.0, z = 0.2 }, fontScale = 1.8, colorValue = 0xFF00FF00, colorUnit = 0xFFFFFFFF }
local state = { speed = 0, angle = 0, active = false, pos = imgui.ImVec2(0, 0) }
local lastSpeedUpdate = 0

local function getVehicle()
    if isCharInAnyCar(PLAYER_PED) then
        local car = storeCarCharIsInNoSave(PLAYER_PED)
        if getCarCharIsUsing(PLAYER_PED) == car and getDriverOfCar(car) == PLAYER_PED then
            return car
        end
    end
end

local function updateData(vehicle)
    local now = os.clock() * 1000
    if now - lastSpeedUpdate >= 150 then
        state.speed = math.floor(getCarSpeed(vehicle) * 3.6)
        lastSpeedUpdate = now
    end
    local wx, wy, wz = getOffsetFromCarInWorldCoords(vehicle, cfg.offset.x, cfg.offset.y, cfg.offset.z)
    local sx, sy = convert3DCoordsToScreen(wx, wy, wz)
    if not sx or not sy then return false end
    state.pos.x, state.pos.y = sx, sy
    local cx, cy, cz = getOffsetFromCarInWorldCoords(vehicle, 0.0, 0.0, 0.0)
    local ux, uy, uz = getOffsetFromCarInWorldCoords(vehicle, 0.0, 0.0, 1.0)
    local vecUpX = ux - cx
    local vecUpZ = uz - cz
    state.angle = -math.atan2(vecUpX, vecUpZ)

    return true
end

local function drawRotatedText(dl, text, pos, angle, color, ox)
    local size = imgui.CalcTextSize(text)
    local start = dl.VtxBuffer.Size
    dl:AddText(imgui.ImVec2(pos.x + ox, pos.y - size.y * 0.5), color, text)
    if math.abs(angle) > 0.001 then
        local cosA, sinA = math.cos(angle), math.sin(angle)
        for i = start, dl.VtxBuffer.Size - 1 do
            local v = dl.VtxBuffer.Data[i]
            local lx, ly = v.pos.x - pos.x, v.pos.y - pos.y
            v.pos.x, v.pos.y = pos.x + lx * cosA - ly * sinA, pos.y + lx * sinA + ly * cosA
        end
    end
end

imgui.OnFrame(function() return state.active end, function(player)
    player.HideCursor = true
    imgui.SetNextWindowPos(imgui.ImVec2(0, 0))
    imgui.SetNextWindowSize(imgui.GetIO().DisplaySize)
    imgui.Begin("##speedo", nil, 0x8F)
    local dl = imgui.GetWindowDrawList()
    imgui.SetWindowFontScale(cfg.fontScale)
    local sp, un = tostring(state.speed), " km/h"
    local ws, wu = imgui.CalcTextSize(sp).x, imgui.CalcTextSize(un).x
    local halfTotal = (ws + wu) * 0.5
    drawRotatedText(dl, sp, state.pos, state.angle, cfg.colorValue, -halfTotal)
    drawRotatedText(dl, un, state.pos, state.angle, cfg.colorUnit, -halfTotal + ws)
    imgui.End()
end)

function main()
    while not isSampAvailable() do wait(100) end
    while true do
        wait(0)
        local car = getVehicle()
        state.active = car and updateData(car) or false
    end
end

Использование в скрипте - https://www.blast.hk/threads/256877/
 
Последнее редактирование:

tripple sixx

Активный
164
70
1786335398089.png


Описание: Меняем цвет аризоновского чата (_chat.asi)
Код:
Lua:
local ffi = require("ffi")

ffi.cdef[[
typedef void* HMODULE;
typedef unsigned long DWORD;
typedef int BOOL;
typedef unsigned int uintptr_t;
typedef unsigned int uint32_t;

HMODULE GetModuleHandleA(const char* lpModuleName);

BOOL VirtualProtect(
    void* lpAddress,
    unsigned long dwSize,
    unsigned long flNewProtect,
    unsigned long* lpflOldProtect
);
]]

local kernel32 = ffi.load("kernel32")

local PAGE_EXECUTE_READWRITE = 0x40


--------------------------------------------------
-- colors
--------------------------------------------------

local INPUT_COLOR           = 0xF2000000
local BUTTON_INACTIVE_COLOR = 0x90000000
local BUTTON_ACTIVE_COLOR   = 0xF2000000

local SCROLLBAR_NORMAL_COLOR = 0x000000
local SCROLLBAR_HOVER_COLOR  = 0x000000
local SCROLLBAR_ACTIVE_COLOR = 0x000000

--------------------------------------------------
-- rva
--------------------------------------------------

local RVA_INPUT_1 = 0x13718
local RVA_INPUT_2 = 0x13771

local RVA_BUTTON_INACTIVE = 0x142FE
local RVA_BUTTON_ACTIVE   = 0x1431B

local RVA_UI_CONTEXT = 0x175D88


local STYLE_COLORS_OFFSET = 5584
local STYLE_SCROLLBAR_NORMAL = 7
local STYLE_SCROLLBAR_HOVER  = 8
local STYLE_SCROLLBAR_ACTIVE = 9

local function makeWritable(address, size)
    local oldProtect = ffi.new("unsigned long[1]")

    if kernel32.VirtualProtect(
        ffi.cast("void*", address),
        size,
        PAGE_EXECUTE_READWRITE,
        oldProtect
    ) == 0 then
        return false
    end

    return true
end

local function writeDword(address, value)
    if not makeWritable(address, 4) then
        return false
    end

    ffi.cast("uint32_t*", address)[0] = value

    return true
end

local function rgbComponent(rgb, shift)
    return bit.band(
        bit.rshift(rgb, shift),
        0xFF
    )
end

local function setStyleRGB(context, index, rgb)
    local address =
        context
        + STYLE_COLORS_OFFSET
        + index * 16

    if not makeWritable(address, 16) then
        return false
    end

    local color = ffi.cast(
        "float*",
        address
    )

    local r = rgbComponent(rgb, 16)
    local g = rgbComponent(rgb, 8)
    local b = rgbComponent(rgb, 0)

    color[0] = r / 255.0
    color[1] = g / 255.0
    color[2] = b / 255.0
    return true
end

local function getUIContext(base)
    local ptr = ffi.cast(
        "uint32_t*",
        base + RVA_UI_CONTEXT
    )

    local context = tonumber(ptr[0])

    if not context or context == 0 then
        return nil
    end

    return context
end

local function patchStaticColors(base)
    writeDword(base + RVA_INPUT_1, INPUT_COLOR)
    writeDword(base + RVA_INPUT_2, INPUT_COLOR)
    writeDword(base + RVA_BUTTON_INACTIVE, BUTTON_INACTIVE_COLOR)
    writeDword(base + RVA_BUTTON_ACTIVE, BUTTON_ACTIVE_COLOR)
end

local function patchScrollbar(context)
    setStyleRGB(context, STYLE_SCROLLBAR_NORMAL, SCROLLBAR_NORMAL_COLOR)
    setStyleRGB(context, STYLE_SCROLLBAR_HOVER, SCROLLBAR_HOVER_COLOR)
    setStyleRGB(context, STYLE_SCROLLBAR_ACTIVE, SCROLLBAR_ACTIVE_COLOR)
end

function main()
    while not isSampAvailable() do
        wait(100)
    end

    local module

    repeat
        module = kernel32.GetModuleHandleA(
            "_chat.asi"
        )

        wait(100)
    until module ~= nil

    local base = tonumber(
        ffi.cast(
            "uintptr_t",
            module
        )
    )

    patchStaticColors(base)

    local context

    repeat
        context = getUIContext(base)

        if not context then
            wait(100)
        end
    until context
    patchScrollbar(context)
    while true do
        patchScrollbar(context)
        wait(0)
    end
end
 

Tectrex

Известный
167
211
Описание: Прикрепление текста к транспорту через матрицу поворота(аналог кватернион)
Пример использования:
code:
local sx, sy = getVehicle3DTextCoords(vehicle, -2.2, 0.0, 0.2)
if sx and sy then
    renderFontDrawText(font, "Hello World", sx, sy, 0xFFFFFFFF)
end
Код:
code:
local memory = require("memory")

local function getVehicle3DTextCoords(vehicle, offsetX, offsetY, offsetZ)
    local ptr = getCarPointer(vehicle)
    if ptr == 0 then return nil, nil end
    local mPtr = memory.getuint32(ptr + 0x14, false)
    if mPtr == 0 then return nil, nil end
    local rx, ry, rz = memory.getfloat(mPtr + 0x0, false), memory.getfloat(mPtr + 0x4, false), memory.getfloat(mPtr + 0x8, false)
    local fx, fy, fz = memory.getfloat(mPtr + 0x10, false), memory.getfloat(mPtr + 0x14, false), memory.getfloat(mPtr + 0x18, false)
    local ux, uy, uz = memory.getfloat(mPtr + 0x20, false), memory.getfloat(mPtr + 0x24, false), memory.getfloat(mPtr + 0x28, false)
    local px, py, pz = getCarCoordinates(vehicle)
    local worldX = px + (rx * offsetX) + (fx * offsetY) + (ux * offsetZ)
    local worldY = py + (ry * offsetX) + (fy * offsetY) + (uy * offsetZ)
    local worldZ = pz + (rz * offsetX) + (fz * offsetY) + (uz * offsetZ)
    return convert3DCoordsToScreen(worldX, worldY, worldZ)
end

Использование в скрипте - https://www.blast.hk/threads/256877/

Зачем читать мемори вручную через memory.getuint32 и делать 9 вызовов memory.getfloat, если в муне уже есть нативка getOffsetFromCarInWorldCoords? Она делает то же самое что и ты накалякал.
 
  • Нравится
Реакции: Willy4ka, Joce и chapo