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

chromiusj

fullstack eblan
Модератор
5,800
4,099
И второй вопрос… как так получается, что у в текстдравах лавок Аризоны некоторые modelId одинаковые у разных предметов? Например, у бронзовой, золотой и платиновой рулетки в слоте текстдрава лежит моделька 1649. Более того! Сморим дальше… у Монеты Основания тоже 1649. Это как?
на модельку можно наложить цвет
посмотри тут какие данные можно получить
 
  • Нравится
Реакции: Ciske

veXOR

Новичок
2
0
поставь кодировку файла cp1251
1750910523065.png
1750910529397.png
 

chapo

tg/inst: @moujeek
Всефорумный модератор
9,130
12,257
Подскажите. Я правильно понимаю, что нет возможности легко получить список всех видимых текстдравов? Что их надо записывать в таблицу при появлении в событии onShowTextDraw?

И второй вопрос… как так получается, что у в текстдравах лавок Аризоны некоторые modelId одинаковые у разных предметов? Например, у бронзовой, золотой и платиновой рулетки в слоте текстдрава лежит моделька 1649. Более того! Сморим дальше… у Монеты Основания тоже 1649. Это как?
1649 выступает в роли плейсхолдера, там нужно смотреть на текст текстдрава, потому что эти модели рисуются как спрайты
 

abnomegd

Активный
347
37

При сканировании сообщения из /id скрываются из чата чтобы не засорять чат. Мне нужно чтобы при сканировании номеров и ников, информациия из /id анализировалась и сканировалась, сохранялась в конфиг прежде чем сообщение из /id будет скрыто, хоть это и моментально это возможно сделать?
вот сам код

code:
require "lib.moonloader"
local inicfg = require "inicfg"
local sampev = require 'lib.samp.events'
local samputils = require 'lib.sampfuncs'

local config_path = getWorkingDirectory() .. "\\config\\sms_config.ini"
if not doesDirectoryExist(getWorkingDirectory() .. "\\config") then
    createDirectory(getWorkingDirectory() .. "\\config")
end

local config = inicfg.load({ sms = { list = "", players = "" } }, config_path)

-- Переменные для сбора SMS
local existingNumbers = {}
local existingPlayers = {}
local collecting = false
local scanning = false

-- Таблица для отслеживания отправленных ID
local sentIds = {}

local function loadExistingData()
    existingNumbers = {}
    existingPlayers = {}
 
    if config.sms.list ~= nil and config.sms.list ~= "" then
        for num in string.gmatch(config.sms.list, "[^,]+") do
            existingNumbers[num] = true
        end
    end
 
    if config.sms.players ~= nil and config.sms.players ~= "" then
        for pair in string.gmatch(config.sms.players, "[^,]+") do
            local number, name = pair:match("([^=]+)=(.+)")
            if number and name then
                existingPlayers[number] = name
            end
        end
    end
end

local function saveData()
    local allNumbers = {}
    for num in pairs(existingNumbers) do
        table.insert(allNumbers, num)
    end
    config.sms.list = table.concat(allNumbers, ",")
 
    local allPlayers = {}
    for num, name in pairs(existingPlayers) do
        table.insert(allPlayers, num .. "=" .. name)
    end
    config.sms.players = table.concat(allPlayers, ",")
 
    inicfg.save(config, config_path)
end

local function addPlayer(number, name)
    if existingNumbers[number] then
        print("[SMS Collector] Номер уже в конфиге: " .. number)
        return false
    else
        existingNumbers[number] = true
        existingPlayers[number] = name
        saveData()
        print("[SMS Collector] Добавлен: " .. number .. " (" .. name .. ")")
        return true
    end
end

local function removeColorCodes(text)
    return text:gsub("{[%x]+}", "")
end

local function extractNickFromAd(text)
    local senderNick = text:match("Отправил%s+([%w_]+)")
    if senderNick then
        return senderNick
    end
 
    senderNick = text:match("от%s+([%w_]+)")
    if senderNick then
        return senderNick
    end
 
    local nick = text:match("^([%w_]+)")
    if nick then
        return nick
    end
 
    return "Объявление"
end

-- Функция для обработки ответов на /id
local function processAndAddPlayer(cleanText)
    -- Паттерны для разных форматов ответов
    local patterns = {
        {pattern = "(.+) id (%d+) %- тел%. (%d+)",
         extract = function(p, id, num) return p, num end},
        {pattern = "(.+) id (%d+) %- ren%. (%d+)",
         extract = function(p, id, num) return p, num end},
        {pattern = "(.+) id (%d+) %(.-%) %- тел%. (%d+)",
         extract = function(p, id, num) return p, num end},
        {pattern = "(.+) id (%d+) %(.-%) %- ren%. (%d+)",
         extract = function(p, id, num) return p, num end},
        {pattern = "(.+) id (%d+)",
         extract = function(p, id) return p, nil end}
    }
 
    for _, p in ipairs(patterns) do
        local playerName, id, number = cleanText:match(p.pattern)
        if playerName then
            playerName, number = p.extract(playerName, id, number)
            playerName = playerName:gsub("%s+$", ""):gsub("^%s+", "")
       
            if number then
                if addPlayer(number, playerName) then
                    print("[Сканирование] Добавлено: ID "..id.." | "..number.." | "..playerName)
                end
                return true
            else
                print("[Сканирование] Найден ID "..id..": "..playerName.." (номер не обнаружен)")
                return true
            end
        end
    end
 
    return false
end

-- Блокировка отображения команд /id в чате
function sampev.onSendCommand(cmd)
    if scanning then
        local id = cmd:match("^/id%s+(%d+)")
        if id then
            return false -- Блокируем вывод команды
        end
    end
end

-- Обработка серверных сообщений
function sampev.onServerMessage(color, text)
    local cleanText = removeColorCodes(text)
 
    if scanning then
        -- Обработка ответов на /id
        if processAndAddPlayer(cleanText) then
            return true -- Скрываем сообщение
        end
    end
 
    if collecting then
        -- Обработка объявлений
        local found = false
        for number in cleanText:gmatch("тел%.%s*(%d+)") or cleanText:gmatch("ren%.%s*(%d+)") do
            found = true
            local playerName = extractNickFromAd(cleanText)
            addPlayer(number, playerName)
        end
   
        if found then
            print("[SMS Collector] Найдено в объявлении: " .. cleanText)
        end
    end
end

-- Обработка сообщений чата
function sampev.onChatMessage(color, text, playerId)
    local cleanText = removeColorCodes(text)
 
    if scanning then
        -- Обработка ответов на /id
        if processAndAddPlayer(cleanText) then
            return false -- Скрываем сообщение
        end
    end
 
    if collecting then
        local playerName = sampGetPlayerNickname(playerId) or "Неизвестно"
        local found = false
   
        for number in cleanText:gmatch("тел%.%s*(%d+)") or cleanText:gmatch("ren%.%s*(%d+)") do
            found = true
            addPlayer(number, playerName)
        end
   
        if found then
            print("[SMS Collector] Найдено в чате: " .. cleanText)
        end
    end
 
    return true
end

-- Функция сканирования ID
local function scanIDs()
    if scanning then
        print("[SMS Collector] Сканирование уже запущено!")
        return
    end

    scanning = true
    sentIds = {}
 
    print("[SMS Collector] Начинаем сканирование ID 0-1000...")
    print("Все найденные номера будут мгновенно сохраняться")

    lua_thread.create(function()
        for i = 0, 1000 do
            if not scanning then break end
            if sampIsPlayerConnected(i) then
                table.insert(sentIds, i)
                sampSendChat("/id " .. i)
                wait(100)
            end
        end
   
        -- Ждем завершения обработки
        wait(3000)
        scanning = false
        sampAddChatMessage("Сканирование завершено.", 0x00FF00)
        print("[SMS Collector] Сканирование завершено")
    end)
end

function main()
    while not isSampAvailable() do wait(100) end

    loadExistingData()
    sampAddChatMessage("SMS Collector Pro v21 загружен. Команды:", 0x00FFAA)
    sampAddChatMessage("/collectsms - начать сбор", 0x00FFAA)
    sampAddChatMessage("/collectstop - остановить сбор", 0x00FFAA)

    sampRegisterChatCommand("collectsms", function()
        if collecting then
            sampAddChatMessage("Сбор уже активен.", 0xFFFF00)
            return
        end
        collecting = true
        sampAddChatMessage("Сбор номеров и ников активирован!", 0x00FF00)
        print("[SMS Collector] Запущено сканирование ID")
        scanIDs()
    end)

    sampRegisterChatCommand("collectstop", function()
        if not collecting then
            sampAddChatMessage("Сбор не активен.", 0xFFFF00)
            return
        end
        collecting = false
        scanning = false
        sampAddChatMessage("Сбор остановлен.", 0xFF9900)
        print("[SMS Collector] Сканирование остановлено")
    end)

    wait(-1)
end
up
 

Naito

Активный
165
36
I'm trying to create a PC Camera type for MonetLoader, but I'm having a problem. It works perfectly, but the camera jerks and also won't let me move it manually. Is there a way to fix this? Please.


code:
local ffi = require("ffi")
local SAMemory = require "SAMemory"
local gta = ffi.load("GTASA")
local camera = SAMemory.camera
local screenWidth, screenHeight = getScreenResolution()
SAMemory.require("CCamera")
ffi.cdef [[ typedef struct RwV3d{float x,y,z;}RwV3d;void _ZN4CPed15GetBonePositionER5RwV3djb(void* thiz,RwV3d* posn,uint32_t bone,bool calledFromCam);]]

local lockedCameraH = 0
local lockedCameraV = 0
local isLocked = false

function isPlayerMoving()
    local speed = getCharSpeed(PLAYER_PED)
    return speed > 0.02
end

function cameraLockSystem()
    if not doesCharExist(PLAYER_PED) then return end
    
    local playerMoving = isPlayerMoving()
    
    if playerMoving then
        if not isLocked then
            lockedCameraH = camera.aCams[0].fHorizontalAngle
            lockedCameraV = camera.aCams[0].fVerticalAngle
            isLocked = true
        end
        
        camera.aCams[0].fHorizontalAngle = lockedCameraH
        camera.aCams[0].fVerticalAngle = lockedCameraV
        
    else
        isLocked = false
    end
end

function main()
    while true do
        wait(0)
        
        if isSampAvailable() then
            cameraLockSystem()
        end
    end
end
 

dmitry.karle

Известный
367
102
I'm trying to create a PC Camera type for MonetLoader, but I'm having a problem. It works perfectly, but the camera jerks and also won't let me move it manually. Is there a way to fix this? Please.


code:
local ffi = require("ffi")
local SAMemory = require "SAMemory"
local gta = ffi.load("GTASA")
local camera = SAMemory.camera
local screenWidth, screenHeight = getScreenResolution()
SAMemory.require("CCamera")
ffi.cdef [[ typedef struct RwV3d{float x,y,z;}RwV3d;void _ZN4CPed15GetBonePositionER5RwV3djb(void* thiz,RwV3d* posn,uint32_t bone,bool calledFromCam);]]

local lockedCameraH = 0
local lockedCameraV = 0
local isLocked = false

function isPlayerMoving()
    local speed = getCharSpeed(PLAYER_PED)
    return speed > 0.02
end

function cameraLockSystem()
    if not doesCharExist(PLAYER_PED) then return end
  
    local playerMoving = isPlayerMoving()
  
    if playerMoving then
        if not isLocked then
            lockedCameraH = camera.aCams[0].fHorizontalAngle
            lockedCameraV = camera.aCams[0].fVerticalAngle
            isLocked = true
        end
      
        camera.aCams[0].fHorizontalAngle = lockedCameraH
        camera.aCams[0].fVerticalAngle = lockedCameraV
      
    else
        isLocked = false
    end
end

function main()
    while true do
        wait(0)
      
        if isSampAvailable() then
            cameraLockSystem()
        end
    end
end
Lua:
local ffi = require("ffi")
local SAMemory = require "SAMemory"
local gta = ffi.load("GTASA")
local camera = SAMemory.camera
local screenWidth, screenHeight = getScreenResolution()
SAMemory.require("CCamera")

local lockedCameraH = 0
local lockedCameraV = 0
local isLocked = false
local lastMovingState = false
local manualControlTimeout = 0

function isPlayerMoving()
    local speed = getCharSpeed(PLAYER_PED)
    return speed > 0.02
end

function cameraLockSystem()
    if not doesCharExist(PLAYER_PED) then return end
    local playerMoving = isPlayerMoving()
    if playerMoving ~= lastMovingState then -- состояние движения изменилось
        if playerMoving then
            lockedCameraH = camera.aCams[0].fHorizontalAngle
            lockedCameraV = camera.aCams[0].fVerticalAngle
            isLocked = true
        else
            isLocked = false
            manualControlTimeout = 1000
        end
        lastMovingState = playerMoving
    end
    if playerMoving and isLocked then -- движение и камера заблокирована
        local currentH = camera.aCams[0].fHorizontalAngle
        local currentV = camera.aCams[0].fVerticalAngle
        camera.aCams[0].fHorizontalAngle = currentH + (lockedCameraH - currentH) * 0.1
        camera.aCams[0].fVerticalAngle = currentV + (lockedCameraV - currentV) * 0.1
        if manualControlTimeout <= 0 then -- повернуть камеру вручную
            local hDiff = math.abs(camera.aCams[0].fHorizontalAngle - lockedCameraH)
            local vDiff = math.abs(camera.aCams[0].fVerticalAngle - lockedCameraV)
            if hDiff > 0.1 or vDiff > 0.1 then
                lockedCameraH = camera.aCams[0].fHorizontalAngle
                lockedCameraV = camera.aCams[0].fVerticalAngle
            end
        end
    end
    if manualControlTimeout > 0 then
        manualControlTimeout = manualControlTimeout - getFrameTime()
    end
end

function main()
    while true do
        wait(0)
        if isSampAvailable() then
            cameraLockSystem()
        end
    end
end
check, the problem with the jittery camera and the inability to manually control it is caused by the script constantly overwriting the camera angles without considering manual movement.