script_name("GamepadMouseControl")
script_author("chatgpt")
local encoding = require 'encoding'
encoding.default = 'CP1251'
local u8 = encoding.UTF8
local ffi = require 'ffi'
local bit = require 'bit'
-- FFI-объявления для XInput и WinAPI
ffi.cdef[[
typedef unsigned long DWORD;
typedef unsigned short WORD;
typedef unsigned char BYTE;
typedef int BOOL;
typedef short SHORT;
typedef unsigned long ULONG_PTR;
typedef void* HWND;
typedef struct { long left; long top; long right; long bottom; } RECT;
typedef struct { long x; long y; } POINT;
typedef struct _XINPUT_GAMEPAD {
WORD wButtons;
BYTE bLeftTrigger;
BYTE bRightTrigger;
SHORT sThumbLX;
SHORT sThumbLY;
SHORT sThumbRX;
SHORT sThumbRY;
} XINPUT_GAMEPAD;
typedef struct _XINPUT_STATE {
DWORD dwPacketNumber;
XINPUT_GAMEPAD Gamepad;
} XINPUT_STATE;
DWORD XInputGetState(DWORD dwUserIndex, XINPUT_STATE *pState);
void XInputEnable(BOOL enable);
HWND FindWindowA(const char* lpClassName, const char* lpWindowName);
BOOL GetClientRect(HWND hWnd, RECT *lpRect);
BOOL ClientToScreen(HWND hWnd, POINT *lpPoint);
BOOL SetCursorPos(int X, int Y);
void mouse_event(DWORD dwFlags, DWORD dx, DWORD dy, DWORD dwData, ULONG_PTR dwExtraInfo);
BOOL ClipCursor(const RECT *lpRect);
]]
-- Загрузка библиотек
local user32 = ffi.load("user32.dll")
local xinput = ffi.load("xinput1_4.dll", true) or ffi.load("XInput9_1_0.dll", true)
xinput.XInputEnable(1)
-- Получение окна SA:MP
local hwnd = user32.FindWindowA(nil, "GTA:SA:MP") -- ← заголовок окна
local crect = ffi.new("RECT")
local screenRect = ffi.new("RECT")
-- Начальные координаты курсора
local curX, curY = 400, 300
-- Настройки чувствительности
local DEADZONE = 8000
local SPEED = 15
-- Состояние управления
local controlEnabled = false
local lastButtons = 0
-- Шрифт для текста на экране
local font = renderCreateFont("Tahoma", 14, 5)
-- Если окно найдено — ограничим курсор областью окна
if hwnd ~= nil then
user32.GetClientRect(hwnd, crect)
local pt = ffi.new("POINT", {0, 0})
user32.ClientToScreen(hwnd, pt)
screenRect.left = pt.x
screenRect.top = pt.y
screenRect.right = pt.x + crect.right
screenRect.bottom = pt.y + crect.bottom
user32.ClipCursor(screenRect)
-- Центр курсора
curX = crect.right / 2
curY = crect.bottom / 2
end
function main()
while not isSampAvailable() do wait(100) end
while true do
wait(0)
-- Получаем состояние геймпада
local state = ffi.new("XINPUT_STATE")
if xinput.XInputGetState(0, state) == 0 then
local gp = state.Gamepad
local btn = gp.wButtons
-- Переключение по кнопке Start (0x0010)
local startNow = bit.band(btn, 0x0010) ~= 0
local startBefore = bit.band(lastButtons, 0x0010) ~= 0
if startNow and not startBefore then
controlEnabled = not controlEnabled
local text1 = "{00FF00}[GamepadMouse]{FFFFFF} Управление: " .. (controlEnabled and "включено" or "выключено")
sampAddChatMessage(u8:decode(text1), -1)
wait(200)
end
lastButtons = btn
if controlEnabled then
-- Получение положения стика
local lx, ly = gp.sThumbLX, gp.sThumbLY
if math.abs(lx) < DEADZONE then lx = 0 end
if math.abs(ly) < DEADZONE then ly = 0 end
-- Преобразование в движение курсора
local dx = lx / 32767 * SPEED
local dy = ly / 32767 * SPEED
curX = curX + dx
curY = curY - dy -- инвертируем, т.к. вверх — уменьшение Y
-- Ограничиваем в пределах окна
if hwnd ~= nil then
local w, h = crect.right, crect.bottom
if curX < 0 then curX = 0 end
if curY < 0 then curY = 0 end
if curX > w then curX = w end
if curY > h then curY = h end
-- Устанавливаем курсор на экране
local pt = ffi.new("POINT", {curX, curY})
user32.ClientToScreen(hwnd, pt)
user32.SetCursorPos(pt.x, pt.y)
end
-- Кнопка A (0x1000) — ЛКМ
if bit.band(btn, 0x1000) ~= 0 then
user32.mouse_event(0x0002, 0, 0, 0, 0) -- LEFTDOWN
user32.mouse_event(0x0004, 0, 0, 0, 0) -- LEFTUP
wait(150)
end
-- Кнопка X (0x4000) — ПКМ
if bit.band(btn, 0x4000) ~= 0 then
user32.mouse_event(0x0008, 0, 0, 0, 0) -- RIGHTDOWN
user32.mouse_event(0x0010, 0, 0, 0, 0) -- RIGHTUP
wait(150)
end
-- Прокрутка вверх (LB — 0x0100)
if bit.band(btn, 0x0100) ~= 0 then
user32.mouse_event(0x0800, 0, 0, 120, 0)
wait(150)
end
-- Прокрутка вниз (RB — 0x0200)
if bit.band(btn, 0x0200) ~= 0 then
user32.mouse_event(0x0800, 0, 0, -120, 0)
wait(150)
end
end
end
end
end
-- Отображение текста на экране
function onD3DPresent()
local text2 = string.format("Gamepad мышь: %s", controlEnabled and "включено" or "выключено")
renderFontDrawText(font,
u8:decode(text2),
510, 500, controlEnabled and 0xFF00FF00 or 0xFFFF0000)
end
-- При завершении скрипта снимаем ограничение курсора
function onScriptTerminate(s, quit)
user32.ClipCursor(nil)
end