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

chapo

чопа сребдс // @moujeek
Модератор
8,959
11,737
Как только ввожу команду и открываю диалог, то открывается пункт Информация, а не диалог с выбором пункта
Lua:
function cmd_mh()
    lua_thread.create(function()
        sampShowDialog(2, "{ff0000}МВД{0000ff}Хелпер", "Информация\nНастройки", "Выбрать", "Отмена", 2)
        while sampIsDialogActive(1) do wait(100) end
        local _, button, list, _ = sampHasDialogRespond(2)
        if button == 1 then
            if list == 0 then
                info()
            elseif list == 1 then
                SettingsDlg()
            end
        end
    end)
end
функция sampHasDialogRespond первым параметром возвращает bool, который ты почему то решил проигнорировать. Если bool == true, то значит что ты что-то тыкнул в диалоге, так что тебе просто нужно заменить _ например на "result" и перед проверкой на кнопку проверять равен ли result true
 
  • Нравится
Реакции: fuldic

fuldic

Новичок
10
3
функция sampHasDialogRespond первым параметром возвращает bool, который ты почему то решил проигнорировать. Если bool == true, то значит что ты что-то тыкнул в диалоге, так что тебе просто нужно заменить _ например на "result" и перед проверкой на кнопку проверять равен ли result true
также
Lua:
function cmd_mh()
    lua_thread.create(function()
        sampShowDialog(2, "{ff0000}МВД{0000ff}Хелпер", "Информация\nНастройки", "Выбрать", "Отмена", 2)
        while sampIsDialogActive(1) do wait(100) end
        local result, button, list, _ = sampHasDialogRespond(2)
        if result == true then
            if button == 1 then
                if list == 0 then
                    info()
                elseif list == 1 then
                    SettingsDlg()
                end
            end
        end
    end)
end

функция sampHasDialogRespond первым параметром возвращает bool, который ты почему то решил проигнорировать. Если bool == true, то значит что ты что-то тыкнул в диалоге, так что тебе просто нужно заменить _ например на "result" и перед проверкой на кнопку проверять равен ли result true
Такое происходит только на радмире
 
Последнее редактирование:

Ancwork

Участник
36
2
Вопрос следующий, у меня есть данный код, он качает звук arrest.mp3 с сайта и воспроизводит звук по тригеру в чате передаёт преступника, так вот... Как сделать скачивание нескольких звуков и воспроизводить их рандомном порядке?:
local sounds = {
  {
    url='https://domen-domen/arrest.mp3',
    file_name = 'arrest.mp3',
  },
}

local as_action = require('moonloader').audiostream_state
local sampev = require 'lib.samp.events'
local dirMusic = loadAudioStream('moonloader/sounds/arrest.mp3')

function main()
    repeat wait(0) until isSampAvailable()
    sampAddChatMessage(tag .. 'Вы используете Mhelper', -1)

    -- AHK Команды
    sampRegisterChatCommand('mhelp', mhelp)
if not doesDirectoryExist(getWorkingDirectory()..'\\sounds') then
    createDirectory(getWorkingDirectory()..'\\sounds')
  end
  for i, v in ipairs(sounds) do
    sampAddChatMessage('{FFFFFF}Все файлы для работы загружены!', -1 , v['file_name'])
    if not doesFileExist(getWorkingDirectory()..'\\sounds\\'..v['file_name']) then
      downloadUrlToFile(v['url'], getWorkingDirectory()..'\\sounds\\'..v['file_name'])
    end
  end


    while true do
    wait(0)
    if wasKeyPressed(key.VK_F3) then -- активация по нажатию клавиши X
        main_window_state.v = not main_window_state.v -- переключаем статус активности окна, не забываем про .v
    end
    imgui.Process = main_window_state.v -- теперь значение imgui.Process всегда будет задаваться в зависимости от активности основного окна
  end
end

function sampev.onServerMessage(color, text)
    if text:find('передаёт преступника') then
        setAudioStreamState(dirMusic, as_action.PLAY)
        setAudioStreamVolume(dirMusic, 70)
    end
end
 
  • Грустно
Реакции: qdIbp

qdIbp

Автор темы
Проверенный
1,440
1,184
Вопрос следующий, у меня есть данный код, он качает звук arrest.mp3 с сайта и воспроизводит звук по тригеру в чате передаёт преступника, так вот... Как сделать скачивание нескольких звуков и воспроизводить их рандомном порядке?:
local sounds = {
  {
    url='https://domen-domen/arrest.mp3',
    file_name = 'arrest.mp3',
  },
}

local as_action = require('moonloader').audiostream_state
local sampev = require 'lib.samp.events'
local dirMusic = loadAudioStream('moonloader/sounds/arrest.mp3')

function main()
    repeat wait(0) until isSampAvailable()
    sampAddChatMessage(tag .. 'Вы используете Mhelper', -1)

    -- AHK Команды
    sampRegisterChatCommand('mhelp', mhelp)
if not doesDirectoryExist(getWorkingDirectory()..'\\sounds') then
    createDirectory(getWorkingDirectory()..'\\sounds')
  end
  for i, v in ipairs(sounds) do
    sampAddChatMessage('{FFFFFF}Все файлы для работы загружены!', -1 , v['file_name'])
    if not doesFileExist(getWorkingDirectory()..'\\sounds\\'..v['file_name']) then
      downloadUrlToFile(v['url'], getWorkingDirectory()..'\\sounds\\'..v['file_name'])
    end
  end


    while true do
    wait(0)
    if wasKeyPressed(key.VK_F3) then -- активация по нажатию клавиши X
        main_window_state.v = not main_window_state.v -- переключаем статус активности окна, не забываем про .v
    end
    imgui.Process = main_window_state.v -- теперь значение imgui.Process всегда будет задаваться в зависимости от активности основного окна
  end
end

function sampev.onServerMessage(color, text)
    if text:find('передаёт преступника') then
        setAudioStreamState(dirMusic, as_action.PLAY)
        setAudioStreamVolume(dirMusic, 70)
    end
end
Примерно так, хотя можно было сделать по красивше
Lua:
local sounds = {
    [1] =
    {
        url='https://domen-domen/arrest1.mp3',
        file_name='arrest1.mp3',
    },
    
    [2] =
    {
        url='https://domen-domen/arrest2.mp3',
        file_name='arrest2.mp3',
    },
    
    [3] =
    {
        url='https://domen-domen/arrest3.mp3',
        file_name='arrest3.mp3',
    },
}

local as_action = require('moonloader').audiostream_state
local sampev = require('lib.samp.events')
for i = 1, 3 do
    _G['dirMusic_'..i] = loadAudioStream('moonloader/sounds/arrest'..i..'.mp3')
end

function main()
    repeat
        wait(0)
    until isSampAvailable()
    sampAddChatMessage(tag .. 'Вы используете Mhelper', -1)

    sampRegisterChatCommand('mhelp', function()
        if not doesDirectoryExist(getWorkingDirectory()..'\\sounds') then
            createDirectory(getWorkingDirectory()..'\\sounds')
        end
        
        for i, v in ipairs(sounds) do
            if not doesFileExist(getWorkingDirectory()..'\\sounds\\'..v[i]['file_name']) then
                downloadUrlToFile(v[i]['url'], getWorkingDirectory()..'\\sounds\\'..v[i]['file_name'])
            end
        end
        sampAddChatMessage('{FFFFFF}Все файлы для работы загружены!', -1)
    end)

    while true do
        wait(0)
       
        imgui.Process = main_window_state.v
        if wasKeyPressed(key.VK_F3) then
            main_window_state.v = not main_window_state.v
        end
   
    end
end

function sampev.onServerMessage(color, text)
    if string.match(text, 'передаёт преступника') then
        math.randomseed(os.clock())
        local a = math.random(1, 3)
        setAudioStreamState(_G['dirMusic_'..a], as_action.PLAY)
        setAudioStreamVolume(_G['dirMusic_'..a], 70)
    end
end
 
Последнее редактирование:

Июнь

Новичок
1
0
Можно ли отследить событие краша в игре?
К примеру игра крашится(вылетает) и мы к примеру делаем следующим действием запись в файл или хотя бы что-то.
Не нашел примеров на blasthack.
 
  • Злость
Реакции: qdIbp

ha1z4

Новичок
5
0
в чем может быть проблема? возникает после /update. на момент теста автообновления стояла версия 1.03
tFFNaTV.png



govnolua:
script_name("LunaTools")
script_author("HermitTech")
script_description("Luna Tools. Vers. 1")
script_version("1")

require "lib.moonloader"
require "lib.sampfuncs"
local dlstatus = require('moonloader').download_status
local inicfg = require 'inicfg'

update_status = false

local script_vers = 4
local script_vers_text = "1.04"

local update_url = "https://raw.githubusercontent.com/XakerTv/moontools/refs/heads/main/update.ini"
local update_path = getWorkingDirectory() .. "/update.ini"

local script_url = "https://github.com/XakerTv/moontools/raw/refs/heads/main/tools.lua"
local script_path = thisScript().path

scriptName = "{8B59FF}[ Luna Tools ]{FFFFFF}"
betaScriptName = "[ Luna | DeBug ]"
scriptVersion = "1a"

function main()
    while not isSampAvailable() do wait(0) end
    local playerId = select(2, sampGetPlayerIdByCharHandle(PLAYER_PED))
    local playerName = sampGetPlayerNickname(playerId)
    sampAddChatMessage(scriptName .. " Скрипт готов к работе.", 0xFFFFFF)
    sampAddChatMessage(scriptName .. " С возвращением, " .. playerName, 0xFFFFFF)
    sampAddChatMessage(betaScriptName .. " Открыть главное меню: /mtools", 0xFFFFFF)
    sampAddChatMessage(betaScriptName .. " Версия скрипта: " .. scriptVersion, 0xBFBFBF)

    sampRegisterChatCommand("update", function()
        checkForUpdate()
    end)

    while true do
        wait(0)
        if update_status then
            updateScript()
            break
        end
    end
end

function checkForUpdate()
    sampAddChatMessage(scriptName .. ' Проверка обновлений...', 0xFFFFFF)
    downloadUrlToFile(update_url, update_path, function(id, status)
        if status == dlstatus.STATUS_ENDDOWNLOADDATA then
            local updateIni = inicfg.load(nil, update_path)
            if updateIni and updateIni.info then
                if tonumber(updateIni.info.vers) > script_vers then
                    sampAddChatMessage(scriptName .. ' Доступно обновление! Версия: ' .. updateIni.info.vers_text, -1)
                    update_status = true
                else
                    sampAddChatMessage(scriptName .. ' У вас последняя версия.', 0xFFFFFF)
                end
            else
                sampAddChatMessage(scriptName .. ' Ошибка: update.ini отсутствует или имеет неправильный формат.',
                    0xFF0000)
            end
        else
            sampAddChatMessage(scriptName .. ' Ошибка загрузки update.ini.', 0xFF0000)
        end
    end)
end

function updateScript()
    sampAddChatMessage(scriptName .. ' Обновление началось. Пожалуйста, подождите...', 0xFFFFFF)
    downloadUrlToFile(script_url, script_path, function(id, status)
        if status == dlstatus.STATUS_ENDDOWNLOADDATA then
            sampAddChatMessage(scriptName .. ' Скрипт успешно обновлен!', -1)
            sampAddChatMessage(scriptName .. '==============ОБНОВЛЕНИЕ ' .. scriptVersion .. ' ==============', 0x8B59FF)
            sampAddChatMessage(scriptName .. '* Добавлено: *', -1)
            sampAddChatMessage(scriptName .. '- Функция автообновления', -1)
            thisScript():reload()
        elseif status == dlstatus.STATUS_ERROR then
            sampAddChatMessage(scriptName .. ' Ошибка загрузки нового скрипта.', 0xFF0000)
        end
    end)
end
 

kultizdat.

Известный
158
12
UPD: Решил. Оказывается, просто в print полностью не отображалсь строка. 🤡
Всем привет. Хочу получить полный список машин из /cars (cef) на Аризоне.
Когда ни одна машина не загружена, то все отображается как надо,
1.jpg
но стоит загрузить хоть один автомобиль, строка с этим автомобилем обрезается до слова window.exe
2.jpg
В чем проблема?
Lua:
    if id == 220 then
 
        raknetBitStreamReadInt8(bs)
        if raknetBitStreamReadInt8(bs) == 17 then
            raknetBitStreamIgnoreBits(bs, 32)
            local sizeStrings = raknetBitStreamReadInt8(bs)
            raknetBitStreamIgnoreBits(bs, 24)
            local text = raknetBitStreamReadString(bs, sizeStrings)
            print(text)
        end
    end
Строки из Cef
--Ьєwindow.executeEvent('event.vehicleMenu.pushVehicleItem',`[{"id":3,"title":"Brabus700","sysName":"931.png","status":"notLoaded","labels":[{"title":"Не загружен","icon":"icon-id"}]}]`);яяяя | 220, 17, 0, 0, 0, 0, 186, 0, 0, 0, 119, 105, 110, 100, 111, 119, 46, 101, 120, 101, 99, 117, 116, 101, 69, 118, 101, 110, 116, 40, 39, 101, 118, 101, 110, 116, 46, 118, 101, 104, 105, 99, 108, 101, 77, 101, 110, 117, 46, 112, 117, 115, 104, 86, 101, 104, 105, 99, 108, 101, 73, 116, 101, 109, 39, 44, 32, 96, 91, 123, 34, 105, 100, 34, 58, 51, 44, 34, 116, 105, 116, 108, 101, 34, 58, 34, 66, 114, 97, 98, 117, 115, 32, 55, 48, 48, 34, 44, 34, 115, 121, 115, 78, 97, 109, 101, 34, 58, 34, 57, 51, 49, 46, 112, 110, 103, 34, 44, 34, 115, 116, 97, 116, 117, 115, 34, 58, 34, 110, 111, 116, 76, 111, 97, 100, 101, 100, 34, 44, 34, 108, 97, 98, 101, 108, 115, 34, 58, 91, 123, 34, 116, 105, 116, 108, 101, 34, 58, 34, 205, 229, 32, 231, 224, 227, 240, 243, 230, 229, 237, 224, 34, 44, 34, 105, 99, 111, 110, 34, 58, 34, 105, 99, 111, 110, 45, 105, 100, 34, 125, 93, 125, 93, 96, 41, 59, 255, 255, 255, 255
Ьщwindow.executeEvent('event.vehicleMenu.pushVehicleItem',`[{"id":3,"title":"Brabus700","sysName":"931.png","status":"loaded","labels":[{"title":"Нет","icon":"icon-car-number"},{"title":"ДВС","icon":"icon-fuel"},{"title":1217,"icon":"icon-id"}]}]`);яяяя | 220, 17, 0, 0, 0, 0, 249, 0, 0, 0, 119, 105, 110, 100, 111, 119, 46, 101, 120, 101, 99, 117, 116, 101, 69, 118, 101, 110, 116, 40, 39, 101, 118, 101, 110, 116, 46, 118, 101, 104, 105, 99, 108, 101, 77, 101, 110, 117, 46, 112, 117, 115, 104, 86, 101, 104, 105, 99, 108, 101, 73, 116, 101, 109, 39, 44, 32, 96, 91, 123, 34, 105, 100, 34, 58, 51, 44, 34, 116, 105, 116, 108, 101, 34, 58, 34, 66, 114, 97, 98, 117, 115, 32, 55, 48, 48, 34, 44, 34, 115, 121, 115, 78, 97, 109, 101, 34, 58, 34, 57, 51, 49, 46, 112, 110, 103, 34, 44, 34, 115, 116, 97, 116, 117, 115, 34, 58, 34, 108, 111, 97, 100, 101, 100, 34, 44, 34, 108, 97, 98, 101, 108, 115, 34, 58, 91, 123, 34, 116, 105, 116, 108, 101, 34, 58, 34, 205, 229, 242, 34, 44, 34, 105, 99, 111, 110, 34, 58, 34, 105, 99, 111, 110, 45, 99, 97, 114, 45, 110, 117, 109, 98, 101, 114, 34, 125, 44, 123, 34, 116, 105, 116, 108, 101, 34, 58, 34, 196, 194, 209, 34, 44, 34, 105, 99, 111, 110, 34, 58, 34, 105, 99, 111, 110, 45, 102, 117, 101, 108, 34, 125, 44, 123, 34, 116, 105, 116, 108, 101, 34, 58, 49, 50, 49, 55, 44, 34, 105, 99, 111, 110, 34, 58, 34, 105, 99, 111, 110, 45, 105, 100, 34, 125, 93, 125, 93, 96, 41, 59, 255, 255, 255, 255
1000058824.jpg
1000058820.jpg
 
Последнее редактирование:

Faiser

Участник
65
16
приветствую. у меня есть .json файл, который содержит в себе ники игроков (к примеру 50 штук).
как мне сделать так, чтобы скрипт прописывал к примеру "/invite" и первый ник из json'а, затем "/invite" и второй ник оттуда же и так далее?
 

Неадекватная сова

Известный
Проверенный
251
226
приветствую. у меня есть .json файл, который содержит в себе ники игроков (к примеру 50 штук).
как мне сделать так, чтобы скрипт прописывал к примеру "/invite" и первый ник из json'а, затем "/invite" и второй ник оттуда же и так далее?
 

Faiser

Участник
65
16
подскажите пожалуйста, как сделать так, чтобы при нахождении строчки "[Ошибка] Пока ты в больнице, ты не можешь использовать укроп", скрипт автоматически сохранял ее в json файлик (для кого то это просто, но я 0 в луа).
p.s. по этому уроку я не понял
 

kyrtion

Известный
1,022
373
Поправим немного алгоритм:
1. при нахождении строчки "[Ошибка] Пока ты в больнице, ты не можешь использовать укроп"
2. скрипт автоматически сохранял ее в json файлик
Установим 2 либ: samp.lua и carbjsonconfig. а теперь дальше:
Lua:
-- Task-list:
-- 1. при нахождении строчки "[Ошибка] Пока ты в больнице, ты не можешь использовать укроп"
-- 2. скрипт автоматически сохранял ее в json файлик

local cjc = require('carbjsonconfig')
local sampev = require('samp.events')

-- Воспользуем либ carbJsonConfig ❤ для обработки в JSON
-- типо наша таблица со значениями
local json = {
    -- пустая таблица в chats по умолчанию при первой загрузки скрипта
    chats = {}
}

-- для подгрузки значений из файла
cjc.load(getWorkingDirectory()..'\\config\\test_your_script.json', json)
json() -- для сохранения в файл, также если конфиг изменилось - его нужно сохранить
-- json('reset') -- для сброса таблицы и файла конфига, но желательно не трогать
-- как будто уронишь всех данные в бд и потеряешь без восстановления

local regex = {
    dillInHospital = '^%[Ошибка%] Пока ты в больнице, ты не можешь использовать укроп$'
}

-- отдельная функция для обработки в onServerMessage
local function checkDillInHospital(color, text)
    text = text:gsub('{%x%x%x%x%x%x}', '') -- убираем цветной hex в виде: {FFFFFF}
    if text:find(regex.dillInHospital) then
        table.insert(json.chats, text) -- засунем строка в json.chats
        -- или: json.chats[#json.chats + 1] = text
        json() -- сохраняем
    end
end

-- событие на появление строка в чате
function sampev.onServerMessage(color, text)
    checkDillInHospital(color, text)
    -- вызывает функция. Вернется nil, то там неуказано return для возвращение значение
    -- но функция выполняется 100%
end
 

kultizdat.

Известный
158
12
Поправим немного алгоритм:

Установим 2 либ: samp.lua и carbjsonconfig. а теперь дальше:
Lua:
-- Task-list:
-- 1. при нахождении строчки "[Ошибка] Пока ты в больнице, ты не можешь использовать укроп"
-- 2. скрипт автоматически сохранял ее в json файлик

local cjc = require('carbjsonconfig')
local sampev = require('samp.events')

-- Воспользуем либ carbJsonConfig ❤ для обработки в JSON
-- типо наша таблица со значениями
local json = {
    -- пустая таблица в chats по умолчанию при первой загрузки скрипта
    chats = {}
}

-- для подгрузки значений из файла
cjc.load(getWorkingDirectory()..'\\config\\test_your_script.json', json)
json() -- для сохранения в файл, также если конфиг изменилось - его нужно сохранить
-- json('reset') -- для сброса таблицы и файла конфига, но желательно не трогать
-- как будто уронишь всех данные в бд и потеряешь без восстановления

local regex = {
    dillInHospital = '^%[Ошибка%] Пока ты в больнице, ты не можешь использовать укроп$'
}

-- отдельная функция для обработки в onServerMessage
local function checkDillInHospital(color, text)
    text = text:gsub('{%x%x%x%x%x%x}', '') -- убираем цветной hex в виде: {FFFFFF}
    if text:find(regex.dillInHospital) then
        table.insert(json.chats, text) -- засунем строка в json.chats
        -- или: json.chats[#json.chats + 1] = text
        json() -- сохраняем
    end
end

-- событие на появление строка в чате
function sampev.onServerMessage(color, text)
    checkDillInHospital(color, text)
    -- вызывает функция. Вернется nil, то там неуказано return для возвращение значение
    -- но функция выполняется 100%
end
На 27ой строке просто %x+ разве нельзя?
Вместо повторения одно и того же