Вопросы по 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
 
Последнее редактирование:

Макс | Lycorn

Участник
166
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

Известный
701
518
Нужна помощь с отображением маркеров внутри 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
 
  • Вау
Реакции: whyega52

XRLM

Против ветра рождённый
Проверенный
1,516
1,107
не могу додуматься последней извилиной как это прикрутить,без всяких анимаций и все,чтобы просто наклонено было сразу в исходном положении
убираешь зависимость поворота от таймера и крутишь как хочешь
 
  • Нравится
Реакции: chromiusj

sep

Известный
681
76
помогите вставить проверку на ид (что он есть на сервере) без этого крашет скрипт при воде команды

код:
    sampRegisterChatCommand('tt', function(arg)
        if arg:find('(%d+) (.+)') then
    arg1, arg2 = arg:match('(%d+) (.+)')
    
        local playerNickname = sampGetPlayerNickname(arg1)
        for line in io.lines(FileNameWrite) do -- Цикл, читающий все линии в файле
            if line:find(playerNickname) then
                local number = line:match('%=(%d+)')
                sampProcessChatInput("/sms " ..number.. " " ..arg2)
                local result = setClipboardText(number)
                return
            end
        end
        --sampAddChatMessage('Игрок '..playerNickname..' не найден в списке', -1)
        
    end   
    end)
 

YarikVL

Известный
Проверенный
4,750
1,815
помогите вставить проверку на ид (что он есть на сервере) без этого крашет скрипт при воде команды

код:
    sampRegisterChatCommand('tt', function(arg)
        if arg:find('(%d+) (.+)') then
    arg1, arg2 = arg:match('(%d+) (.+)')
   
        local playerNickname = sampGetPlayerNickname(arg1)
        for line in io.lines(FileNameWrite) do -- Цикл, читающий все линии в файле
            if line:find(playerNickname) then
                local number = line:match('%=(%d+)')
                sampProcessChatInput("/sms " ..number.. " " ..arg2)
                local result = setClipboardText(number)
                return
            end
        end
        --sampAddChatMessage('Игрок '..playerNickname..' не найден в списке', -1)
       
    end  
    end)
На 4 строку:
if sampIsPlayerConnected(arg1) then

Ну и ниже закроешь end’ом

Еще можешь на лвл проверять ( если 0 лвл то игрок еще не авторизовался )
 
  • Нравится
Реакции: sep

chapo

чопа сребдс // @moujeek
Модератор
8,934
11,702
Как получить название функции из которой была вызвана функция?
Например тут мне нужно что бы в принт вывело myCallback, funcA
Lua:
local function myCallback() 
    print('function name is', debug.getinfo(1).name) --> Out: "myCallback"
end

local function funcA()
    myCallback()
end

funcA()

такой вариант не подходит
Lua:
local function myCallback(from)
    print('function name is', debug.getinfo(1).name, 'called from', from)
end

local function funcA()
    myCallback(debug.getinfo(1).name)
end

funcA()
 

whyega52

Гений, миллионер, плейбой, долбаеб
Модератор
2,798
2,659
Как получить название функции из которой была вызвана функция?
Например тут мне нужно что бы в принт вывело myCallback, funcA
Lua:
local function myCallback()
    print('function name is', debug.getinfo(1).name) --> Out: "myCallback"
end

local function funcA()
    myCallback()
end

funcA()

такой вариант не подходит
Lua:
local function myCallback(from)
    print('function name is', debug.getinfo(1).name, 'called from', from)
end

local function funcA()
    myCallback(debug.getinfo(1).name)
end

funcA()
можно делать так:
Lua:
function test()
  local info = debug.getinfo(2, "n")
  print(info.name)
end

function foo()
  test()
end

foo() -- out: foo
если отойти от либы дебага, то можно попробовать что-то сделать через _G
 

Sadow

Известный
1,428
593
Какая есть альтернатива функциям: setCarCollision, setCharCollision, setObjectCollision?