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

d1abol1c

Новичок
2
0
Здравствуйте. Делаю скрипт для админов. Есть AHK AdminTools. Этот АХК позволяет старшим администраторам наказывать игроков по просьбе хелперов. Как это сделать в lua? Ну уже все облазил,нет подобного

Вот адм-чат из chatlog. [A-3]{7FC7FF} Makoto_Nagai(0): /ban Makoto_Nagai 30 cheat
 

CatKnight

Известный
148
54
Здравствуйте. Делаю скрипт для админов. Есть AHK AdminTools. Этот АХК позволяет старшим администраторам наказывать игроков по просьбе хелперов. Как это сделать в lua? Ну уже все облазил,нет подобного

Вот адм-чат из chatlog. [A-3]{7FC7FF} Makoto_Nagai(0): /ban Makoto_Nagai 30 cheat
Ловишь ивентом onServerMessage(color, text) новое сообщение из чата, проверяешь его на совпадение, присваиваешь переменные, отправляешь когда нужно.
 
  • Нравится
Реакции: d1abol1c

WhackerH

Новичок
43
0
Как создать локальный диалог через луа?(желательно бы какую то темку)
 

Acerdragons

Известный
22
0
Собственно, что делать? Хочу чтобы при вводе /buseball активировалась команда, которую я ввел ниже.
Lua:
require "lib.moonloader" -- Основная библиотека Moonloader
require "lib.sampfuncs" -- Дополнительная библиотека для расширения функции

function main() -- Начало всех скриптов на MoonLoader
if not isSampLoaded() then return end
while not isSampAvailable() do wait (100) end
while true do wait (0)
sampRegisterChatCommand("buseball", function (cmd1) -- Регистрация команды для чата
wait (-1)

function cmd1 -- Функция для чата (если команда введена)
    if cmd1 == 1 then
        requestAnimation("FAT") -- Загружаем анимацию
        result = hasAnimationLoaded("FAT") -- Проверка на то что пак анимации "FAT" загружен
        if result then -- Если анимация загружена тогда
        taskPlayAnim(playerPed, "FATWALK", "FAT", 4.0, false, false, false, false, 1170) -- Воспроизводим анимацию.
    end
end
 

Acerdragons

Известный
22
0
Lua:
require "lib.moonloader" -- Основная библиотека Moonloader
require "lib.sampfuncs" -- Дополнительная библиотека для расширения функции

function main() -- Начало всех скриптов на MoonLoader
    if not isSampLoaded() then return end
    while not isSampAvailable() do wait (100) end
    sampRegisterChatCommand("buseball", cmd1) -- Регистрация команды для чата
    wait(-1)
end

function cmd1() -- Функция для чата (если команда введена)
    requestAnimation("FAT") -- Загружаем анимацию
    result = hasAnimationLoaded("FAT") -- Проверка на то что пак анимации "FAT" загружен
    if result then -- Если анимация загружена тогда
    taskPlayAnim(playerPed, "FATWALK", "FAT", 4.0, false, false, false, false, 1170) -- Воспроизводим анимацию.
end
moonloader.log нет, но в консольной строке:
[ML] (error) anim3.lua: ...s (x86)\GTA San Andreas MultiPlayer\moonloader\anim3.lua:17: 'end' expected (to close 'function' at line 11) near '<eof>'
[ML] (error) anim3.lua: Script died due to an error. (0EEE74CC)
 

WhackerH

Новичок
43
0
Lua:
sampShowDialog(dialogId, caption, text, button1, button2, style)
Окей, диалог создался, а как дальше с ним взаимодействовать? Меня интересует именно как при выборе первого пункта выводить текст допустим в консоль
mZ8jT56.png
 

imring

Ride the Lightning
Всефорумный модератор
2,355
2,516
Окей, диалог создался, а как дальше с ним взаимодействовать? Меня интересует именно как при выборе первого пункта выводить текст допустим в консоль
mZ8jT56.png
Заметил, что многие испытывают проблемы с созданием подменю на самповских диалогах и часто спрашивают об этом. Решил упростить жизнь бедолагам и заодно воспользоваться неплохой возможностью подчеркнуть ущербность клео и павн.

Описание: функция для создания бесконечно вложенных подменю на основе диалогов сампа при помощи таблиц

Достоинства
  • всё учтено: возможность повторного показа диалога после выбора пункта, возможность вешать обработчик на пункты с подменю, изменение текста кнопок, пункты с подменю выводятся со стрелками, изменение заголовка каждого диалога отдельно, кнопка "закрыть" меняется на "назад" в подменю
  • всё, что нужно описывать - это структуру диалога и действия. Никакой возни с описанием логики навигации
  • достаточно легко изменить под стандартные менюшки игры и под любой другой вид менюшек
  • меню можно формировать, изменять или дополнять в рантайме без дополнительных усилий
  • с самими диалогами и пунктами меню можно ассоциировать любые данные
  • всё это вмещается в одну единственную функцию длиной в 39 строк
  • можно показывать отбитым фанатам клео и павн, посмеиваясь, что у них такого никогда не будет
submenus.lua · GitHub (https://gist.github.com/THE-FYP/e89a10df29698219b56bb37bc194cb31)
Lua:
function submenus_show(menu, caption, select_button, close_button, back_button)
    select_button, close_button, back_button = select_button or 'Select', close_button or 'Close', back_button or 'Back'
    prev_menus = {}
    function display(menu, id, caption)
        local string_list = {}
        for i, v in ipairs(menu) do
            table.insert(string_list, type(v.submenu) == 'table' and v.title .. '  >>' or v.title)
        end
        sampShowDialog(id, caption, table.concat(string_list, '\n'), select_button, (#prev_menus > 0) and back_button or close_button, sf.DIALOG_STYLE_LIST)
        repeat
            wait(0)
            local result, button, list = sampHasDialogRespond(id)
            if result then
                if button == 1 and list ~= -1 then
                    local item = menu[list + 1]
                    if type(item.submenu) == 'table' then -- submenu
                        table.insert(prev_menus, {menu = menu, caption = caption})
                        if type(item.onclick) == 'function' then
                            item.onclick(menu, list + 1, item.submenu)
                        end
                        return display(item.submenu, id + 1, item.submenu.title and item.submenu.title or item.title)
                    elseif type(item.onclick) == 'function' then
                        local result = item.onclick(menu, list + 1)
                        if not result then return result end
                        return display(menu, id, caption)
                    end
                else -- if button == 0
                    if #prev_menus > 0 then
                        local prev_menu = prev_menus[#prev_menus]
                        prev_menus[#prev_menus] = nil
                        return display(prev_menu.menu, id - 1, prev_menu.caption)
                    end
                    return false
                end
            end
        until result
    end
    return display(menu, 31337, caption or menu.title)
end
Lua:
local my_dialog = {
    title = 'Заголовок основного диалога', -- необязательно
    -- пункты диалога. каждый пункт должен быть заключен в фигурные скобки
    {
        title = 'Пункт 1', -- текст пункта, обязателен
        onclick = function(menu, row) -- функция-обработчик при выборе пункта
            -- параметр menu - это (под)меню, в котором находится этот пункт
            -- row - номер пункта, начиная с единицы
        end
    },
    -- ещё один пункт, но с предотвращением автоматического закрытия диалога при его выборе
    {
        title = 'Пункт 2',
        onclick = function(menu, row)
            return true -- возвращение значения true из функции-обработчика предотвратит закрытие диалога после выбора пункта
        end
    },
    -- третий пункт, но уже с подменю
    {
        title = 'Пункт с подменю',
        -- подменю. имеет точно такую же структуру, как и основной диалог
        submenu = {
            title = 'Заголовок подменю', -- однако заголовок тут не обязателен. если он не задан будет использоваться текст пункта, открывающего это подменю
            -- каждый пункт подменю точно так же должен быть заключен в фигурные скобки
            {
                title = 'Пункт подменю 1'
                -- onclick не обязателен
            },
            {
                title = 'Пункт подменю 2',
                -- подменю в подменю. так можно до бесконечности
                submenu = {
                    title = 'Под-подменю',
                    onclick = function(menu, row, submenu) -- обработчик можно вешать и на открытие подменю, он выполнится перед показом
                        -- menu и row такие же, а submenu - это подменю, которое будет открыто
                        -- здесь бесполезно использовать return true
                    end,
                    -- наличие onclick для подменю не означает, что предопределенных пунктов в нём быть не может
                    {
                        title = 'Пункт #6141235'
                    }
                }
            },
            {
                title = 'Пункт подменю 3'
            }
        }
    }
}
Диалог с разными действиями без подменю. В создании диалогов без подменю с помощью этой функции смысла мало, т.к. код обычного диалога с несколькими пунктами получается проще, если делать его обычным путём.
Тут только структура диалога и пример вызова, действия можете прикрутить сами
Lua:
local sf = require 'sampfuncs'

local my_dialog = {
    {
        title = 'Вылечить игрока',
        onclick = function()
            -- действия
        end
    },
    {
        title = 'Выдать броню',
        onclick = function()
            -- ...
        end
    },
    {
        title = 'Выдать миниган',
        onclick = function()
            -- ...
        end
    },
    {
        title = 'Ещё-что нибудь',
        onclick = function()
            -- ...
        end
    },
    {
        title = 'Телепорт в рандомную точку',
        onclick = function()
            -- ...
        end
    }
}

function main()
    while true do
        wait(0)
        if wasKeyPressed(0x31) then
            submenus_show(my_dialog, 'Бесполезное меню')
        end
    end
end
Тоже только структура и пример вызова
Lua:
local sf = require 'sampfuncs'

local mod_submenus_sa = {
    {
        title = '{AAAAAA}GTA'
    },
    {
        title = 'Читы',
        submenu = {
            {
                title = 'Восстановить здоровье',
                onclick = function()
                    -- setCharHealth blabla
                end
            },
            {
                title = 'Восстановить броню',
                onclick = function()
                    -- ...
                end
            },
            {
                title = 'Починить транспорт',
                onclick = function()
                    -- ...
                end
            },
            {
                title = 'Деньги',
                submenu = {
                    {
                        title = '$500',
                        onclick = function()
                            -- ...
                        end
                    },
                    {
                        title = '$1 000',
                        onclick = function()
                            -- ...
                        end
                    },
                    {
                        title = '$100 000',
                        onclick = function()
                            -- ...
                        end
                    }
                }
            },
            {
                title = 'Бессмертие: выкл',
                onclick = function(menu, row)
                    godmode_active = not godmode_active
                    menu[row].title = godmode_active and 'Бессмертие: вкл' or 'Бессмертие: выкл'
                    -- ...
                    return true
                end
            }
        }
    },
    {
        title = 'Оружие',
        submenu = {
            {
                title = 'Выдать патроны',
                onclick = function()
                    -- ...
                end
            }
        }
    },
    {
        title = 'Транспорт',
        submenu = {
            -- ..
        }
    },
    {
        title = 'Телепорты',
        submenu = {
            -- ..
        }
    },
    {
        title = ' '
    },
    {
        title = '{AAAAAA}SA-MP'
    },
    {
        title = 'Игроки',
        submenu = {},
        onclick = function(menu, row, submenu)
            table.insert(submenu,{title='heuehue'})
            -- тут можно заполнить submenu списком игроков. например, сделать для каждого игрока подменю с какими-то действиями над ним
        end
    },
    {
        title = 'Патчи',
        submenu = {
            -- ...
        }
    }
}

function main()
    -- инициализация меню
    -- создание списка оружий
    local weaps = require 'game.weapons'
    for id = weaps.FIST, weaps.PARACHUTE do
        if weaps.names[id] then
            table.insert(mod_submenus_sa[3].submenu, {title = weaps.names[id], onclick = mod_menu_give_weapon})
        end
    end
    while true do
        wait(0)
        if testCheat('MSS') then
            submenus_show(mod_submenus_sa, 'mod_submenus_sa v1.3.3.7 by TurboRofl team', 'Выбрать', 'Закрыть', 'Назад')
        end
    end
end
 

Aniki

🐰
Администратор
1,225
1,506
оно, да, спасибо. Но еще вопрос, я привык использовать обычно в main() sampRegisterChatCommand и отдельно функцию делать, как это сделать на этом примере?
Вместо анонимной функции пишешь ее имя, а потом отдельно ее пишешь
Lua:
sampRegisterChatCommand('ban', ban)

function ban(args)
    if #args > 0 then
        pID, Reason = args:match('(%d+)%s+(.*)')
        print('ID: '..pID..' Reason: '..Reason)
    end
end
 

kamazingg

Известный
12
1
Ребят, при работе с onServerMessage столкнулся с такой проблемой:

Допустим я ушел в афк на час, набралось достаточно много сообщений, вышел из афк и onServerMessage начинает это всё обрабатывать. Как можно это дело подправить, чтобы код выполнялся только на те сообщения, которые были получены вне афк?
 
  • Нравится
Реакции: f0rtrix

ShuffleBoy

Известный
Друг
754
429
Ребят, при работе с onServerMessage столкнулся с такой проблемой:

Допустим я ушел в афк на час, набралось достаточно много сообщений, вышел из афк и onServerMessage начинает это всё обрабатывать. Как можно это дело подправить, чтобы код выполнялся только на те сообщения, которые были получены вне афк?
script_properties('work-in-pause') ?