- Версия SA-MP
-
- 0.3.7-R3
AutoRespawn
Автоматический перезаход для АФК фармил
Версия: 1 | Автор: Qabeee
Автоматический перезаход для АФК фармил
Версия: 1 | Автор: Qabeee
Описание
Небольшой скрипт для перезахода после того как вас нокнули на притоне / разорвало соединение
После получения сообщения от сервера о завершении лечения скрипт запускает обратный отсчет в 10 секунд, выполняет /rec, выбирает заранее указанную точку спавна, а после появления персонажа открывает инвентарь и вызывает охранника.
Всё работает автоматически, без необходимости каждый раз выполнять действия вручную.
Возможности
- Автоматический перезаход после завершения лечения.
- Выбор нужной точки спавна
- Автоматический спавн охранника после появления персонажа
- Ручной перезаход с помощью команды /arec
- Отображение текущего состояния скрипта
- Настраиваемая горячая клавиша открытия меню.
1. Открыть меню /aurec или F12.
2. Выбрать необходимую точку спавна.
3. Нажать «Включить авто релог».
Готово. Теперь в случаи, если вас убьют после окончания лечения вы перезайдете на притон
Примечание: F12 - стандартная клавиша открытия меню. При желании её можно изменить непосредственно в интерфейсе.
Команды
/aurec — открыть/закрыть меню.
/arec — выполнить ручной перезаход.
Зависимости
Установка
1. Установить необходимые библиотеки.
2. Переместить файл AutoRespawn.lua в папку moonloader.
3. Запустить игру и открыть меню командой /aurec.
Настройки сохраняются автоматически.
Скриншот интерфейса
Открытый код: скрипт распространяется в формате .lua без обфускации. Любой желающий может ознакомиться с исходником и при необходимости изменить его под себя.
Код:
script_name('AutoRespawn')
script_version('1')
script_author('Qabeee')
local acef = require('arizona-events')
local sampev = require('lib.samp.events')
local imgui = require('mimgui')
local inicfg = require('inicfg')
local encoding = require('encoding')
local ffi = require('ffi')
encoding.default = 'UTF-8'
local cyr = encoding.CP1251
local defaults = {
main = {
enabled = false,
selected = '',
pending = 0,
cycle_selected = '',
hotkey = 123
},
points = {count = 0}
}
local cfg = inicfg.load(defaults, 'Q123')
cfg.main = cfg.main or {}
cfg.points = cfg.points or {}
local function diagnostic(s)
print('[AutoRespawn] ' .. tostring(s))
end
local function errorMessage(s)
diagnostic(s)
if isSampAvailable() then
sampAddChatMessage(cyr('[AutoRespawn] ' .. s), 0xFF6666)
end
end
local function save()
local ok, result = pcall(inicfg.save, cfg, 'Q123')
if not ok or result == false then
diagnostic('Settings save error: ' .. tostring(result))
return false
end
return true
end
local settings = {
enabled = cfg.main.enabled == true
or tostring(cfg.main.enabled) == 'true',
selected = tostring(cfg.main.selected or ''),
hotkey = tonumber(cfg.main.hotkey) or 123,
points = {}
}
local count = tonumber(cfg.points.count) or 0
for i = 1, math.min(count, 100) do
local name = cfg.points['p' .. i]
if type(name) == 'string' and name ~= '' then
settings.points[#settings.points + 1] = name
end
end
local function saveSettings()
cfg.main.enabled = settings.enabled
cfg.main.selected = settings.selected
cfg.main.hotkey = settings.hotkey
cfg.points = {count = #settings.points}
for i, name in ipairs(settings.points) do
cfg.points['p' .. i] = name
end
return save()
end
local oldPending = tonumber(cfg.main.pending) or 0
local pending = oldPending > 0
and oldPending <= os.time() + 5
and os.time() - oldPending <= 180
local restored = pending
local cycleSelected = ''
if pending then
cycleSelected = tostring(cfg.main.cycle_selected or '')
if cycleSelected == '' then
cycleSelected = settings.selected
end
else
cfg.main.pending = 0
cfg.main.cycle_selected = ''
save()
end
local window = imgui.new.bool(false)
local search = imgui.new.char[128]()
local uiError = ''
local state = 'idle'
local enteredAt = 0
local actionAt = 0
local deadlineAt = 0
local captureKey = false
local captureArmed = false
local previousHotkeyDown = false
local keyCodes = {}
local keyNames = {}
for n = 65, 90 do keyCodes[#keyCodes + 1] = n; keyNames[n] = string.char(n) end
for n = 48, 57 do keyCodes[#keyCodes + 1] = n; keyNames[n] = string.char(n) end
for n = 112, 123 do keyCodes[#keyCodes + 1] = n; keyNames[n] = 'F' .. (n - 111) end
for n = 96, 105 do keyCodes[#keyCodes + 1] = n; keyNames[n] = 'NUM ' .. (n - 96) end
keyCodes[#keyCodes + 1] = 32; keyNames[32] = 'SPACE'
if not keyNames[settings.hotkey] then settings.hotkey = 123 end
local function anyBindKeyDown()
for _, vk in ipairs(keyCodes) do
if isKeyDown(vk) then return true end
end
return false
end
local function statusText()
if state == 'hospital_wait' then return 'Лечение завершено' end
if state == 'wait_reconnect' then return 'Ожидание появления' end
if state == 'settle' then return 'Подготовка инвентаря' end
if state == 'inventory_request' or state == 'inventory_wait' then return 'Открытие инвентаря' end
if state == 'security_tab' or state == 'security_wait' or state == 'select_guard'
or state == 'select_wait' or state == 'menu_wait' or state == 'choose_menu'
or state == 'confirm_wait' or state == 'confirm_guard' then return 'Вызов охранника' end
if state == 'close_inventory' or state == 'close_inventory_wait' or state == 'finished' then
return 'Завершение цикла'
end
if settings.enabled then return 'Ожидание завершения лечения' end
return 'Авто релог отключен'
end
local spawnAt
local syncCount = 0
local firstSyncAt
local lastSyncAt
local inventoryCount = 0
local inventoryOpened = false
local guardMenuAction
local guardMenuIndex
local hospitalAt
local hospitalCount = 0
local lastTreatment = -30000
local spawnSelectedThisLogin = false
local lastSpawnSignature
local font
local function go(nextState, delay, timeout)
state = nextState
enteredAt = os.time()
actionAt = enteredAt + (delay or 0)
deadlineAt = timeout and enteredAt + timeout or 0
diagnostic('STATE ' .. nextState)
end
local function resetConnectionSignals()
spawnAt = nil
syncCount = 0
firstSyncAt = nil
lastSyncAt = nil
end
local function halt(reason)
pending = false
cycleSelected = ''
cfg.main.pending = 0
cfg.main.cycle_selected = ''
save()
inventoryCount = 0
inventoryOpened = false
guardMenuAction = nil
guardMenuIndex = nil
go('idle')
diagnostic(reason)
end
local function sendCEF(payload)
local ok, err = pcall(
acef.send,
'onArizonaSend',
{text = payload, server_id = 0}
)
if not ok then
errorMessage('Ошибка CEF: ' .. tostring(err))
halt('CEF request failed')
end
return ok
end
local function issueRec()
if pending or state ~= 'idle' then
diagnostic('Cycle already running: ' .. state)
return
end
local selected = settings.enabled and settings.selected or ''
cfg.main.pending = os.time()
cfg.main.cycle_selected = selected
if not save() then
errorMessage('Ошибка сохранения состояния.')
cfg.main.pending = 0
cfg.main.cycle_selected = ''
return
end
pending = true
cycleSelected = selected
inventoryCount = 0
inventoryOpened = false
guardMenuAction = nil
guardMenuIndex = nil
spawnSelectedThisLogin = false
lastSpawnSignature = nil
resetConnectionSignals()
go('wait_reconnect', 0, 45)
diagnostic('Executing /rec')
local ok, err = pcall(sampProcessChatInput, '/rec')
if not ok then
errorMessage('Ошибка /rec: ' .. tostring(err))
halt('Reconnect failed')
end
end
local function inventory()
if not pending or state ~= 'inventory_request' then return end
inventoryCount = inventoryCount + 1
if inventoryCount > 1 then
errorMessage('Инвентарь не открылся.')
halt('Inventory attempt limit')
return
end
go('inventory_wait', 0, 12)
local ok, err = pcall(sampSendChat, '/invent')
if not ok then
errorMessage('Ошибка /invent: ' .. tostring(err))
halt('Inventory failed')
end
end
local function sendSpawn(id)
local s = 'authSpawn|' .. tostring(id)
local bs = raknetNewBitStream()
raknetBitStreamWriteInt8(bs, 220)
raknetBitStreamWriteInt8(bs, 18)
raknetBitStreamWriteInt32(bs, #s)
raknetBitStreamWriteString(bs, s)
raknetBitStreamWriteInt32(bs, 0)
raknetSendBitStreamEx(bs, 1, 7, 1)
raknetDeleteBitStream(bs)
end
local function activeSpawnName()
if pending then return cycleSelected end
if settings.enabled then return settings.selected end
return ''
end
local function processSpawnPoints(points)
if type(points) ~= 'table' then return false end
local names = {}
local selectedPoint
local wanted = activeSpawnName()
for _, p in ipairs(points) do
if type(p) == 'table' and type(p.spawn) == 'string' then
local name = cyr:decode(p.spawn)
names[#names + 1] = name
if wanted ~= '' and name == wanted and p.id ~= nil then
selectedPoint = p
end
end
end
if #names == 0 then
diagnostic('No spawn points received')
return false
end
settings.points = names
if not saveSettings() then
diagnostic('Spawn list saving failed')
end
diagnostic('Spawn points: ' .. #names)
if wanted == '' or spawnSelectedThisLogin then
return true
end
if not selectedPoint then
diagnostic('Selected spawn unavailable: ' .. wanted)
return true
end
local ok, err = pcall(sendSpawn, selectedPoint.id)
if ok then
spawnSelectedThisLogin = true
diagnostic('Selected spawn: ' .. wanted)
else
diagnostic('Spawn selection error: ' .. tostring(err))
end
return true
end
local function bitStreamStructure(bs)
local text = ''
for i = 1, raknetBitStreamGetNumberOfBytesUsed(bs) do
local byte = raknetBitStreamReadInt8(bs)
if byte >= 32 and byte <= 255 and byte ~= 37 then
text = text .. string.char(byte)
end
end
raknetBitStreamResetReadPointer(bs)
return text
end
local function handleSpawnPacket(id, bs)
if id ~= 239 then return end
local ok, err = pcall(function()
local text = bitStreamStructure(bs)
if not text:find(
'event.auth.initializeSpawnPoints', 1, true
) then
return
end
local event, data = text:match(
"window%.executeEvent%('([%w%.]+)',%s*'(.+)'%)"
)
if event ~= 'event.auth.initializeSpawnPoints' then
diagnostic('Spawn event mismatch')
return
end
if data == lastSpawnSignature then return end
local decodedOk, decoded = pcall(decodeJson, data)
if not decodedOk or type(decoded) ~= 'table' then
diagnostic('Spawn JSON decoding failed')
return
end
if processSpawnPoints(decoded[1]) then
lastSpawnSignature = data
end
end)
if not ok then
diagnostic('Spawn packet error: ' .. tostring(err))
end
end
addEventHandler('onReceivePacket', handleSpawnPacket)
local function dialogTextUtf8(value)
if type(value) ~= 'string' then return '' end
local ok, result = pcall(function()
return cyr:decode(value)
end)
if ok and type(result) == 'string' then return result end
return value
end
local function recognizeGuardMenu(body)
local plain = dialogTextUtf8(body)
:gsub('{%x%x%x%x%x%x}', '')
local index = 0
local spawnIndex
local activeGuard = false
for line in (plain .. '\n'):gmatch('(.-)\n') do
line = line:gsub('\r', '')
if line:find('Заспавнить рядом с собой', 1, true) then
spawnIndex = index
end
if line:find('Спрятать', 1, true)
or line:find('Отозвать', 1, true)
or line:find('Убрать охранника', 1, true)
then
activeGuard = true
end
index = index + 1
end
if activeGuard then return 'already_spawned' end
if spawnIndex ~= nil then return 'spawn', spawnIndex end
return 'unknown'
end
function sampev.onShowDialog(id, style, title, b1, b2, body)
local titleText = dialogTextUtf8(title)
if titleText:find('Выбор места спавна', 1, true) then
local parsed = {}
local plain = dialogTextUtf8(body)
for line in plain:gmatch('[^\r\n]+') do
local name = line:match('%[%d+%] %{ffffff%}%s*(.+)')
if name then
if name:find('Сохраненная точка', 1, true) then
name = 'Сохраненная точка'
end
parsed[#parsed + 1] = name
else
parsed[#parsed + 1] = ''
end
end
if #parsed > 0 then
settings.points = parsed
saveSettings()
end
local wanted = activeSpawnName()
if wanted ~= '' and not spawnSelectedThisLogin then
for index, name in ipairs(parsed) do
if name == wanted then
local ok, err = pcall(
sampSendDialogResponse,
id, 1, index - 1, ''
)
if ok then
spawnSelectedThisLogin = true
diagnostic('Dialog spawn selected: ' .. wanted)
return false
end
diagnostic('Dialog response error: ' .. tostring(err))
break
end
end
end
end
if not pending then return end
if state == 'menu_wait' and id == 25749 then
guardMenuAction, guardMenuIndex = recognizeGuardMenu(body)
diagnostic(
'Guard: ' .. guardMenuAction
.. ' index=' .. tostring(guardMenuIndex)
)
go('choose_menu', 1, 8)
elseif state == 'confirm_wait' and id == 0 then
go('confirm_guard', 1, 8)
end
end
local treatmentPhrases = {
'Вы закончили свое лечение',
'Вы закончили своё лечение',
'Вы закончили лечение'
}
function sampev.onServerMessage(color, message)
if type(message) ~= 'string' then return end
local s = dialogTextUtf8(
message:gsub('{%x%x%x%x%x%x}', '')
)
if pending and state == 'inventory_wait' then
if s:find(
'Нельзя так быстро открывать инвентарь', 1, true
) then
go('inventory_rejected', 0, 0)
diagnostic('Inventory cooldown received')
return
end
end
if not settings.enabled or pending or state ~= 'idle' then
return
end
if settings.selected == '' then return end
local t = getGameTimer()
if t - lastTreatment < 20000 then return end
for _, phrase in ipairs(treatmentPhrases) do
if s:find(phrase, 1, true) then
lastTreatment = t
hospitalAt = os.time() + 10
hospitalCount = 10
go('hospital_wait')
diagnostic('Treatment finished; countdown started')
return
end
end
end
function sampev.onPlayerSpawn()
if pending and state == 'wait_reconnect' then
spawnAt = os.time()
diagnostic('OBSERVED onPlayerSpawn')
end
end
function sampev.onSendPlayerSync(data)
if not pending or state ~= 'wait_reconnect' then return end
local now = os.time()
if lastSyncAt ~= now then
syncCount = syncCount + 1
if not firstSyncAt then firstSyncAt = now end
lastSyncAt = now
end
end
function sampev.onInitGame()
spawnSelectedThisLogin = false
lastSpawnSignature = nil
resetConnectionSignals()
diagnostic('OBSERVED onInitGame')
end
function acef.onArizonaDisplay(packet)
if not pending or type(packet) ~= 'table' then return end
local s = type(packet.text) == 'string'
and packet.text
or (
type(packet[1]) == 'table'
and packet[1].text
or nil
)
if type(s) ~= 'string' then return end
local visible = s:find(
'event.inventory.setPlayerInventoryVisible', 1, true
) and s:find('true', 1, true)
if visible and (
state == 'wait_reconnect'
or state == 'settle'
or state == 'inventory_request'
or state == 'inventory_wait'
or state == 'inventory_rejected'
) then
inventoryOpened = true
go('security_tab', 1, 10)
diagnostic('ACK inventory visible')
elseif inventoryOpened and (
(
s:find('event.inventory.setPlayerInventoryVisible', 1, true)
and s:find('false', 1, true)
)
or (
s:find('event.setActiveView', 1, true)
and s:find('null', 1, true)
)
) then
inventoryOpened = false
diagnostic('ACK inventory closed')
elseif state == 'security_wait'
and s:find('event.inventory.updateCharacterTab', 1, true)
and s:find('security', 1, true)
then
go('select_guard', 1, 10)
end
end
local function disableHospital()
local previous = settings.enabled
settings.enabled = false
if not saveSettings() then
settings.enabled = previous
uiError = 'Ошибка сохранения настроек.'
errorMessage(uiError)
return
end
if state == 'hospital_wait' then
hospitalAt = nil
hospitalCount = 0
go('idle')
end
uiError = ''
diagnostic('Automation disabled')
end
local function enableHospital()
if settings.selected == '' then
uiError = 'Ошибка! Не выбрано место спавна.'
errorMessage(uiError)
return
end
local found = false
for _, name in ipairs(settings.points) do
if name == settings.selected then
found = true
break
end
end
if not found then
uiError = 'Ошибка! Выбранная точка отсутствует в списке.'
errorMessage(uiError)
return
end
settings.enabled = true
if not saveSettings() then
settings.enabled = false
uiError = 'Ошибка сохранения настроек.'
errorMessage(uiError)
return
end
uiError = ''
diagnostic('Automation enabled: ' .. settings.selected)
end
local function searchKey(value)
local ok, cp = pcall(function()
return cyr:encode(tostring(value or ''))
end)
if not ok or type(cp) ~= 'string' then
return tostring(value or ''):lower()
end
local result = {}
for i = 1, #cp do
local b = cp:byte(i)
if (b >= 65 and b <= 90)
or (b >= 192 and b <= 223)
then
b = b + 32
elseif b == 168 then
b = 184
end
result[i] = string.char(b)
end
return table.concat(result)
end
local function matchesSearch(name, query)
if query == '' then return true end
return searchKey(name):find(searchKey(query), 1, true) ~= nil
end
local function cleanCheck(cx, cy)
local draw = imgui.GetWindowDrawList()
local color = imgui.GetColorU32(imgui.Col.Text)
draw:AddLine(imgui.ImVec2(cx - 7, cy), imgui.ImVec2(cx - 2, cy + 5), color, 2.5)
draw:AddLine(imgui.ImVec2(cx - 2, cy + 5), imgui.ImVec2(cx + 8, cy - 6), color, 2.5)
end
imgui.OnInitialize(function()
imgui.SwitchContext()
local st = imgui.GetStyle()
st.WindowPadding = imgui.ImVec2(18, 12)
st.FramePadding = imgui.ImVec2(9, 7)
st.ItemSpacing = imgui.ImVec2(7, 7)
st.ItemInnerSpacing = imgui.ImVec2(6, 5)
st.WindowTitleAlign = imgui.ImVec2(.5, .5)
st.WindowRounding = 7
st.ChildRounding = 5
st.FrameRounding = 5
st.ScrollbarSize = 9
st.WindowBorderSize = 1
st.ChildBorderSize = 0
st.Colors[imgui.Col.WindowBg] = imgui.ImVec4(.055, .055, .068, 1)
st.Colors[imgui.Col.ChildBg] = imgui.ImVec4(.065, .065, .079, 1)
st.Colors[imgui.Col.TitleBg] = imgui.ImVec4(.08, .08, .095, 1)
st.Colors[imgui.Col.TitleBgActive] = imgui.ImVec4(.08, .08, .095, 1)
st.Colors[imgui.Col.FrameBg] = imgui.ImVec4(.11, .11, .13, 1)
st.Colors[imgui.Col.Button] = imgui.ImVec4(.39, .23, .8, 1)
st.Colors[imgui.Col.ButtonHovered] = imgui.ImVec4(.46, .29, .9, 1)
st.Colors[imgui.Col.Header] = imgui.ImVec4(0, 0, 0, 0)
st.Colors[imgui.Col.HeaderHovered] = imgui.ImVec4(.14, .14, .17, 1)
st.Colors[imgui.Col.HeaderActive] = imgui.ImVec4(.14, .14, .17, 1)
st.Colors[imgui.Col.Border] = imgui.ImVec4(.22, .22, .26, 1)
imgui.GetIO().IniFilename = nil
end)
imgui.OnFrame(function()
return window[0]
end, function()
local sw, sh = getScreenResolution()
imgui.SetNextWindowPos(imgui.ImVec2(sw / 2, sh / 2), imgui.Cond.FirstUseEver, imgui.ImVec2(.5, .5))
imgui.SetNextWindowSize(imgui.ImVec2(830, 525), imgui.Cond.Always)
local flags = imgui.WindowFlags.NoResize + imgui.WindowFlags.NoCollapse
+ imgui.WindowFlags.NoScrollbar + imgui.WindowFlags.NoScrollWithMouse
imgui.Begin('AutoRespawn | v1', window, flags)
local allW = imgui.GetContentRegionAvail().x
local allH = imgui.GetContentRegionAvail().y
local gap = 16
local leftW = math.floor((allW - gap) * .49)
local rightW = allW - gap - leftW
local panelH = allH - 43
imgui.BeginChild('##left_panel', imgui.ImVec2(leftW, panelH), false,
imgui.WindowFlags.NoScrollbar)
local previousPadding = imgui.GetStyle().WindowPadding
imgui.GetStyle().WindowPadding = imgui.ImVec2(12, 10)
imgui.BeginChild('##selected_block', imgui.ImVec2(0, 88), true,
imgui.WindowFlags.NoScrollbar)
imgui.GetStyle().WindowPadding = previousPadding
imgui.SetCursorPosX(12)
imgui.TextDisabled('Выбранная точка спавна:')
imgui.Spacing()
imgui.SetCursorPosX(12)
if settings.selected == '' then
imgui.TextDisabled('Не выбрано')
else
imgui.PushTextWrapPos(imgui.GetCursorPosX() + leftW - 62)
imgui.Text(settings.selected)
imgui.PopTextWrapPos()
local a, b = imgui.GetItemRectMin(), imgui.GetItemRectMax()
cleanCheck(b.x + 16, (a.y + b.y) / 2)
end
imgui.EndChild()
imgui.Spacing()
local previousPadding = imgui.GetStyle().WindowPadding
imgui.GetStyle().WindowPadding = imgui.ImVec2(12, 8)
imgui.BeginChild('##help_area', imgui.ImVec2(0, 46), false,
imgui.WindowFlags.NoScrollbar)
imgui.GetStyle().WindowPadding = previousPadding
imgui.SetCursorPosX(12)
imgui.Text('Как работает скрипт')
imgui.SameLine(nil, 8)
imgui.PushStyleColor(imgui.Col.Button, imgui.ImVec4(.25, .19, .38, 1))
imgui.PushStyleColor(imgui.Col.ButtonHovered, imgui.ImVec4(.38, .27, .61, 1))
imgui.SmallButton('?##help')
imgui.PopStyleColor(2)
if imgui.IsItemHovered() then
imgui.BeginTooltip()
imgui.PushTextWrapPos(imgui.GetCursorPosX() + 350)
imgui.Text('Как работает скрипт')
imgui.Separator()
imgui.Text('После появления в больнице персонаж лечится. Для этого необходим аксессуар с регенерацией HP.')
imgui.Spacing()
imgui.Text('Когда сервер отправит в чат сообщение «Вы закончили свое лечение», начнётся отсчёт 10 секунд.')
imgui.Spacing()
imgui.Text('Затем выполняется /rec. После появления персонажа скрипт открывает инвентарь и вызывает охранника. Можно продолжать стоять АФК.')
imgui.Spacing()
imgui.Separator()
imgui.Text('ВАЖНО: выберите нужную точку спавна и включите авто релог.')
imgui.PopTextWrapPos()
imgui.EndTooltip()
end
imgui.EndChild()
imgui.SetCursorPosY(201)
imgui.SetCursorPosX(12)
imgui.TextDisabled('Состояние скрипта')
imgui.SetCursorPosX(12)
imgui.Text(statusText())
imgui.Spacing()
imgui.SetCursorPosX(12)
imgui.TextDisabled('Открытие меню: /aurec или')
imgui.SameLine(nil, 6)
if imgui.SmallButton((captureKey and 'Нажмите клавишу...' or keyNames[settings.hotkey]) .. '##bind') then
captureKey = true
captureArmed = false
previousHotkeyDown = true
end
if captureKey then
imgui.SetCursorPosX(12)
imgui.TextDisabled('ESC — отмена; только латиница, цифры и F-клавиши')
end
imgui.Spacing()
local controlsH = settings.enabled and 70 or 46
if uiError ~= '' then controlsH = controlsH + 38 end
local targetY = panelH - controlsH - 2
imgui.SetCursorPosY(math.max(imgui.GetCursorPosY() + 4, targetY))
local previousPadding = imgui.GetStyle().WindowPadding
imgui.GetStyle().WindowPadding = imgui.ImVec2(12, 8)
imgui.BeginChild('##control_block', imgui.ImVec2(0, controlsH), false,
imgui.WindowFlags.NoScrollbar)
imgui.GetStyle().WindowPadding = previousPadding
if uiError ~= '' then
imgui.PushTextWrapPos(0)
imgui.TextColored(imgui.ImVec4(1, .4, .4, 1), uiError)
imgui.PopTextWrapPos()
end
if settings.enabled then
imgui.PushStyleColor(imgui.Col.Button, imgui.ImVec4(.23, .23, .26, 1))
imgui.PushStyleColor(imgui.Col.ButtonHovered, imgui.ImVec4(.30, .30, .34, 1))
if imgui.Button('Отключить авто релог', imgui.ImVec2(-1, 38)) then
disableHospital()
end
imgui.PopStyleColor(2)
local p = imgui.GetCursorScreenPos()
cleanCheck(p.x + 8, p.y + 8)
imgui.SetCursorPosX(imgui.GetCursorPosX() + 23)
imgui.Text('Авто релог активен')
else
if imgui.Button('Включить авто релог', imgui.ImVec2(-1, 38)) then
enableHospital()
end
end
imgui.EndChild()
imgui.EndChild()
imgui.SameLine(nil, gap)
local previousPadding = imgui.GetStyle().WindowPadding
imgui.GetStyle().WindowPadding = imgui.ImVec2(12, 10)
imgui.BeginChild('##right_panel', imgui.ImVec2(rightW, panelH), true,
imgui.WindowFlags.NoScrollbar)
imgui.GetStyle().WindowPadding = previousPadding
imgui.Text('Доступные точки: ' .. tostring(#settings.points))
imgui.Separator()
imgui.Spacing()
imgui.SetNextItemWidth(-1)
if imgui.InputTextWithHint then
imgui.InputTextWithHint('##spawn_search', 'Поиск', search, 128)
else
imgui.InputText('##spawn_search', search, 128)
if ffi.string(search) == '' and not imgui.IsItemActive() then
local p = imgui.GetItemRectMin()
imgui.GetWindowDrawList():AddText(imgui.ImVec2(p.x + 9, p.y + 7),
imgui.GetColorU32(imgui.Col.TextDisabled), 'Поиск')
end
end
imgui.Spacing()
local query = ffi.string(search)
local matches = 0
imgui.BeginChild('##results', imgui.ImVec2(0, imgui.GetContentRegionAvail().y - 3), false)
for index, name in ipairs(settings.points) do
if type(name) == 'string' and name ~= '' and matchesSearch(name, query) then
matches = matches + 1
if imgui.Selectable('##point_' .. index, false, 0,
imgui.ImVec2(0, 32)) then
local old = settings.selected
settings.selected = name
uiError = ''
if not saveSettings() then
settings.selected = old
uiError = 'Ошибка сохранения настроек.'
end
end
local a = imgui.GetItemRectMin()
local b = imgui.GetItemRectMax()
local draw = imgui.GetWindowDrawList()
local midY = (a.y + b.y) * 0.5
local accent = imgui.GetColorU32(imgui.Col.Button)
local textColor = imgui.GetColorU32(imgui.Col.Text)
local textH = imgui.CalcTextSize(name).y
draw:AddRectFilled(
imgui.ImVec2(a.x + 8, midY - 7),
imgui.ImVec2(a.x + 11, midY + 7),
accent, 1.5
)
draw:AddText(
imgui.ImVec2(a.x + 23, midY - textH * 0.5),
textColor, name
)
if settings.selected == name then
cleanCheck(b.x - 17, midY)
end
end
end
if matches == 0 then imgui.TextDisabled('Ничего не найдено') end
imgui.EndChild()
imgui.EndChild()
imgui.Spacing()
imgui.Separator()
imgui.BeginChild('##footer', imgui.ImVec2(0, 24), false,
imgui.WindowFlags.NoScrollbar)
local credit = 'by Qabeee'
local tw = imgui.CalcTextSize(credit).x
imgui.SetCursorPosX((imgui.GetWindowWidth() - tw) / 2)
imgui.TextDisabled(credit)
imgui.EndChild()
imgui.End()
end)
function main()
while not isSampAvailable() do wait(100) end
sampRegisterChatCommand('arec', issueRec)
sampRegisterChatCommand('aurec', function()
window[0] = not window[0]
uiError = ''
end)
font = renderCreateFont('Arial', 13, 5)
if restored then
resetConnectionSignals()
go('wait_reconnect', 0, 45)
diagnostic('Restored cycle; waiting for spawn confirmation')
end
diagnostic(
'Version 1 loaded | points='
.. tostring(#settings.points)
.. ' | enabled=' .. tostring(settings.enabled)
)
while true do
wait(0)
local now = os.time()
if captureKey then
if not captureArmed then
if not anyBindKeyDown() and not isKeyDown(27) then captureArmed = true end
elseif isKeyDown(27) then
captureKey = false
else
for _, vk in ipairs(keyCodes) do
if isKeyDown(vk) then
local old = settings.hotkey
settings.hotkey = vk
if not saveSettings() then settings.hotkey = old end
previousHotkeyDown = true
captureKey = false
diagnostic('Menu hotkey: ' .. keyNames[settings.hotkey])
break
end
end
end
else
local down = isKeyDown(settings.hotkey)
if down and not previousHotkeyDown and not sampIsChatInputActive()
and not sampIsDialogActive()
and (type(isPauseMenuActive) ~= 'function' or not isPauseMenuActive()) then
window[0] = not window[0]
uiError = ''
end
previousHotkeyDown = down
end
if state == 'hospital_wait' then
if not settings.enabled then
hospitalAt = nil
hospitalCount = 0
go('idle')
elseif hospitalAt then
hospitalCount = math.max(0, hospitalAt - now)
if now >= hospitalAt then
hospitalAt = nil
hospitalCount = 0
go('idle')
issueRec()
end
end
elseif state == 'wait_reconnect' then
if spawnAt and now - spawnAt >= 8 then
go('settle', 3, 25)
elseif syncCount >= 3
and firstSyncAt
and now - firstSyncAt >= 8
then
go('settle', 3, 25)
elseif now - enteredAt >= 45 then
halt('Reconnect cancelled: no spawn/sync confirmation')
end
elseif state == 'settle' and now >= actionAt then
go('inventory_request', 1, 15)
elseif state == 'inventory_request'
and now >= actionAt
then
inventory()
elseif state == 'inventory_rejected' then
errorMessage('Инвентарь не открылся.')
halt('Inventory rejected')
elseif state == 'security_tab' and now >= actionAt then
go('security_wait', 0, 10)
sendCEF('requestShowingInventory|28')
elseif state == 'select_guard' and now >= actionAt then
go('select_wait', 1, 10)
sendCEF('selectSecurity|{"id":0}')
elseif state == 'select_wait' and now >= actionAt then
go('menu_wait', 0, 10)
sendCEF('clickOnMenu|{"id": 0}')
elseif state == 'choose_menu' and now >= actionAt then
if guardMenuAction == 'spawn'
and guardMenuIndex ~= nil
then
go('confirm_wait', 0, 10)
local ok, err = pcall(
sampSendDialogResponse,
25749,
1,
guardMenuIndex,
''
)
if not ok then
errorMessage('Ошибка меню: ' .. tostring(err))
halt('Guard menu error')
end
else
local ok, err = pcall(function()
if sampIsDialogActive()
and sampGetCurrentDialogId() == 25749
then
sampCloseCurrentDialogWithButton(0)
end
end)
if not ok then
errorMessage('Ошибка закрытия меню: ' .. tostring(err))
halt('Guard dialog close error')
elseif inventoryOpened then
go('close_inventory', 1, 6)
else
halt('Guard dialog cancelled')
end
end
elseif state == 'close_inventory'
and now >= actionAt
then
go('close_inventory_wait', 0, 6)
sendCEF('inventoryClose')
elseif state == 'close_inventory_wait'
and not inventoryOpened
then
halt('Inventory closed')
elseif state == 'confirm_guard'
and now >= actionAt
then
go('finished')
local ok, err = pcall(
sampSendDialogResponse,
0, 1, 0, ''
)
if not ok then
errorMessage('Ошибка подтверждения: ' .. tostring(err))
halt('Guard confirm error')
else
halt('Guard sequence completed')
end
end
if pending
and deadlineAt > 0
and now >= deadlineAt
and state ~= 'idle'
then
local failedState = state
if failedState == 'inventory_wait' then
errorMessage('Инвентарь не открылся.')
end
halt('Timeout: ' .. failedState)
end
if state == 'hospital_wait'
and hospitalCount > 0
and font
then
local w, h = getScreenResolution()
renderFontDrawText(
font,
cyr('Релог через... ' .. hospitalCount),
math.floor(w * 0.045),
math.floor(h * 0.60),
0xFFFFFFFF
)
end
end
end