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

AntonAnton123

Активный
187
94
Как получить координаты спавна транспорта? Которые в /dl отображаются.
Не текущие координаты, а именно позиция спавна
бери от сюда функцию или переименуй на английский название и команду, тут в чат координаты пишет
 

Вложения

  • место спавна кара.lua
    1.1 KB · Просмотры: 5
  • Нравится
Реакции: kyurew

yorenov

Участник
44
37
Мне нужно ввести команду в чат, которая зарегистрирована другим скриптом/плагином. Пытался так:
lua:
sampRegisterChatCommand("test1", function()
    param = "/test 1_plane"
    sampAddChatMessage(param, -1)
    local bs = raknetNewBitStream()
    if param:sub(1, 1) == "/" then
        raknetBitStreamWriteInt32(bs, #param)
    else
        raknetBitStreamWriteInt8(bs, #param)
    end
    raknetBitStreamWriteString(bs, param)
    local rpcId = param:sub(1, 1) == "/" and 50 or 101
    raknetSendRpc(rpcId, bs)
    raknetDeleteBitStream(bs)
end)
Проблема: cmd которая зарегана другим плагином не пишет, ну то есть плагин который зарегал эту cmd не видит ввод (зарегана через сампапи, C++)
sampSendChat и sampProcessChatInput не воркают тоже, помогайте, луа сеньоры :D
 

meowprd

Тот самый Котовский
Проверенный
1,298
731
Мне нужно ввести команду в чат, которая зарегистрирована другим скриптом/плагином. Пытался так:
lua:
sampRegisterChatCommand("test1", function()
    param = "/test 1_plane"
    sampAddChatMessage(param, -1)
    local bs = raknetNewBitStream()
    if param:sub(1, 1) == "/" then
        raknetBitStreamWriteInt32(bs, #param)
    else
        raknetBitStreamWriteInt8(bs, #param)
    end
    raknetBitStreamWriteString(bs, param)
    local rpcId = param:sub(1, 1) == "/" and 50 or 101
    raknetSendRpc(rpcId, bs)
    raknetDeleteBitStream(bs)
end)
Проблема: cmd которая зарегана другим плагином не пишет, ну то есть плагин который зарегал эту cmd не видит ввод (зарегана через сампапи, C++)
sampSendChat и sampProcessChatInput не воркают тоже, помогайте, луа сеньоры :D
Нашел из древней заначки такой код.
Не помню, рабочий ли он. Поддерживает все версии 0.3.7
Lua:
local offsets = {
    SAMP_CHAT_INPUT_INFO_OFFSET = {
        [1] = 0x21a0e8,-- r1
        [2] = 0x21a0f0,
        [3] = 0x21a0e8,
        [4] = 0x26e8cc,
        [5] = 0x26e9fc,
        [6] = 0x26e9fc,
        [7] = 0x26EB84

    },
    pChatInput = {
        [1] = 0x8,
        [2] = 0x8,
        [3] = 0x8,
        [4] = 0x8,
        [5] = 0x8,
        [6] = 0x8,
        [7] = 0x8

    },
    CDXUTEditBox = {
        [1] = 0x80f60,
        [2] = 0x81000,
        [3] = 0x80f60,
        [4] = 0x84e70,
        [5] = 0x855b0,
        [6] = 0x0855e0,
        [7] = 0x85580

    },
    PROCESS_INPUT = {
        [1] = 0x65d30, -- r1
        [2] = 0x65e00, -- r2
        [3] = 0x65d30, -- 0.3dl
        [4] = 0x69260, -- r3
        [5] = 0x69990, -- r4
        [6] = 0x699d0, --r42
        [7] = 0x699D0 -- r5
    }
}

function getSampVersion()
    local version = 0
    local samp = getModuleHandle('samp.dll') + 0x128
    local samp1 = readMemory(samp, 4, true)

    if samp1 == 0x5542F47A then
        version = 1
    end
    if samp1 == 0x59C30C94 then
        version = 2
    end
    if samp1 == 0x5A6A3130 then
        version = 3
    end

    samp = samp - 8
    samp1 = readMemory(samp, 4, true)

    if samp1 == 0x5C0B4243 then
        version = 4
    end
    if samp1 == 0x5DD606CD then
        version = 5
    end
    if samp1 == 0x6094ACAB then
        version = 6
    end
    if samp1 == 0x6372C39E then
        version = 7
    end

    return version
end

function sampProcessChatInput(text)
    local samp_dll = getModuleHandle('samp.dll')
    local version = getSampVersion()
    local offset_SAMP_CHAT_INPUT_INFO_OFFSET = offsets.SAMP_CHAT_INPUT_INFO_OFFSET[version]
    local offset_pchat = offsets.pChatInput[version]
    local offset_peditbox = offsets.CDXUTEditBox[version]
    local offset_processinput = offsets.PROCESS_INPUT[version]
    local SAMP_CHAT_INPUT_INFO_OFFSET = samp_dll + offset_SAMP_CHAT_INPUT_INFO_OFFSET

    local pChatInput = readMemory(SAMP_CHAT_INPUT_INFO_OFFSET, 4, false)
    local pEditBox0 = pChatInput + offset_pchat
    local pEditBox = readMemory(pEditBox0, 4, false)
    local CDXUTEditBox = samp_dll + offset_peditbox
    local func = ffi.cast("void(__thiscall *)(void *, void*,  char *)", CDXUTEditBox)
    if func ~= 0 then
        local TextPointer = ffi.cast("void*", pEditBox)
        local EditBox = ffi.cast('void*', CDXUTEditBox)
        local c_str = ffi.new("char[?]", #text + 1)
        ffi.copy(c_str, text)
        func(TextPointer, c_str, EditBox)
    end
    local PROCESS_INPUT = samp_dll + offset_processinput
    local funcc = ffi.cast('void(__fastcall *)(void *)', PROCESS_INPUT)
    if funcc ~= 0 then
        local text_ptr = ffi.cast("void*", pChatInput)
        funcc(text_ptr)
    end
end
 

ARMOR

Я будто попал в другое измерение
Модератор
5,020
7,105
Нашел из древней заначки такой код.
Не помню, рабочий ли он. Поддерживает все версии 0.3.7
Lua:
local offsets = {
    SAMP_CHAT_INPUT_INFO_OFFSET = {
        [1] = 0x21a0e8,-- r1
        [2] = 0x21a0f0,
        [3] = 0x21a0e8,
        [4] = 0x26e8cc,
        [5] = 0x26e9fc,
        [6] = 0x26e9fc,
        [7] = 0x26EB84

    },
    pChatInput = {
        [1] = 0x8,
        [2] = 0x8,
        [3] = 0x8,
        [4] = 0x8,
        [5] = 0x8,
        [6] = 0x8,
        [7] = 0x8

    },
    CDXUTEditBox = {
        [1] = 0x80f60,
        [2] = 0x81000,
        [3] = 0x80f60,
        [4] = 0x84e70,
        [5] = 0x855b0,
        [6] = 0x0855e0,
        [7] = 0x85580

    },
    PROCESS_INPUT = {
        [1] = 0x65d30, -- r1
        [2] = 0x65e00, -- r2
        [3] = 0x65d30, -- 0.3dl
        [4] = 0x69260, -- r3
        [5] = 0x69990, -- r4
        [6] = 0x699d0, --r42
        [7] = 0x699D0 -- r5
    }
}

function getSampVersion()
    local version = 0
    local samp = getModuleHandle('samp.dll') + 0x128
    local samp1 = readMemory(samp, 4, true)

    if samp1 == 0x5542F47A then
        version = 1
    end
    if samp1 == 0x59C30C94 then
        version = 2
    end
    if samp1 == 0x5A6A3130 then
        version = 3
    end

    samp = samp - 8
    samp1 = readMemory(samp, 4, true)

    if samp1 == 0x5C0B4243 then
        version = 4
    end
    if samp1 == 0x5DD606CD then
        version = 5
    end
    if samp1 == 0x6094ACAB then
        version = 6
    end
    if samp1 == 0x6372C39E then
        version = 7
    end

    return version
end

function sampProcessChatInput(text)
    local samp_dll = getModuleHandle('samp.dll')
    local version = getSampVersion()
    local offset_SAMP_CHAT_INPUT_INFO_OFFSET = offsets.SAMP_CHAT_INPUT_INFO_OFFSET[version]
    local offset_pchat = offsets.pChatInput[version]
    local offset_peditbox = offsets.CDXUTEditBox[version]
    local offset_processinput = offsets.PROCESS_INPUT[version]
    local SAMP_CHAT_INPUT_INFO_OFFSET = samp_dll + offset_SAMP_CHAT_INPUT_INFO_OFFSET

    local pChatInput = readMemory(SAMP_CHAT_INPUT_INFO_OFFSET, 4, false)
    local pEditBox0 = pChatInput + offset_pchat
    local pEditBox = readMemory(pEditBox0, 4, false)
    local CDXUTEditBox = samp_dll + offset_peditbox
    local func = ffi.cast("void(__thiscall *)(void *, void*,  char *)", CDXUTEditBox)
    if func ~= 0 then
        local TextPointer = ffi.cast("void*", pEditBox)
        local EditBox = ffi.cast('void*', CDXUTEditBox)
        local c_str = ffi.new("char[?]", #text + 1)
        ffi.copy(c_str, text)
        func(TextPointer, c_str, EditBox)
    end
    local PROCESS_INPUT = samp_dll + offset_processinput
    local funcc = ffi.cast('void(__fastcall *)(void *)', PROCESS_INPUT)
    if funcc ~= 0 then
        local text_ptr = ffi.cast("void*", pChatInput)
        funcc(text_ptr)
    end
end
После перезапуска скрипта вызовёт краш игры при использовании команды
 
  • Грустно
Реакции: meowprd

yorenov

Участник
44
37
Нашел из древней заначки такой код.
Не помню, рабочий ли он. Поддерживает все версии 0.3.7
Lua:
local offsets = {
    SAMP_CHAT_INPUT_INFO_OFFSET = {
        [1] = 0x21a0e8,-- r1
        [2] = 0x21a0f0,
        [3] = 0x21a0e8,
        [4] = 0x26e8cc,
        [5] = 0x26e9fc,
        [6] = 0x26e9fc,
        [7] = 0x26EB84

    },
    pChatInput = {
        [1] = 0x8,
        [2] = 0x8,
        [3] = 0x8,
        [4] = 0x8,
        [5] = 0x8,
        [6] = 0x8,
        [7] = 0x8

    },
    CDXUTEditBox = {
        [1] = 0x80f60,
        [2] = 0x81000,
        [3] = 0x80f60,
        [4] = 0x84e70,
        [5] = 0x855b0,
        [6] = 0x0855e0,
        [7] = 0x85580

    },
    PROCESS_INPUT = {
        [1] = 0x65d30, -- r1
        [2] = 0x65e00, -- r2
        [3] = 0x65d30, -- 0.3dl
        [4] = 0x69260, -- r3
        [5] = 0x69990, -- r4
        [6] = 0x699d0, --r42
        [7] = 0x699D0 -- r5
    }
}

function getSampVersion()
    local version = 0
    local samp = getModuleHandle('samp.dll') + 0x128
    local samp1 = readMemory(samp, 4, true)

    if samp1 == 0x5542F47A then
        version = 1
    end
    if samp1 == 0x59C30C94 then
        version = 2
    end
    if samp1 == 0x5A6A3130 then
        version = 3
    end

    samp = samp - 8
    samp1 = readMemory(samp, 4, true)

    if samp1 == 0x5C0B4243 then
        version = 4
    end
    if samp1 == 0x5DD606CD then
        version = 5
    end
    if samp1 == 0x6094ACAB then
        version = 6
    end
    if samp1 == 0x6372C39E then
        version = 7
    end

    return version
end

function sampProcessChatInput(text)
    local samp_dll = getModuleHandle('samp.dll')
    local version = getSampVersion()
    local offset_SAMP_CHAT_INPUT_INFO_OFFSET = offsets.SAMP_CHAT_INPUT_INFO_OFFSET[version]
    local offset_pchat = offsets.pChatInput[version]
    local offset_peditbox = offsets.CDXUTEditBox[version]
    local offset_processinput = offsets.PROCESS_INPUT[version]
    local SAMP_CHAT_INPUT_INFO_OFFSET = samp_dll + offset_SAMP_CHAT_INPUT_INFO_OFFSET

    local pChatInput = readMemory(SAMP_CHAT_INPUT_INFO_OFFSET, 4, false)
    local pEditBox0 = pChatInput + offset_pchat
    local pEditBox = readMemory(pEditBox0, 4, false)
    local CDXUTEditBox = samp_dll + offset_peditbox
    local func = ffi.cast("void(__thiscall *)(void *, void*,  char *)", CDXUTEditBox)
    if func ~= 0 then
        local TextPointer = ffi.cast("void*", pEditBox)
        local EditBox = ffi.cast('void*', CDXUTEditBox)
        local c_str = ffi.new("char[?]", #text + 1)
        ffi.copy(c_str, text)
        func(TextPointer, c_str, EditBox)
    end
    local PROCESS_INPUT = samp_dll + offset_processinput
    local funcc = ffi.cast('void(__fastcall *)(void *)', PROCESS_INPUT)
    if funcc ~= 0 then
        local text_ptr = ffi.cast("void*", pChatInput)
        funcc(text_ptr)
    end
end
крашит при любом раскладе, radmir (037 r3)
version выдает 4, флудится пкд функа
getSampVersion(), хотя вызываю не в потоке
 
  • Грустно
Реакции: meowprd

Taymor_Smith

Участник
31
3
Добрый вечер всем,
  • Нужно луа/клео которое будет писать автоматически вводить в чат цифры,которые будут появлятся внизу экрана.
  • Данные цифры будут менятся.
 

Taymor_Smith

Участник
31
3
Последнее редактирование:

asdassss

Новичок
2
0
Ребят привет, срочно нужно как в lua скрипте включить и выключить по команде или клавише?
 

ZaeJ

Участник
35
1
Добрый день, подскажите код для луа, с помощью которого при нажатии клавиши луашник будет сам открывать чат и писать текст(за определенное кд)
т.е. Чтоб на фрапсе было видно, что я пишу текст от руки, а не биндером
 

Jefferson.!2.

Новичок
22
1
Как отключить курсор в диалоге?

sampToggleCursor — Мышь снова появляется в центре экрана
showCursor — Ничего не произошло
 

ChаtGPT

Активный
404
97
Когда игрок наступает на ганг-зону, она немного стает прозрачнее, как-бы подствечивает, что игрок находится на территории.
Как можно запретить эту "подстветку", чтобы при нахождении на ганг-зоне, она не изменяла прозрачность?
 
Последнее редактирование:

Naito

Активный
160
34
Does anyone help me?
On line 54 to 68 the code is all well structured, there is no error log but it rarely responds to the buttons, I press the button called "Heridas" it is supposed to call the
function mostrarHeridas()
function but it doesn't work, the same thing happens with the 2 buttons, they don't call the function. please help


code:
script_name('Heridas')
script_author('Naito')
script_version('1.7')

require 'moonloader'
require 'sampfuncs'
local sampev = require 'samp.events'

local heridas = {}
local contadorHeridas = {}
local contadorAimbot = {}
local contadorCabeza = {}
local tiempoUltimoImpacto = {}

local weapons = {
    [0] = 'Punos', [1] = 'Nudillo de hierro', [2] = 'Palo de golf', [3] = 'Porra policial',
    [4] = 'Cuchillo afilado', [5] = 'Bate de beisbol', [6] = 'Pala', [7] = 'Palo de billar',
    [8] = 'Katana', [9] = 'Motosierra', [10] = 'Dildo rosa', [11] = 'Dildo blanco',
    [12] = 'Vibrador blanco', [13] = 'Vibrador grande', [14] = 'Ramo de flores', [15] = 'Baston',
    [16] = 'Granada', [17] = 'Gas lagrimogeno', [18] = 'Molotov', [22] = 'Pistola 9mm',
    [23] = "'Taser-X19'", [24] = 'Desert Eagle', [25] = 'Escopeta', [26] = 'Escopeta recortada',
    [27] = 'Escopeta de combate', [28] = 'Micro Uzi', [29] = 'MP5', [30] = 'AK-47', [31] = 'M4',
    [32] = 'Tec-9', [33] = 'Rifle', [34] = 'Francotirador', [35] = 'Lanza cohetes',
    [36] = 'Dispositivo lanzacohetes', [37] = 'Lanzallamas', [38] = 'Minigun',
    [39] = 'Dinamita', [40] = 'Detonador', [41] = 'Lata de spray', [42] = 'Extintor de fuego',
    [43] = 'Camara', [44] = 'Lentes de vision nocturna', [45] = 'Lentes de vision termica',
    [46] = 'Mini-paracaidas', [49] = 'Choque de veh'
}

local partesCuerpo = {
    [3] = 'Torso', [4] = 'Cintura', [5] = 'Brazo izquierdo', [6] = 'Brazo derecho',
    [7] = 'Pierna izquierda', [8] = 'Pierna derecha', [9] = 'Cabeza'
}

local armasAimbot = {
    [24] = true, [25] = true, [27] = true, [23] = true, [33] = true, [34] = true
}

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

    sampAddChatMessage("{FFFFFF}[Heridas] Script cargado!", 0x00FF00)
    sampAddChatMessage("{FFFFFF}[Heridas] Autor: Naito", 0x00FF00)
    sampAddChatMessage("{FFFFFF}[Heridas] Comando: /.heridas", 0x00FF00)
    
    sampRegisterChatCommand(".heridas", mostrarOpciones)

    while true do
        wait(500)
        verificarTiempoImpactos()
    end
end

function mostrarOpciones()
    local miNombre = sampGetPlayerNickname(select(2, sampGetPlayerIdByCharHandle(playerPed)))
    sampShowDialog(32701, "{FFFFFF}Registro de {60BD7E}" .. miNombre,
        "{FFFFFF}Heridas\n{FFFFFF}Danos", "Ok", "Cancel", 2)
end

function sampev.onDialogResponse(dialogId, button, listbox, input)
    if dialogId == 32701 then
        if button == 1 then
            mostrarHeridas()
        elseif button == 2 then
            mostrarDanos()
        end
    end
end

function mostrarDanos()
    local miNombre = sampGetPlayerNickname(select(2, sampGetPlayerIdByCharHandle(playerPed)))
    
    if #heridas == 0 then
        sampShowDialog(32702, "{FFFFFF}Danos de {60BD7E}" .. miNombre, "{FFFFFF}No tienes daños registrados.", "Cerrar", "", 0)
        return
    end

    local maxDanos = 150
    local texto = ""
    local count = 0
    for i = #heridas, math.max(#heridas - maxDanos + 1, 1), -1 do
        local v = heridas[i]
        if v.parte == "Vehiculo" then
            local tiempoPasado = os.time() - v.tiempo
            local tiempoStr = calcularTiempo(tiempoPasado)
            texto = texto .. string.format("{F6DA9D}%-16s{C0C0C0} -%.2f en %-15s {EAAEA0}%s    {FFFFFF}hace %s\n",
                v.arma, v.dano, v.parte, v.jugador, tiempoStr)
            count = count + 1
            if count >= maxDanos then break end
        end
    end
    
    sampShowDialog(32702, "{FFFFFF}Danos de {60BD7E}" .. miNombre, texto, "Cerrar", "", 2)
end

function sampev.onSendTakeDamage(playerID, damage, weaponID, bodypart)
    if damage > 0 then
        local nickname = sampGetPlayerNickname(playerID)
        local weaponName = weapons[weaponID] or "Desconocido"
        local parteCuerpo = partesCuerpo[bodypart] or "Desconocida"
        local danoEntero = math.floor(damage)
        local tiempo = os.time()

        table.insert(heridas, {jugador = nickname, arma = weaponName, dano = danoEntero, parte = parteCuerpo, tiempo = tiempo})

        if not contadorHeridas[nickname] then
            contadorHeridas[nickname] = {}
        end

        if not contadorHeridas[nickname][parteCuerpo] then
            contadorHeridas[nickname][parteCuerpo] = 0
        end

        contadorHeridas[nickname][parteCuerpo] = contadorHeridas[nickname][parteCuerpo] + 1

        if contadorHeridas[nickname][parteCuerpo] > 10 then
            sampAddChatMessage(string.format(
                "Se ha detectado posible Aimbot en el Jugador: %s | Parte del cuerpo: %s", nickname, parteCuerpo), 0xFF0000)
            contadorHeridas[nickname][parteCuerpo] = 0
        end
    end

    if damage > 0 and armasAimbot[weaponID] then
        local nickname = sampGetPlayerNickname(playerID)

        if not contadorAimbot[nickname] then
            contadorAimbot[nickname] = 0
        end

        contadorAimbot[nickname] = contadorAimbot[nickname] + 1
        tiempoUltimoImpacto[nickname] = os.time()

        if contadorAimbot[nickname] >= 10 then
            sampAddChatMessage(string.format(
                "Se ha detectado posible Aimbot en %s por impactar 10 balas seguidas sin fallar.", nickname), 0xFF0000)
            contadorAimbot[nickname] = 0
        end
    end

    if bodypart == 9 then
        local nickname = sampGetPlayerNickname(playerID)

        if not contadorCabeza[nickname] then
            contadorCabeza[nickname] = 0
        end

        contadorCabeza[nickname] = contadorCabeza[nickname] + 1

        if contadorCabeza[nickname] >= 5 then
            sampAddChatMessage(string.format(
                "Posible Aimbot: Jugador %s sin fallos de Headshots (Cabeza).", nickname), 0xFFFF00)
            contadorCabeza[nickname] = 0
        end
    end
end

function verificarTiempoImpactos()
    local tiempoActual = os.time()

    for nickname, ultimoTiempo in pairs(tiempoUltimoImpacto) do
        if tiempoActual - ultimoTiempo >= 1.5 then
            contadorAimbot[nickname] = 0
            tiempoUltimoImpacto[nickname] = nil
        end
    end
end

function sampev.onSendVehicleDamage(vehicleID, damage)
    if damage > 0 then
        local miNombre = sampGetPlayerNickname(select(2, sampGetPlayerIdByCharHandle(playerPed)))
        local danoEntero = math.floor(damage)
        local tiempo = os.time()

        table.insert(heridas, {jugador = miNombre, arma = "Choque de veh", dano = danoEntero, parte = "Vehiculo", tiempo = tiempo})
    end
end

function mostrarHeridas()
    local miNombre = sampGetPlayerNickname(select(2, sampGetPlayerIdByCharHandle(playerPed)))
    
    if #heridas == 0 then
        sampShowDialog(32700, "{FFFFFF}Heridas de {60BD7E}" .. miNombre, "{FFFFFF}No tienes heridas registradas.", "Cerrar", "", 0)
        return
    end

    local maxHeridas = 150
    local texto = ""

    local count = 0
    for i = #heridas, math.max(#heridas - maxHeridas + 1, 1), -1 do
        local v = heridas[i]
        local tiempoPasado = os.time() - v.tiempo
        local tiempoStr = calcularTiempo(tiempoPasado)

        texto = texto .. string.format("{F6DA9D}%-16s{C0C0C0} -%.2f en %-15s {EAAEA0}%s    {FFFFFF}hace %s\n",
    v.arma, v.dano, v.parte, v.jugador, tiempoStr)

        count = count + 1
        if count >= maxHeridas then break end
    end

    sampShowDialog(32700, "{FFFFFF}Heridas de {60BD7E}" .. miNombre, texto, "Cerrar", "", 2)
end

function calcularTiempo(segundos)
    if segundos < 60 then
        return string.format("%d segs", segundos)
    elseif segundos < 3600 then
        return string.format("%d mins", math.floor(segundos / 60))
    elseif segundos < 86400 then
        return string.format("%d hs", math.floor(segundos / 3600))
    else
        return string.format("%d dias", math.floor(segundos / 86400))
    end
end