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

Robert Daniels

Известный
12
0
Вопрос такой.
У меня есть горячие клавиши, которые отвечают за вызов команд (Кнопка 1 - /admins, Кнопка 2 - /leaders).
Но, есть еще одна функция в цикле и конкретно задержка мешает срабатыванию горячих клавиш, т.е., они срабатывают с задержкой, если изменяю "wait(1000)" на "wait(0)" или вовсе убираю, функция начинает не корректно считать секунды. Сломал всю голову, помогите - сенсеи :c

Lua:
local _, myid = sampGetPlayerIdByCharHandle(PLAYER_PED)
      if not sampIsPlayerPaused(myid) then
      wait(1000)
      local data = LIP.load('moonloader\\config\\second.ini')
      data.second.gosecond = data.second.gosecond + 1
      LIP.save('moonloader\\config\\second.ini', data);
    end

Вот код, с циклом вместе:

Lua:
while true do
    wait(0)
local _, myid = sampGetPlayerIdByCharHandle(PLAYER_PED)
      if not sampIsPlayerPaused(myid) then
      wait(1000)
      local data = LIP.load('moonloader\\config\\second.ini')
      data.second.gosecond = data.second.gosecond + 1
      LIP.save('moonloader\\config\\second.ini', data);
    end
 

Bogach

Активный
558
27
Вопрос такой.
У меня есть горячие клавиши, которые отвечают за вызов команд (Кнопка 1 - /admins, Кнопка 2 - /leaders).
Но, есть еще одна функция в цикле и конкретно задержка мешает срабатыванию горячих клавиш, т.е., они срабатывают с задержкой, если изменяю "wait(1000)" на "wait(0)" или вовсе убираю, функция начинает не корректно считать секунды. Сломал всю голову, помогите - сенсеи :c

Lua:
local _, myid = sampGetPlayerIdByCharHandle(PLAYER_PED)
      if not sampIsPlayerPaused(myid) then
      wait(1000)
      local data = LIP.load('moonloader\\config\\second.ini')
      data.second.gosecond = data.second.gosecond + 1
      LIP.save('moonloader\\config\\second.ini', data);
    end

Вот код, с циклом вместе:

Lua:
while true do
    wait(0)
local _, myid = sampGetPlayerIdByCharHandle(PLAYER_PED)
      if not sampIsPlayerPaused(myid) then
      wait(1000)
      local data = LIP.load('moonloader\\config\\second.ini')
      data.second.gosecond = data.second.gosecond + 1
      LIP.save('moonloader\\config\\second.ini', data);
    end
Создай второй поток и функции с задержкой помести туда.
 

4el0ve4ik

Известный
Всефорумный модератор
1,550
1,343
Вопрос такой.
У меня есть горячие клавиши, которые отвечают за вызов команд (Кнопка 1 - /admins, Кнопка 2 - /leaders).
Но, есть еще одна функция в цикле и конкретно задержка мешает срабатыванию горячих клавиш, т.е., они срабатывают с задержкой, если изменяю "wait(1000)" на "wait(0)" или вовсе убираю, функция начинает не корректно считать секунды. Сломал всю голову, помогите - сенсеи :c

Lua:
local _, myid = sampGetPlayerIdByCharHandle(PLAYER_PED)
      if not sampIsPlayerPaused(myid) then
      wait(1000)
      local data = LIP.load('moonloader\\config\\second.ini')
      data.second.gosecond = data.second.gosecond + 1
      LIP.save('moonloader\\config\\second.ini', data);
    end

Вот код, с циклом вместе:

Lua:
while true do
    wait(0)
local _, myid = sampGetPlayerIdByCharHandle(PLAYER_PED)
      if not sampIsPlayerPaused(myid) then
      wait(1000)
      local data = LIP.load('moonloader\\config\\second.ini')
      data.second.gosecond = data.second.gosecond + 1
      LIP.save('moonloader\\config\\second.ini', data);
    end
Создай второй поток и функции с задержкой помести туда.
Ну или делать таймер с помощью os.clock()
 

makasinuch

Новичок
52
5
Сделай на хуке.
Для меня это пока что очень сложная тема! Потому как вообще с этим не знаком

На данный момент интересуют 2 вопроса
1. Как мне сделать что бы sampSendChat("/fillcar") сработал только 1 Раз?? Даже если string.find находит данный текст
Lua:
str, --[[string]] prefstr, --[[int]] colstr, --[[int]] pcolstr = sampTextdrawGetString(2076)
if string.find(str, "fuel: 79", 0, true) ~= nil then
sampSendChat("/fillcar")
end
2. Возможно ли данный код завершить досрочно при каком то событии? Например если после 2 нажатии появился в чате какой то текст - то функция прерывается
Lua:
for i = 1, 5 do
        sampSendDialogResponse(14, 1, 5, -1)
      end
/code]
 

CAPTA!N

Новичок
220
50
Lua:
script_name("aaa")

require 'lib.moonloader'
require 'lib.sampfuncs'

dict = {
    ["АВ"] = "[SFPD]",
    ["МВ"] = "[LVPD]",
    ["ЫВ"] = "[LSPD]",
    ["YY"] = "[Yakuza]",
    ["НН"] = "[Yakuza]"
}

function main()
   if not isSampLoaded() or not isCleoLoaded() or not isSampfuncsLoaded() then return end
    while not isSampAvailable() do wait(100) end
   while true do
     wait(0)
     input = sampGetChatInputText()
      for bad, good in pairs(dict) do
          sampSetChatInputText(string.gsub(input, bad, good))
      end
   end
end
Я пытался задебажить эту бадягу, но ничего хорошего мне не высветилось. В логе все ок. Прощу помощи
 

FYP

Известный
Автор темы
Администратор
1,763
5,906
Кроме setVirtualKeyDown больше нету возможности эмулировать нажатия клавиш?
есть ещё setGameKeyState для эмуляции игровых нажатий
Как мне сделать что бы sampSendChat("/fillcar") сработал только 1 Раз?? Даже если string.find находит данный текст
добавить переменную-флаг и перед действием проверять, задана ли она
Возможно ли данный код завершить досрочно при каком то событии? Например если после 2 нажатии появился в чате какой то текст - то функция прерывается
этот код выполняется моментально, его не получится завершить ни по какому событию, т.к. никакое событие не сработает (за исключением события onScriptTerminate, если это приведёт к ошибке), пока он не выполнится. то, что ты хочешь сделать, нужно делать другим способом.
Lua:
script_name("aaa")

require 'lib.moonloader'
require 'lib.sampfuncs'

dict = {
    ["АВ"] = "[SFPD]",
    ["МВ"] = "[LVPD]",
    ["ЫВ"] = "[LSPD]",
    ["YY"] = "[Yakuza]",
    ["НН"] = "[Yakuza]"
}

function main()
   if not isSampLoaded() or not isCleoLoaded() or not isSampfuncsLoaded() then return end
    while not isSampAvailable() do wait(100) end
   while true do
     wait(0)
     input = sampGetChatInputText()
      for bad, good in pairs(dict) do
          sampSetChatInputText(string.gsub(input, bad, good))
      end
   end
end
Я пытался задебажить эту бадягу, но ничего хорошего мне не высветилось. В логе все ок. Прощу помощи
у тебя инпут перезаписывается каждый раз в цикле, не учитывая предыдущие замены. вместо sampSetChatInputText делай обновление переменной input в цикле и после цикла вызывай sampSetChatInputText
 

CAPTA!N

Новичок
220
50
Lua:
script_name("aaa")

require 'lib.moonloader'
require 'lib.sampfuncs'

dict = {
   ["АВ"] = "[SFPD]",
   ["МВ"] = "[LVPD]",
   ["ЫВ"] = "[LSPD]",
   ["YY"] = "[Yakuza]",
   ["НН"] = "[Yakuza]"
}

function main()
   if not isSampLoaded() or not isCleoLoaded() or not isSampfuncsLoaded() then return end
    while not isSampAvailable() do wait(100) end
   while true do
     wait(0)
      for bad, good in pairs(dict) do
            input = sampGetChatInputText()
            if string.match(input, bad) == bad then
              sampSetChatInputText(string.gsub(input, bad, good))
            end
      end
   end
end
Я решил не выносить sampSetChatInputText, а сделать для него условие, так как если делать так как ты говоришь, то он будет просто перезаписывать в пустую, что возможно повлияет на фпс. У меня так же было и с предыдущей версией, где я делал два массива со словами, которые мне нужно заменить, и словами на замену, но все равно не работает.
 

FYP

Известный
Автор темы
Администратор
1,763
5,906
@Шелди решать - это, конечно, хорошо, но только в следующий раз решая как сделать скрипт или не слушать правильные рекомендации, не забудь воспользоваться логикой.
и к слову о фпс: твой код, как первый, так и второй, хоть и не повлияет на фпс, но теоретически будет создавать большую нагрузку, чем правильное решение, предложенное мой.
 

CAPTA!N

Новичок
220
50
@Шелди решать - это, конечно, хорошо, но только в следующий раз решая как сделать скрипт или не слушать правильные рекомендации, не забудь воспользоваться логикой.
и к слову о фпс: твой код, как первый, так и второй, хоть и не повлияет на фпс, но теоретически будет создавать большую нагрузку, чем правильное решение, предложенное мой.
Но насколько я понимаю, скрипт будет впустую перезаписывать независимо от того есть там что поменять или нет. Или я не прав?

В любом случае код не пашет
Lua:
function main()
   if not isSampLoaded() or not isCleoLoaded() or not isSampfuncsLoaded() then return end
    while not isSampAvailable() do wait(100) end
   while true do
     wait(0)
      for bad, good in pairs(dict) do
            input = sampGetChatInputText()
          string.gsub(input, bad, good)
      end
    sampSetChatInputText(input)
   end
end
 
Последнее редактирование модератором:

Moonlight_Ru

Известный
66
21
в коде вроде всё в порядке, должно работать без проблем, но можешь попробовать изменить data.targetType на tonumber(data.targetType).
и судя по крашлогу у тебя установлен samp addon и хуева гора всяких модов, ты уверен, что проблема вызвана не конфликтами?

реально?
Спасибо, ФИП, совет с tonumber помог.

Самп аддон я, кажется, никогда не ставил. Хотя я не знаю за что отвечает половина .asi файлов у меня в сборке, когда-то давно их туда закинул. :D

Windows 2000 пишет в крашлоге видимо потому, что совместимость у gta_sa.exe на него выставлена.
 

FYP

Известный
Автор темы
Администратор
1,763
5,906
похоже, это затянется...

@Шелди вот готовый код:
Lua:
function main()
   if not isSampLoaded() or not isCleoLoaded() or not isSampfuncsLoaded() then return end
   while not isSampAvailable() do wait(100) end
   while true do
    wait(0)
     local input = sampGetChatInputText()
     local new_input = input
     for bad, good in pairs(dict) do
       new_input = string.gsub(new_input, bad, good)
     end
     if new_input ~= input then
       sampSetChatInputText(new_input)
     end
   end
end
 

Moonlight_Ru

Известный
66
21
У меня тут трабл с хуком.

Скрипт должен искать во входящем RPC onServerMessage (входящее сообщение в чат от сервера) определённое сочетание символов, и в случае, если он его найдёт, обработать всё сообщение и отправить в чат определённый текст (через sampSendChat()), используя данные, полученные из входящего сообщения, и всё это работает хорошо... вот только в том случае, если было отправлено сообщение в чат от клиента серверу, то обработанное сообщение от сервера клиенту в чат уже не выводится, в этом, собственно, и проблема.

И ещё один вопросик. Можно ли применять в регулярках moonloader'a эту конструкцию -> "(или так|или эдак)"? По идее должно работать, но не работает.

Lua:
local RPChook = require 'lib.samp.events'
local inicfg = require 'inicfg'


function main()
    while not isSampAvailable() do wait(0) end
    AutoMedsSettingsData = inicfg.load({
        Settings = {
        AutoMedsOnOffStatus = 1,
        MedsPrice = 2000,
        CMessagesOnOffStatus = 1,
        SMessagesOnOffStatus = 0,
        }
    }, "AutoMedsSettings")
    AutoMedsOnOffStatus = AutoMedsSettingsData.Settings.AutoMedsOnOffStatus
    MedsPrice = AutoMedsSettingsData.Settings.MedsPrice
    CMessagesOnOffStatus = AutoMedsSettingsData.Settings.CMessagesOnOffStatus
    SMessagesOnOffStatus = AutoMedsSettingsData.Settings.SMessagesOnOffStatus
    inicfg.save(AutoMedsSettingsData, "AutoMedsSettings")
    wait(-1)
end


function RPChook.onServerMessage(color, text)
                if AutoMedsOnOffStatus == 1 then
                                    if CMessagesOnOffStatus == 1 then
                                                        if string.find(text, "^У игрока недостаточно средств для покупки%.$") then
                                                            sampSendChat(string.format("/c У Вас не хватает денег на таблетки. ( Необходимо: $%d )", MedsPrice))
                                                        elseif string.find(text, "^Игроку уже кто%-то предложил купить лекарство%.$") then
                                                            sampSendChat(string.format("/c Вам уже предложены таблетки. Пропишите: [/accept meds]"))
                                                        elseif string.find(text, "^Игрок в чёрном списке МЧС%.$") then
                                                            sampSendChat(string.format("/c Вы в Черном Списке М.Ч.С. ( Таблетки купить не сможете. )"))
                                                        elseif string.find(text, "^Продать лекарство указанному игроку будет можно через несколько минут%.$") then
                                                            sampSendChat(string.format("/c У Вас откат на покупку таблеток, ожидайте. ( Откат 5 минут. )"))
                                                        elseif string.find(text, "^У игрока ещё много лекарства%.$") then
                                                            sampSendChat(string.format("/c У Вас максимальное количество таблеток."))
                                                        elseif SMessagesOnOffStatus == 1 then
                                                                        if string.find(text, "^Игрок далеко от тебя%.$")
                                                                        then sampSendChat(string.format("/s Вы далеко для продажи таблеток, подойдите ближе")) end
                                                        end
                                    end
                                    if
                                    string.find(text, "%+pl") or
                                    string.find(text, "%+зд") or
                                    string.find(text, "=pl") or
                                    string.find(text, "=зд") or
                                    string.find(text, "лекарс") or
                                    string.find(text, "ktrfhc") or
                                    string.find(text, "meds") or
                                    string.find(text, "медс") or
                                    string.find(text, "ьувы") or
                                    string.find(text, "vtlc") or
                                    string.find(text, "табл") or
                                    string.find(text, "tabl") or
                                    string.find(text, "nf,k") or
                                    string.find(text, "ефид")
                                    then
                                                        local _, myId = sampGetPlayerIdByCharHandle(PLAYER_PED)
                                                        local myName = sampGetPlayerNickname(myId)
                                                        if string.find(text, "(.+)%[(%d+)%] говорит%: (.+)") then
                                                                    local space_0, id = string.match (text, "(.+)%[(%d+)%] говорит%: (.+)")
                                                                    local playerName = sampGetPlayerNickname(id)
                                                                    if playerName ~= myName then
                                                                        sampSendChat(string.format("/sellmeds %d %d", id, MedsPrice))
                                                                    end
                                                        elseif string.find(text, "(.+)%[(%d+)%] кричит%: (.+)") then
                                                                    local space_0, id = string.match (text, "(.+)%[(%d+)%] кричит%: (.+)")
                                                                    local playerName = sampGetPlayerNickname(id)
                                                                    if playerName ~= myName then
                                                                        sampSendChat(string.format("/sellmeds %d %d", id, MedsPrice))
                                                                    end
                                                        elseif string.find(text, "%>%> (.+) от (.+)%((%d+)%)%: (.+)") then
                                                                    local space_0, space_1, id = string.match (text, "%>%> (.+) от (.+)%((%d+)%)%: (.+)")
                                                                    local playerName = sampGetPlayerNickname(id)
                                                                    if playerName ~= myName then
                                                                        sampSendChat(string.format("/sellmeds %d %d", id, MedsPrice))
                                                                    end
                                                        end
                                    end
                end
end