Вопросы по Lua скриптингу

Общая тема для вопросов по разработке скриптов на языке программирования Lua, в частности под MoonLoader.
  • Задавая вопрос, убедитесь, что его нет в списке частых вопросов и что на него ещё не отвечали (воспользуйтесь поиском).
  • Поищите ответ в теме посвященной разработке Lua скриптов в MoonLoader
  • Отвечая, убедитесь, что ваш ответ корректен.
  • Старайтесь как можно точнее выразить мысль, а если проблема связана с кодом, то обязательно прикрепите его к сообщению, используя блок [code=lua]здесь мог бы быть ваш код[/code].
  • Если вопрос связан с MoonLoader-ом первым делом желательно поискать решение на wiki.

Частые вопросы

Как научиться писать скрипты? С чего начать?
Информация - Гайд - Всё о Lua скриптинге для MoonLoader(https://blast.hk/threads/22707/)
Как вывести текст на русском? Вместо русского текста у меня какие-то каракули.
Изменить кодировку файла скрипта на Windows-1251. В Atom: комбинация клавиш Ctrl+Shift+U, в Notepad++: меню Кодировки -> Кодировки -> Кириллица -> Windows-1251.
Как получить транспорт, в котором сидит игрок?
Lua:
local veh = storeCarCharIsInNoSave(PLAYER_PED)
Как получить свой id или id другого игрока?
Lua:
local _, id = sampGetPlayerIdByCharHandle(PLAYER_PED) -- получить свой ид
local _, id = sampGetPlayerIdByCharHandle(ped) -- получить ид другого игрока. ped - это хендл персонажа
Как проверить, что строка содержит какой-то текст?
Lua:
if string.find(str, 'текст', 1, true) then
-- строка str содержит "текст"
end
Как эмулировать нажатие игровой клавиши?
Lua:
local game_keys = require 'game.keys' -- где-нибудь в начале скрипта вне функции main

setGameKeyState(game_keys.player.FIREWEAPON, -1) -- будет сэмулировано нажатие клавиши атаки
Все иды клавиш находятся в файле moonloader/lib/game/keys.lua.
Подробнее о функции setGameKeyState здесь: lua - setgamekeystate | BlastHack — DEV_WIKI(https://www.blast.hk/wiki/lua:setgamekeystate)
Как получить id другого игрока, в которого целюсь я?
Lua:
local valid, ped = getCharPlayerIsTargeting(PLAYER_HANDLE) -- получить хендл персонажа, в которого целится игрок
if valid and doesCharExist(ped) then -- если цель есть и персонаж существует
  local result, id = sampGetPlayerIdByCharHandle(ped) -- получить samp-ид игрока по хендлу персонажа
  if result then -- проверить, прошло ли получение ида успешно
    -- здесь любые действия с полученным идом игрока
  end
end
Как зарегистрировать команду чата SAMP?
Lua:
-- До бесконечного цикла/задержки
sampRegisterChatCommand("mycommand", function (param)
     -- param будет содержать весь текст введенный после команды, чтобы разделить его на аргументы используйте string.match()
    sampAddChatMessage("MyCMD", -1)
end)
Крашит игру при вызове sampSendChat. Как это исправить?
Это происходит из-за бага в SAMPFUNCS, когда производится попытка отправки пакета определенными функциями изнутри события исходящих RPC и пакетов. Исправления для этого бага нет, но есть способ не провоцировать его. Вызов sampSendChat изнутри обработчика исходящих RPC/пакетов нужно обернуть в скриптовый поток с нулевой задержкой:
Lua:
function onSendRpc(id)
  -- крашит:
  -- sampSendChat('Send RPC: ' .. id)

  -- норм:
  lua_thread.create(function()
    wait(0)
    sampSendChat('Send RPC: ' .. id)
  end)
end
 
Последнее редактирование:

tyukapa

Активный
299
65
Ищешь метку:
Бежишь туда: https://www.blast.hk/threads/37428/#post-366765
Я бы не парился с меткой, но мне не подходит этот вариант (https://www.blast.hk/threads/37428/#post-366765)

че ты высрал, долбаеб
https://wiki.blast.hk/ru/moonloader/lua/changeBlipColour
 
Последнее редактирование:
  • Эм
Реакции: YarikVL

YarikVL

Известный
Проверенный
4,796
1,813
Я бы не парился с меткой, но мне не подходит этот вариант (https://www.blast.hk/threads/37428/#post-366765)
Чаво? Чем тебе не подходит бег на координаты?
Ты даже не указал какой у тебя чекпоинт, если гоночный то вот я делал тему ( хоть я и спрашивал про поднятие чекпоинта скриптом, но мне скинули туда получение координат )

Получишь координаты чекпоинта - будешь вызывать функцию бега и передавать в неё координаты метки.
 

Dmitriy Makarov

25.05.2021
Проверенный
2,481
1,113
Подскажите, как делается биндер? Мне нужно, что-бы текст который написали в InputTextMultiline отправлялся в чат по кмд/кнопке которую указали, и с задержкой так же.
Так же, как и обычный биндер.
Lua:
-- while true do
if isKeyJustPressed(_G['VK_'..bindersettings.hotkeys.binderhotkey1]) then
    for line in string.gmatch(bindersettings.text.bindertext1.v, "[^\r\n]+") do
        sampAddChatMessage(u8:decode(line), -1)
        wait(tonumber(bindersettings.delay.binderdelay.v))
    end
end
Для команд то же самое.
Lua:
local sampev = require "lib.samp.events"

function sampev.onSendCommand(command)
    if command == bindersettings.commands.bindercmd1.v then
        for line in string.gmatch(bindersettings.text.bindertext1.v, "[^\r\n]+") do
            lua_thread.create(function()
                sampAddChatMessage(u8:decode(line), -1)
                wait(tonumber(bindersettings.delay.binderdelay.v))
            end)
        end
        return false
    end
end
Но я бы посоветовал не делать эти binder1, binder2, а сделать через цикл for.
Можешь реализацию взять из этого скрипта, тут это есть.
 
  • Нравится
Реакции: хуега)

Julimba

Участник
108
10
qq, пытаюсь сделать автоматическую создания кфг, если его нету, при запуске сразу крашит, в чем моя ошибка?
Lua:
local directIni = getWorkingDirectory() .. '\\MHelper.ini' -- Вверху, рядом с подгрузкой всех библиотек и т.п
local status = inicfg.load(mainIni, 'MHelper.ini')

local mainIni = inicfg.load({
    config = {
    target = 'false',
    GOS = '1500',
    distans = 'false',
    OPG = '2000',
    GHETTO = '2500',
    PLAYERS = '500',
    },
}, directIni) -- где то чуть ниже

if not doesFileExist('moonloader/MHelper.ini') then inicfg.save(mainIni, 'MHelper.ini') end -- в мейне
Крашит игру сразу при запуске игры
 

Dmitriy Makarov

25.05.2021
Проверенный
2,481
1,113
qq, пытаюсь сделать автоматическую создания кфг, если его нету, при запуске сразу крашит, в чем моя ошибка?
Lua:
local directIni = getWorkingDirectory() .. '\\MHelper.ini' -- Вверху, рядом с подгрузкой всех библиотек и т.п
local status = inicfg.load(mainIni, 'MHelper.ini')

local mainIni = inicfg.load({
    config = {
    target = 'false',
    GOS = '1500',
    distans = 'false',
    OPG = '2000',
    GHETTO = '2500',
    PLAYERS = '500',
    },
}, directIni) -- где то чуть ниже

if not doesFileExist('moonloader/MHelper.ini') then inicfg.save(mainIni, 'MHelper.ini') end -- в мейне
Крашит игру сразу при запуске игры
Наверное потому, что ты во 2-й строке пытаешься получить mainIni с 4-й строки, который ещё не успел появиться.)
Попробуй убери вторую строку, который с
local status начинается.
 
  • Нравится
Реакции: хуега)

Dmitriy Makarov

25.05.2021
Проверенный
2,481
1,113
ничего не поменялось
Уверен, что крашит именно из-за этого?
Замени весь код, что ты скинул - на этот и посмотри. Если ничего не изменится, то, скорее всего, проблема в другом. И посмотри что в логе.
Lua:
local mainIni = inicfg.load({
    config = {
        target = 'false',
        GOS = '1500',
        distans = 'false',
        OPG = '2000',
        GHETTO = '2500',
        PLAYERS = '500',
    }
}, "moonloader/MHelper.ini")

if not doesFileExist('moonloader/MHelper.ini') then
    inicfg.save(mainIni, 'MHelper.ini')
end
 

Макс | Lycorn

Участник
156
13
Так же, как и обычный биндер.
Lua:
-- while true do
if isKeyJustPressed(_G['VK_'..bindersettings.hotkeys.binderhotkey1]) then
    for line in string.gmatch(bindersettings.text.bindertext1.v, "[^\r\n]+") do
        sampAddChatMessage(u8:decode(line), -1)
        wait(tonumber(bindersettings.delay.binderdelay.v))
    end
end
Для команд то же самое.
Lua:
local sampev = require "lib.samp.events"

function sampev.onSendCommand(command)
    if command == bindersettings.commands.bindercmd1.v then
        for line in string.gmatch(bindersettings.text.bindertext1.v, "[^\r\n]+") do
            lua_thread.create(function()
                sampAddChatMessage(u8:decode(line), -1)
                wait(tonumber(bindersettings.delay.binderdelay.v))
            end)
        end
        return false
    end
end
Но я бы посоветовал не делать эти binder1, binder2, а сделать через цикл for.
Можешь реализацию взять из этого скрипта, тут это есть.
Попробовал, не хочет, может нужно делать сохранение в тот же... ini файл? а потом может и будет работать
 
Последнее редактирование:

Andrinall

Известный
681
532
Нужна помощь с отображением маркеров внутри rect радара.
Отметил на скрине где должна быть метка на радаре, никак не могу додуматься как её туда пихнуть.

( getPointInRect из mim radar не предлагать, я хоть и взял оттуда transform,
но у меня немного по другому всё устроено и по итогу getPointInRect не даёт правильных координат мне)
(Пробовал и разницей uv) и (переводить uv в проценты, находить углы картинки и от них отталкиваться), (пробовал getPointRect из mim radar)
и много другой фигни на магических числах )

(все выше описанные способы давали или точку за пределами радара или вовсе краш)

1685603876589.png
Lua:
local ffi      = require 'ffi'
local encoding = require 'encoding'
local imgui    = assert(select(2, pcall(require, 'mimgui')), "Lib mimgui not loaded")
local imgsize  = assert(select(2, pcall(require, 'imagesize')), "Lib imagesize not loaded")
require 'moonloader'

local u8 = encoding.UTF8
encoding.default = "CP1251"

local new = imgui.new
local sw, sh, map_image, map_size, size, radius, scale, c_scale, default_font, global2D_UV
local path = (getWorkingDirectory() .. "\\resource\\HUDMAP.png") -- ( 1024x1024 карта(map) из gta_sa_dir\models\fronten2.txd )

local draw_radar                     = new.bool(false)
local inner_radar_icons              = new.bool(false)
local show_all_world_sides           = new.bool(true)
local russian_world_sides            = new.bool(false)
local use_custom_scale               = new.bool(false)
local use_custom_border_color        = new.bool(false)
local use_transparent_background     = new.bool(true)
local rotate_map_with_camera_heading = new.bool(true)
local show_other_players             = new.bool(true)
local show_npc_on_radar              = new.bool(false)

local player_marker_size  = new.float(5.0)
local custom_size         = new.int(800)
local custom_border_color = new.float[4]( 1.0, 0.8, 0.92, 0.7 )

imgui.OnInitialize(function()
    imgui.GetIO().IniFilename = nil
    sw, sh = getScreenResolution()

    map_image = assert(imgui.CreateTextureFromFile(path), "Map Image Not Loaded")
    
    local file = assert(io.open(path, "rb"), "Map Image File Not Opened For Get Image Size")
    local data = file:read("*a")
    file:close()
    local w, h, f = imgsize.imgsize_string(data)
    assert(w, "Error Getting Image Size : " .. f)

    size, radius = 700, 175

    imgui.GetStyle().Colors[imgui.Col.WindowBg] = imgui.ImVec4(111 / 255, 137 / 255, 169 / 255, 1.0)
    imgui.GetStyle().WindowPadding = imgui.ImVec2(0.0,0.0)
    imgui.GetStyle().WindowRounding = 8.0

    map_size = imgui.ImVec2(w, h)
    scale    = imgui.ImVec2(map_size.x / 1024, map_size.y / 1024)
    c_scale  = imgui.ImVec2(2, 1)

    default_font = {
        ptr   = imgui.GetIO().Fonts.Fonts.Data[0],
        width = imgui.GetIO().Fonts.ConfigData.Data[0].SizePixels
    }
end)

local radar = imgui.OnFrame(function() return draw_radar[0] end, function(self) -- draw
    if isPauseMenuActive() then self.HideCursor = true; return true end

    imgui.SetNextWindowPos(imgui.ImVec2(sw / 9, sh - sh / 1.8), imgui.Cond.FirstUseEver)
    imgui.SetNextWindowSize(imgui.ImVec2(radius + 100, radius), imgui.Cond.FirstUseEver)
    if imgui.Begin("##t", draw_radar, imgui.WindowFlags.NoTitleBar + (use_transparent_background[0] and imgui.WindowFlags.NoBackground or 0) + imgui.WindowFlags.NoScrollbar + imgui.WindowFlags.NoScrollWithMouse + imgui.WindowFlags.NoResize) then
        local pos   = imgui.GetWindowPos()
        local wsize = imgui.GetWindowSize()
        local wnd = {
            pos = pos, size = wsize, -- window pos and size
            centre  = imgui.ImVec2(pos.x + wsize.x / 2, pos.y + wsize.y / 2), -- window center
            corners = { pos, imgui.ImVec2(pos.x + (radius + 100), pos.y + radius) }, -- window corners
        }

        local x, y, _ = getActiveCameraCoordinates() -- getCharCoordinates(PLAYER_PED) doesn't fit
        local ax, ay, _ = getActiveCameraPointAt()
        local angle = getHeadingFromVector2d(ax - x, ay - y) -- for rotate map
        local wdl   = imgui.GetWindowDrawList() -- for map image and markers
        local fdl   = imgui.GetForegroundDrawList() -- for world sides and temporary for players in stream zone

        setupRadarScale() -- scale radar depending on speed

        global2D_UV = {
            left_c  = transform3DtoGlobal2D(imgui.ImVec2(x - size, y + size), getScale()), -- UV left up corner of image inside radar (wnd.corners[1])
            right_c = transform3DtoGlobal2D(imgui.ImVec2(x + size, y - size), getScale()) -- UV right down corner of image inside radar (wnd.corners[2])
        }
        
        local start = wdl.VtxBuffer.Size -- 0
        drawRadarMap(wdl, map_image, global2D_UV.left_c, global2D_UV.right_c, wnd)
        if show_other_players[0] then
            --drawPlayersInStream(fdl, wnd)
        end
        drawPlayerMarker(wdl, wnd, player_marker_size[0])
        if rotate_map_with_camera_heading[0] then
            imgui.RotateVerts(wdl, start, wdl.VtxBuffer.Size, imgui.ImVec2(math.rad(-angle), math.rad(-angle)), imgui.ImVec2(wnd.centre.x, wnd.centre.y)) -- rotate map, markers(players, icons) and gangzones
        end

        local c = custom_border_color
        fdl:AddRect(pos, imgui.ImVec2(pos.x + wsize.x, pos.y + wsize.y), use_custom_border_color[0] and imgui.ColorConvertFloat4ToU32(imgui.ImVec4(c[0], c[1], c[2], c[3])) or 0xFF000000, 4, 15, 3) -- radar frame

        drawSideOfWorld(fdl, 90, russian_world_sides[0] and "С" or "N", wnd)
        if show_all_world_sides[0] then
            drawSideOfWorld(fdl,   0, russian_world_sides[0] and "В" or "E", wnd)
            drawSideOfWorld(fdl, 180, russian_world_sides[0] and "З" or "W", wnd)
            drawSideOfWorld(fdl, 270, russian_world_sides[0] and "Ю" or "S", wnd)
        end

        imgui.End()
    end

    self.HideCursor = not isKeyDown(VK_Z)
end)

function setupRadarScale()
    if isCharInAnyCar(PLAYER_PED) then
        local speed = getCarSpeed(storeCarCharIsInNoSave(PLAYER_PED)) * 5
        size = custom_size[0] + (speed < (custom_size[0] * 1.25) and speed or (custom_size[0] * 1.25))
    else
        size = custom_size[0]
    end
end

function drawRadarMap(draw_list, texture, lc, rc, wnd)
    draw_list:AddImage(texture,
        imgui.ImVec2(wnd.centre.x - radius * 1.5, wnd.centre.y - radius * 1.5),
        imgui.ImVec2(wnd.centre.x + radius * 1.5, wnd.centre.y + radius * 1.5),
        lc, rc, 0xFFFFFFFF)
end

function drawPlayerMarker(draw_list, wnd, msize)
    local char_angle = getCharHeading(PLAYER_PED)
    local pmarker_start = draw_list.VtxBuffer.Size
    local function _draw(draw_list, wnd, msize)
        draw_list:PathArcTo(imgui.ImVec2( wnd.centre.x, wnd.centre.y + 4 ), msize, 0.0, math.pi, 16)
        draw_list:PathLineTo(imgui.ImVec2( wnd.centre.x - 0.5, wnd.centre.y - (msize * 1.25) ))
        draw_list:PathLineTo(imgui.ImVec2( wnd.centre.x + 0.5, wnd.centre.y - (msize * 1.25) ))
    end
    _draw(draw_list, wnd, msize)
    draw_list:PathFillConvex(0xFF0000FF)
    draw_list:PathClear()

    _draw(draw_list, wnd, msize)
    draw_list:PathStroke(0xFF000000, true, 1)
    draw_list:PathClear()
    imgui.RotateVerts(draw_list, pmarker_start, draw_list.VtxBuffer.Size, imgui.ImVec2(math.rad(char_angle), math.rad(char_angle)), imgui.ImVec2(wnd.centre.x, wnd.centre.y + 2))
end

function drawPlayersInStream(draw_list, wnd)
    local x, y, _ = getCharCoordinates(PLAYER_PED)
    local function _draw(pos)
        
    end

    for _, ped in ipairs(getAllChars()) do
        if not isCharDead(ped) and ped ~= PLAYER_PED then
            local cx, cy, _ = getCharCoordinates(ped)
            _draw(imgui.ImVec2(cx, cy))
        end
    end
end

function getScale() return use_custom_scale[0] and c_scale or scale end

function transform3DtoGlobal2D(pos, scale) -- from mim radar, modified
    scale = scale or imgui.ImVec2(1, 1)
    return imgui.ImVec2((pos.x + 3000 * scale.x) / (6000 * scale.x), (-pos.y + 3000 * scale.y) / (6000 * scale.y))
end

function drawSideOfWorld(draw_list, angle, marker, wnd)
    local x, y, _ = getActiveCameraCoordinates()
    local ax, ay, _ = getActiveCameraPointAt()

    local a = (math.rad((rotate_map_with_camera_heading[0] and getHeadingFromVector2d(ax - x, ay - y) or 0) - angle) / math.rad(360)) * (math.pi * 2)
    local side = limitRadarPoint(
        wnd.centre.x + radius * math.cos(a),
        wnd.centre.y + wnd.size.y / 1.4 * math.sin(a),
        inner_radar_icons[0] and 10 or 0, wnd
    )
    local marker_size = imgui.CalcTextSize(u8(marker))

    draw_list:AddCircleFilled(side, 10, 0xFF000000, 16)
    draw_list:AddTextFontPtr(default_font.ptr, default_font.width + 2, imgui.ImVec2(side.x - marker_size.x / 2, side.y - marker_size.y / 1.6), 0xFFFFFFFF, u8(marker))
end

function limitRadarPoint(x, y, radius, wnd)
    if x < wnd.corners[1].x + (radius or 0) then x = wnd.corners[1].x + (radius or 0) end
    if x > wnd.corners[2].x - (radius or 0) then x = wnd.corners[2].x - (radius or 0) end
    if y < wnd.corners[1].y + (radius or 0) then y = wnd.corners[1].y + (radius or 0) end
    if y > wnd.corners[2].y - (radius or 0) then y = wnd.corners[2].y - (radius or 0) end
    return imgui.ImVec2(x, y)
end

function isPointInRadar(x, y, pradius, wnd)
    if  x > wnd.corners[1].x + (pradius or 0)
    and x < wnd.corners[2].x - (pradius or 0)
    and y > wnd.corners[1].y + (pradius or 0)
    and y < wnd.corners[2].y - (pradius or 0)
    then return true end
    return false
end

local radar_orig_byte = 0
function memDisableRadar(bDisabled) -- from CHudSA.cpp (sobeit sources)
    local FUNC_DrawRadar = 0x58A330
    if bDisabled and radar_orig_byte == 0 then
        radar_orig_byte = ffi.cast("uint8_t*", FUNC_DrawRadar)[0]
        ffi.cast("uint8_t*", FUNC_DrawRadar)[0] = 0xC3
    elseif not bDisabled and radar_orig_byte ~= 0 then
        ffi.cast("uint8_t*", FUNC_DrawRadar)[0] = radar_orig_byte
        radar_orig_byte = 0
    end
end

function main()
    if not isSampLoaded() or not isSampfuncsLoaded() then return end
    repeat wait(100) until isSampAvailable()
    sampRegisterChatCommand("radar", function()
        draw_radar[0] = not draw_radar[0]
        memDisableRadar(draw_radar[0])
    end)
    wait(-1)
end

function getPointOnCurve(cstart, cend, cradius, point, max_points)
    local a = cstart + (point / max_points) * (cend - cstart)
    return imgui.ImVec2(math.cos(a) * cradius, math.sin(a) * cradius)
end

function onScriptTerminate(sc, qg)
    if map_image then imgui.ReleaseTexture(map_image) map_image = nil end
    if radar_orig_byte ~= 0 then memDisableRadar(false) end
    collectgarbage()
end

-- >> imgui_internal.h
--function ImMin(lhs, rhs) return imgui.ImVec2(math.min(lhs.x, rhs.x), math.min(lhs.y, rhs.y)); end
--function ImMax(lhs, rhs) return imgui.ImVec2(math.max(lhs.x, rhs.x), math.max(lhs.y, rhs.y)); end
--function ImDot(a, b) return a.x * b.x + a.y * b.y end
--function ImClamp(v, mn, mx) return imgui.ImVec2((v.x < mn.x) and mn.x or ((v.x > mx.x) and mx.x or v.x), (v.y < mn.y) and mn.y or ((v.y > mx.y) and mx.y or v.y)); end
function ImRotate(v, cos_a, sin_a) return imgui.ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a) end
--function ImLengthSqr(vec2) return (vec2.x * vec2.x) + (vec2.y * vec2.y) end
-- <<

-- int vtx_start, int vtx_end, ImVec2 rotate_vec ( в радианах ), ImVec2 center_vec
function imgui.RotateVerts(draw_list, vtx_start, vtx_end, rotate_vec, center_vec)
    local base = math.pi / 2
    local sx, cx = math.sin(base + rotate_vec.x), math.cos(base + rotate_vec.x)
    local sy, cy = math.sin(base + rotate_vec.y), math.cos(base + rotate_vec.y)
    local center = { ImRotate(center_vec, sx, cx), ImRotate(center_vec, sy, cy) }

    center[1].x = center[1].x - center_vec.x
    center[2].y = center[2].y - center_vec.y

    for vert = vtx_start, vtx_end do
        local pos = draw_list.VtxBuffer.Data[vert].pos
        local rot = { ImRotate(pos, sx, cx), ImRotate(pos, sy, cy) }
        draw_list.VtxBuffer.Data[vert].pos.x = rot[1].x - center[1].x
        draw_list.VtxBuffer.Data[vert].pos.y = rot[2].y - center[2].y
    end
end
Либа imagesize которая юзается в скрипте: https://github.com/leafo/imagesize
 
  • Вау
Реакции: хуега)

XRLM

Известный
2,552
870
не могу додуматься последней извилиной как это прикрутить,без всяких анимаций и все,чтобы просто наклонено было сразу в исходном положении
убираешь зависимость поворота от таймера и крутишь как хочешь
 
  • Нравится
Реакции: хромиус)