- 23
- 15
- Версия SA-MP
-
- 0.3.7-R2
Всем привет!
Столкнулся с проблемой при запуске скрипта, который очень важен для меня. При загрузке он сразу вылетает, а в логе moonloader появляется следующая ошибка:
[ML] (error) init.lua: C:\Games\arizona gay\moonloader\init.lua:28: attempt to call field 'queue' (a nil value)
stack traceback:
C:\Games\arizona gay\moonloader\init.lua:28: in main chunk
[ML] (error) init.lua: Script died due to an error. (1D9E1D9C)
Я так понимаю, проблема в том, что у меня установлена устаревшая версия библиотеки effil, в которой еще нет необходимой для работы скрипта функции queue.
Огромная просьба, поделитесь, пожалуйста, ссылкой на актуальную и рабочую версию библиотеки effil.lua. Уже несколько часов ищу, но попадаются либо нерабочие, либо такие же старые версии.
Заранее огромное спасибо всем, кто откликнется!
Столкнулся с проблемой при запуске скрипта, который очень важен для меня. При загрузке он сразу вылетает, а в логе moonloader появляется следующая ошибка:
[ML] (error) init.lua: C:\Games\arizona gay\moonloader\init.lua:28: attempt to call field 'queue' (a nil value)
stack traceback:
C:\Games\arizona gay\moonloader\init.lua:28: in main chunk
[ML] (error) init.lua: Script died due to an error. (1D9E1D9C)
Я так понимаю, проблема в том, что у меня установлена устаревшая версия библиотеки effil, в которой еще нет необходимой для работы скрипта функции queue.
Огромная просьба, поделитесь, пожалуйста, ссылкой на актуальную и рабочую версию библиотеки effil.lua. Уже несколько часов ищу, но попадаются либо нерабочие, либо такие же старые версии.
Заранее огромное спасибо всем, кто откликнется!
lua:
script_name ("AAAAAAAAAAAAAAAAAAAAAA")
-- Улучшенная проверка наличия и версии библиотек
local mimgui_ok, imgui = pcall(require, 'mimgui')
local effil_ok, effil = pcall(require, 'effil')
if not mimgui_ok or not effil_ok then
print("~r~[TaimerPRO] КРИТИЧЕСКАЯ ОШИБКА:")
if not mimgui_ok then print("~r~ - Библиотека 'mimgui' не найдена. Скрипт не может работать без нее.") end
if not effil_ok then print("~r~ - Библиотека 'effil' не найдена. Скрипт не может работать без нее.") end
print("~y~[TaimerPRO] Пожалуйста, установите недостающие библиотеки и перезапустите скрипт.")
thisScript():unload()
return
end
if not effil.queue then
print("~r~[TaimerPRO] КРИТИЧЕСКАЯ ОШИБКА:")
print("~r~ - Ваша версия библиотеки 'effil' устарела и не поддерживает функцию 'queue'.")
print("~y~[TaimerPRO] Пожалуйста, обновите 'effil' до последней версии, чтобы скрипт мог работать корректно.")
print("~y~[TaimerPRO] Скачать можно с Blast.hk или других ресурсов MoonLoader.")
thisScript():unload()
return
end
-- Подключение остальных необходимых библиотек
local dkjson = require 'dkjson'
local encoding = require 'encoding'
encoding.default = 'CP1251'
local u8 = encoding.UTF8
local https = require 'ssl.https'
local sampev = require 'lib.samp.events'
local vkeys = require 'vkeys'
-- Встроенная библиотека Base64 для самодостаточности скрипта
local b64 = {}
do
local b = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
function b64.encode(data)
return ((data:gsub('.', function(x)
local r, b = '', x:byte()
for i = 8, 1, -1 do r = r .. (b % 2 ^ i - b % 2 ^ (i - 1) > 0 and '1' or '0') end
return r;
end) .. '0000'):gsub('%d%d%d%d%d%d', function(x)
if (#x < 6) then return '' end
local c = 0
for i = 1, 6 do c = c + (x:sub(i, i) == '1' and 2 ^ (6 - i) or 0) end
return b:sub(c + 1, c + 1)
end) .. ({ '', '==', '=' })[#data % 3 + 1])
end
function b64.decode(data)
data = string.gsub(data, '[^'..b..'=]', '')
return (data:gsub('.', function(x)
if (x == '=') then return '' end
local r, f = '', (b:find(x) - 1)
for i = 6, 1, -1 do r = r .. (f % 2 ^ i - f % 2 ^ (i - 1) > 0 and '1' or '0') end
return r;
end):gsub('%d%d%d%d%d%d%d%d', function(x)
if (#x < 8) then return '' end
local c = 0
for i = 1, 8 do c = c + (x:sub(i, i) == '1' and 2 ^ (8 - i) or 0) end
return string.char(c)
end))
end
end
-- Глобальные переменные и состояние
local main_window_state = imgui.new.bool(false)
local script_version = "1.2.1"
local config_path = getFolderPath(0x1A) .. '\\moonloader\\config\\TaimerPRO.json'
local state_path = getFolderPath(0x1A) .. '\\moonloader\\config\\TaimerPRO.state.json'
local W, H = getScreenResolution()
local message_queue = effil.queue()
local active_timers = {}
local last_telegram_update_id = 0
-- Переменные для UI
local inputs = {
profile_name = imgui.new.char[128](''),
hours = imgui.new.int(0),
minutes = imgui.new.int(5),
seconds = imgui.new.int(0),
message = imgui.new.char[256](''),
font_name = imgui.new.char[128]('Arial'),
font_size = imgui.new.int(20),
font_flags = imgui.new.int(5),
tg_token = imgui.new.char[256](''),
tg_chat_id = imgui.new.char[128]('')
}
local editing_profile_id = nil
local show_profile_editor = false
local customFont = nil
--- Вспомогательные функции для ImGui (идея из Flooder.lua) ---
function imgui.CenterText(text)
local width = imgui.GetWindowWidth()
local text_width = imgui.CalcTextSize(text).x
imgui.SetCursorPosX((width - text_width) / 2)
imgui.Text(text)
end
function imgui.Hint(text)
if imgui.IsItemHovered() then
imgui.SetTooltip(text)
end
end
-------------------------------------------------------------
-- Настройки по умолчанию
local defaultConfig = {
settings = {
theme = "Dark",
hud = {
mode = 1, -- 1: ImGui окно, 2: Нативный текст
imgui_pos = { x = 10, y = 200 },
native_pos = { x = W / 2, y = 10 },
font_name = "Arial",
font_size = 20,
font_flags = 5,
font_color = "FFFFFF",
show_shadow = true
},
hotkeysEnabled = true,
telegram = {
enabled = false,
allow_commands = false,
token_b64 = "",
chat_id = ""
}
},
profiles = {},
autostarts = {},
chains = {},
window = {
pos = { x = 500, y = 300 },
size = { x = 600, y = 400 }
}
}
local config = {}
-----------------------------------------------------
--[[ 1. ОСНОВА И УПРАВЛЕНИЕ КОНФИГУРАЦИЕЙ ⚙️ ]]--
-----------------------------------------------------
local function merge_tables(default, custom)
local result = {}
for k, v in pairs(default) do
if type(v) == 'table' and custom and custom[k] and type(custom[k]) == 'table' then
result[k] = merge_tables(v, custom[k])
elseif custom and custom[k] ~= nil then
result[k] = custom[k]
else
result[k] = v
end
end
return result
end
function saveAllSettings()
config.settings.hud.font_name = u8.decode(inputs.font_name)
config.settings.hud.font_size = inputs.font_size[0]
config.settings.hud.font_flags = inputs.font_flags[0]
config.settings.telegram.token_b64 = b64.encode(u8.decode(inputs.tg_token))
config.settings.telegram.chat_id = u8.decode(inputs.tg_chat_id)
local file = io.open(config_path, 'w')
if file then
file:write(dkjson.encode(config, { indent = true }))
file:close()
end
end
function fontUpdate()
local s = config.settings.hud
customFont = renderCreateFont(s.font_name, s.font_size, s.font_flags)
end
function load_settings_on_startup()
local file = io.open(config_path, 'r')
local loaded_config
if file then
local content = file:read('*a')
file:close()
pcall(function() loaded_config = dkjson.decode(content) end)
end
config = merge_tables(defaultConfig, loaded_config or {})
imgui.Strncpy(inputs.font_name, u8.encode(config.settings.hud.font_name), 128)
inputs.font_size[0] = config.settings.hud.font_size
inputs.font_flags[0] = config.settings.hud.font_flags
if config.settings.telegram.token_b64 and config.settings.telegram.token_b64 ~= "" then
pcall(function()
local decoded_token = b64.decode(config.settings.telegram.token_b64)
imgui.Strncpy(inputs.tg_token, decoded_token, 256)
end)
end
if config.settings.telegram.chat_id then
imgui.Strncpy(inputs.tg_chat_id, u8.encode(config.settings.telegram.chat_id), 128)
end
fontUpdate()
end
-----------------------------------------------------
--[[ 2. ЛОГИКА ТАЙМЕРОВ И МНОГОПОТОЧНОСТЬ ⏱️ ]]--
-----------------------------------------------------
function timer_thread(id, duration, message, profileName, settings)
local start_time = os.clock()
local time_slept = 0
local paused = false
local remaining_on_pause = duration
while time_slept < duration do
local command = message_queue:get(id, 0)
if command == 'stop' then
message_queue:push({ type = 'timer_stopped', id = id, silent = true })
return
elseif command == 'pause' then
paused = true
remaining_on_pause = duration - time_slept
elseif command == 'resume' then
paused = false
duration = remaining_on_pause
start_time = os.clock()
time_slept = 0
end
if not paused then
effil.sleep(100)
time_slept = os.clock() - start_time
else
effil.sleep(200)
end
end
message_queue:push({ type = 'samp', text = ('{00BFFF}[TaimerPRO]{FFFFFF} Таймер "%s" завершен!'):format(profileName) })
if message and message ~= "" then
message_queue:push({ type = 'samp', text = ('{00BFFF}Сообщение:{FFFFFF} %s'):format(message) })
end
if settings.telegram.enabled then
message_queue:push({ type = 'telegram_send', text = ('Таймер "%s" завершен!'):format(profileName) })
end
message_queue:push({ type = 'timer_finished', id = id })
end
function startNewTimer(profile)
-- Проверяем, не запущен ли уже таймер для этого профиля
for _, timer in pairs(active_timers) do
if timer.profile_id == profile.id then
sampAddChatMessage(("{00BFFF}[TaimerPRO]{FFFFFF} Таймер для профиля '%s' уже запущен."):format(profile.name), 0xFF0000)
return
end
end
local id = os.time() + math.random(1000)
local duration = (profile.h or 0) * 3600 + (profile.m or 0) * 60 + (profile.s or 0)
if duration <= 0 then
sampAddChatMessage('{00BFFF}[TaimerPRO]{FFFFFF} Ошибка: Длительность таймера должна быть больше нуля.', 0xFF0000)
return
end
local thread = effil.thread(timer_thread)(id, duration, profile.message, profile.name, config.settings)
active_timers[id] = {
thread = thread,
profile_id = profile.id,
name = profile.name,
start_time = os.time(),
duration = duration,
paused = false,
remaining_on_pause = duration
}
sampAddChatMessage(("{00BFFF}[TaimerPRO]{FFFFFF} Таймер '%s' запущен на %dч %dм %dс."):format(profile.name, profile.h, profile.m, profile.s), 0x2ECC71)
end
function stopTimer(id)
if active_timers[id] then
message_queue:push(id, 'stop')
end
end
function togglePause(id)
local timer = active_timers[id]
if not timer then return end
timer.paused = not timer.paused
if timer.paused then
message_queue:push(id, 'pause')
local elapsed = os.time() - timer.start_time
timer.remaining_on_pause = timer.duration - elapsed
else
message_queue:push(id, 'resume')
timer.start_time = os.time()
timer.duration = timer.remaining_on_pause
end
end
function save_timers_state()
local state_to_save = {}
for id, timer in pairs(active_timers) do
local remaining
if timer.paused then
remaining = timer.remaining_on_pause
else
remaining = timer.duration - (os.time() - timer.start_time)
end
if remaining > 0 then
table.insert(state_to_save, {
profile_id = timer.profile_id,
remaining = remaining
})
end
end
if #state_to_save > 0 then
local file = io.open(state_path, 'w')
if file then
file:write(dkjson.encode(state_to_save))
file:close()
end
end
end
function resume_persistent_timers()
local file = io.open(state_path, 'r')
if not file then return end
local content = file:read('*a')
file:close()
pcall(os.remove, state_path)
local resumed_timers
pcall(function() resumed_timers = dkjson.decode(content) end)
if resumed_timers and type(resumed_timers) == 'table' then
for _, t in ipairs(resumed_timers) do
local profile = config.profiles[t.profile_id]
if profile then
local restored_profile = {
id = profile.id,
name = profile.name,
h = math.floor(t.remaining / 3600),
m = math.floor((t.remaining % 3600) / 60),
s = t.remaining % 60,
message = profile.message
}
startNewTimer(restored_profile)
end
end
end
end
---------------------------------------------------
--[[ 3. СИСТЕМА ПРОФИЛЕЙ И ЦЕПОЧЕК 📂 ]]--
---------------------------------------------------
function openProfileEditor(id)
if id then
local profile = config.profiles[id]
if profile then
editing_profile_id = id
imgui.Strncpy(inputs.profile_name, u8.encode(profile.name), 128)
inputs.hours[0] = profile.h or 0
inputs.minutes[0] = profile.m or 0
inputs.seconds[0] = profile.s or 0
imgui.Strncpy(inputs.message, u8.encode(profile.message or ""), 256)
end
else
editing_profile_id = nil
inputs.profile_name:fill(0)
inputs.hours[0], inputs.minutes[0], inputs.seconds[0] = 0, 5, 0
inputs.message:fill(0)
end
show_profile_editor = true
end
function saveOrUpdateProfile()
local name = u8.decode(inputs.profile_name)
if name == "" then return end
local id = editing_profile_id or tostring(os.time() + math.random(1000))
config.profiles[id] = {
id = id,
name = name,
h = inputs.hours[0],
m = inputs.minutes[0],
s = inputs.seconds[0],
message = u8.decode(inputs.message)
}
saveAllSettings()
show_profile_editor = false
end
function deleteProfile(id)
if not id then return end
config.profiles[id] = nil
saveAllSettings()
show_profile_editor = false
end
---------------------------------------------------
--[[ 4. ГРАФИЧЕСКИЙ ИНТЕРФЕЙС (GUI) 🎨 ]]--
---------------------------------------------------
local ThemeManager = {
themes = {
Dark = function() imgui.SwitchContext() imgui.StyleColorsDark() end,
Light = function() imgui.SwitchContext() imgui.StyleColorsLight() end,
Classic = function() imgui.SwitchContext() imgui.StyleColorsClassic() end,
},
apply = function(themeName)
if ThemeManager.themes[themeName] then
ThemeManager.themes[themeName]()
end
end
}
function render_native_hud()
if not customFont or next(active_timers) == nil then return end
local y_offset = config.settings.hud.native_pos.y
local x_pos = config.settings.hud.native_pos.x
local color = tonumber("0xFF" .. config.settings.hud.font_color)
for id, timer in pairs(active_timers) do
local remaining
if timer.paused then
remaining = timer.remaining_on_pause
else
remaining = timer.duration - (os.time() - timer.start_time)
end
if remaining < 0 then remaining = 0 end
local time_str = string.format("%02d:%02d:%02d", math.floor(remaining / 3600), math.floor((remaining % 3600) / 60), remaining % 60)
local text = u8.encode(("%s: %s %s"):format(timer.name, time_str, timer.paused and "[P]" or ""))
local text_w = renderGetFontDrawTextWidth(customFont, text, false)
local x = x_pos - (text_w / 2)
if config.settings.hud.show_shadow then
renderFontDrawText(customFont, text, x + 1, y_offset + 1, 0xFF000000)
end
renderFontDrawText(customFont, text, x, y_offset, color)
y_offset = y_offset + config.settings.hud.font_size
end
end
function render_imgui_hud()
local s = config.settings.hud
imgui.SetNextWindowPos(imgui.ImVec2(s.imgui_pos.x, s.imgui_pos.y), imgui.Cond.FirstUseEver)
imgui.SetNextWindowSize(imgui.ImVec2(250, 0), imgui.Cond.Always)
imgui.SetNextWindowBgAlpha(0.5)
if imgui.Begin('Active Timers HUD', nil, imgui.WindowFlags.NoTitleBar + imgui.WindowFlags.NoCollapse) then
s.imgui_pos.x, s.imgui_pos.y = imgui.GetWindowPos()
for id, timer in pairs(active_timers) do
local remaining
if timer.paused then
remaining = timer.remaining_on_pause
else
remaining = timer.duration - (os.time() - timer.start_time)
end
if remaining < 0 then remaining = 0 end
local time_str = string.format("%02d:%02d:%02d", math.floor(remaining / 3600), math.floor((remaining % 3600) / 60), remaining % 60)
local elapsed = timer.duration - remaining
imgui.Text(u8.encode(timer.name))
imgui.SameLine()
imgui.Text(time_str)
imgui.ProgressBar(elapsed / timer.duration, imgui.ImVec2(-1, 0), timer.paused and "PAUSED" or "")
end
imgui.End()
end
end
function render_profile_editor_popup()
if show_profile_editor then
imgui.OpenPopup('Редактор профиля')
end
imgui.SetNextWindowSize(imgui.ImVec2(400, 350), imgui.Cond.FirstUseEver)
if imgui.BeginPopupModal('Редактор профиля', true, imgui.WindowFlags.NoResize) then
imgui.Text("Название:")
imgui.InputText('##ProfileName', inputs.profile_name, 128)
imgui.Separator()
imgui.Text("Длительность (Часы:Минуты:Секунды):")
imgui.PushItemWidth(80)
imgui.InputInt('##h', inputs.hours); imgui.SameLine()
imgui.InputInt('##m', inputs.minutes); imgui.SameLine()
imgui.InputInt('##s', inputs.seconds)
imgui.PopItemWidth()
imgui.Separator()
imgui.Text("Сообщение по окончании (необязательно):")
imgui.InputTextMultiline('##Message', inputs.message, 256, imgui.ImVec2(-1, 80))
imgui.Separator()
if imgui.Button('Сохранить', imgui.ImVec2(120, 0)) then saveOrUpdateProfile() end
imgui.SameLine()
if editing_profile_id and imgui.Button('Удалить', imgui.ImVec2(120, 0)) then deleteProfile(editing_profile_id) end
imgui.SameLine(imgui.GetWindowWidth() - 80)
if imgui.Button('Закрыть') then
show_profile_editor = false
imgui.CloseCurrentPopup()
end
imgui.EndPopup()
end
end
imgui.OnFrame(
function() return main_window_state[0] end,
function(self)
ThemeManager.apply(config.settings.theme)
imgui.SetNextWindowPos(imgui.ImVec2(config.window.pos.x, config.window.pos.y), imgui.Cond.FirstUseEver)
imgui.SetNextWindowSize(imgui.ImVec2(config.window.size.x, config.window.size.y), imgui.Cond.FirstUseEver)
if imgui.Begin('TaimerPRO v1.2.1 by Arizona RP Хакер', main_window_state) then
config.window.pos.x, config.window.pos.y = imgui.GetWindowPos()
config.window.size.x, config.window.size.y = imgui.GetWindowSize()
if imgui.BeginTabBar("MainTabBar") then
if imgui.BeginTabItem('Запуск') then
if imgui.Button('Создать новый профиль') then openProfileEditor() end
imgui.Separator()
imgui.Columns(2, "ProfileColumns", false)
imgui.SetColumnWidth(0, 250)
imgui.Text("Профиль"); imgui.NextColumn()
imgui.Text("Действия"); imgui.NextColumn()
imgui.Separator()
for id, profile in pairs(config.profiles) do
imgui.Text(u8.encode(profile.name)); imgui.NextColumn()
local active_id = nil
for a_id, active in pairs(active_timers) do
if active.profile_id == id then active_id = a_id; break; end
end
if active_id then
if imgui.Button('Стоп##'..id) then stopTimer(active_id) end
else
if imgui.Button('Старт##'..id) then startNewTimer(profile) end
end
imgui.SameLine()
if imgui.Button('Ред.##'..id) then openProfileEditor(id) end
imgui.NextColumn()
end
imgui.Columns(1)
imgui.EndTabItem()
end
if imgui.BeginTabItem('Настройки') then
imgui.Text("Настройки HUD"); imgui.Separator()
if imgui.RadioButton("Режим: Окно ImGui", config.settings.hud.mode == 1) then config.settings.hud.mode = 1 end
imgui.Hint("Классический HUD в виде полупрозрачного окна. Можно перетаскивать.")
if imgui.RadioButton("Режим: Нативный текст", config.settings.hud.mode == 2) then config.settings.hud.mode = 2 end
imgui.Hint("Минималистичный HUD в виде простого текста на экране.")
if config.settings.hud.mode == 2 then
imgui.Separator()
imgui.Text("Настройки нативного текста:")
imgui.InputText("Шрифт##fontname", inputs.font_name, 128)
imgui.SliderInt("Размер##fontsize", inputs.font_size, 8, 72)
imgui.InputInt("Стиль (флаги)##fontflags", inputs.font_flags)
imgui.Hint("0-обычный, 1-жирный, 2-наклон, 4-подчеркнутый, 8-перечеркнутый. Можно суммировать (напр. 1+4=5 для жирного+подчеркнутого)")
if imgui.Checkbox("Тень у текста", imgui.new.bool(config.settings.hud.show_shadow)) then
config.settings.hud.show_shadow = not config.settings.hud.show_shadow
end
if imgui.Button("Применить шрифт") then fontUpdate(); saveAllSettings() end
end
imgui.Separator()
imgui.Text("Интеграция с Telegram"); imgui.Separator()
if imgui.Checkbox("Включить уведомления", imgui.new.bool(config.settings.telegram.enabled)) then
config.settings.telegram.enabled = not config.settings.telegram.enabled
end
if imgui.Checkbox("Разрешить команды из Telegram", imgui.new.bool(config.settings.telegram.allow_commands)) then
config.settings.telegram.allow_commands = not config.settings.telegram.allow_commands
end
imgui.Text("Токен бота:"); imgui.InputText("##TGToken", inputs.tg_token, 256)
imgui.Text("Chat ID:"); imgui.InputText("##TGChatID", inputs.tg_chat_id, 128)
if imgui.Button("Сохранить все настройки") then saveAllSettings() end
imgui.EndTabItem()
end
imgui.EndTabBar()
end
render_profile_editor_popup()
imgui.End()
end
end
)
---------------------------------------------------
--[[ 5. ДОПОЛНИТЕЛЬНЫЕ МОДУЛИ И ФУНКЦИИ ⭐ ]]--
---------------------------------------------------
function sendTelegramNotification(text)
if not config.settings.telegram.enabled or not config.settings.telegram.token_b64 or config.settings.telegram.chat_id == "" then return end
local token = b64.decode(config.settings.telegram.token_b64)
local chat_id = config.settings.telegram.chat_id
text = text:gsub(" ", "%%20"):gsub("\n", "%%0A")
local url = string.format("https://api.telegram.org/bot%s/sendMessage?chat_id=%s&text=%s", token, chat_id, text)
effil.thread(function(request_url)
pcall(https.request, request_url)
end)(url)
end
function telegram_polling_thread()
local token = b64.decode(config.settings.telegram.token_b64)
if not token or token == "" then return end
local initial_url = string.format("https://api.telegram.org/bot%s/getUpdates?offset=-1", token)
local _, res = pcall(https.request, initial_url)
if res then
local data, _ = pcall(dkjson.decode, res)
if data and data.ok and #data.result > 0 then
last_telegram_update_id = data.result[#data.result].update_id
end
end
while true do
effil.sleep(2000)
if config.settings.telegram.enabled and config.settings.telegram.allow_commands then
local url = string.format("https://api.telegram.org/bot%s/getUpdates?offset=%d", token, last_telegram_update_id + 1)
local _, response = pcall(https.request, url)
if response then
local data, _ = pcall(dkjson.decode, response)
if data and data.ok and #data.result > 0 then
for _, update in ipairs(data.result) do
last_telegram_update_id = update.update_id
if update.message and update.message.text then
local text = u8.decode(update.message.text)
message_queue:push({ type = 'tg_command', text = text })
end
end
end
end
else
effil.sleep(5000)
end
end
end
sampev.onServerMessage = function(color, text)
if not config.autostarts or next(config.autostarts) == nil then return end
for id, autostart in pairs(config.autostarts) do
if text:find(autostart.keyword) then
local profile = config.profiles[autostart.profile_id]
if profile then
sampAddChatMessage(("{00BFFF}[TaimerPRO]{FFFFFF} Обнаружен триггер '%s'. Запуск таймера '%s'."):format(autostart.keyword, profile.name), 0xFFA500)
startNewTimer(profile)
end
end
end
end
function onScriptTerminate()
save_timers_state()
saveAllSettings()
end
---------------------------------------------------
--[[ 6. ГЛАВНЫЙ ЦИКЛ И ИНИЦИАЛИЗАЦИЯ ]]--
---------------------------------------------------
function main()
if not isSampLoaded() or not isSampfuncsLoaded() then return end
while not isSampAvailable() do wait(100) end
load_settings_on_startup()
resume_persistent_timers()
sampRegisterChatCommand('taimer', function()
main_window_state[0] = not main_window_state[0]
imgui.Process = main_window_state[0]
end)
if config.settings.telegram.enabled and config.settings.telegram.token_b64 ~= "" then
effil.thread(telegram_polling_thread)()
end
sampAddChatMessage('{00BFFF}[TaimerPRO]{FFFFFF} Загружен. Версия ' .. script_version .. '. Активация: /taimer', 0x2ECC71)
while true do
wait(0)
if next(active_timers) ~= nil then
if config.settings.hud.mode == 1 then
render_imgui_hud()
elseif config.settings.hud.mode == 2 then
render_native_hud()
end
end
while true do
local msg = message_queue:pop(0)
if not msg then break end
if msg.type == 'samp' then
sampAddChatMessage(u8.encode(msg.text), 0xFFFFFF)
elseif msg.type == 'telegram_send' then
sendTelegramNotification(msg.text)
elseif msg.type == 'timer_finished' or msg.type == 'timer_stopped' then
if active_timers[msg.id] then
if active_timers[msg.id].thread:status() ~= 'dead' then
active_timers[msg.id].thread:detach()
end
active_timers[msg.id] = nil
end
elseif msg.type == 'tg_command' then
local cmd, arg = msg.text:match("^/(%S+)%s*(.*)$")
if cmd then
cmd = cmd:lower()
if cmd == 'status' then
local response = "Активные таймеры:\n"
if next(active_timers) == nil then
response = "Нет активных таймеров."
else
for id, timer in pairs(active_timers) do
local remaining = timer.duration - (os.time() - timer.start_time)
local time_str = string.format("%02d:%02d:%02d", math.floor(remaining/3600), math.floor(remaining/60)%60, remaining%60)
response = response .. string.format("- %s: осталось %s\n", timer.name, time_str)
end
end
message_queue:push({ type = 'telegram_send', text = response })
elseif cmd == 'profiles' then
local response = "Доступные профили:\n"
if next(config.profiles) == nil then
response = "У вас нет сохраненных профилей."
else
for _, p in pairs(config.profiles) do response = response .. "- " .. p.name .. "\n" end
end
message_queue:push({ type = 'telegram_send', text = response })
elseif cmd == 'start' and arg ~= "" then
local found_profile = nil
for _, p in pairs(config.profiles) do
if p.name:lower() == arg:lower() then
found_profile = p
break
end
end
if found_profile then
startNewTimer(found_profile)
message_queue:push({ type = 'telegram_send', text = "Таймер '"..found_profile.name.."' запущен." })
else
message_queue:push({ type = 'telegram_send', text = "Профиль '"..arg.."' не найден." })
end
elseif cmd == 'stop' and arg ~= "" then
local found_timer_id = nil
for id, timer in pairs(active_timers) do
if timer.name:lower() == arg:lower() then
found_timer_id = id
break
end
end
if found_timer_id then
stopTimer(found_timer_id)
message_queue:push({ type = 'telegram_send', text = "Таймер '"..arg.."' остановлен." })
else
message_queue:push({ type = 'telegram_send', text = "Активный таймер '"..arg.."' не найден." })
end
end
end
end
end
end
end