commit 3dae94be3adbc21a985e792283cafd642de52de5 Author: ZorahM Date: Thu May 1 15:55:55 2025 +0000 Загрузить файлы в «/» diff --git a/!RepFlow.lua b/!RepFlow.lua new file mode 100644 index 0000000..93422a9 --- /dev/null +++ b/!RepFlow.lua @@ -0,0 +1,1660 @@ +require 'lib.moonloader' +local imgui = require 'mimgui' +local sampev = require 'lib.samp.events' +local vkeys = require 'vkeys' +local encoding = require 'encoding' +local inicfg = require 'inicfg' +local ffi = require 'ffi' +local faicons = require 'fAwesome6' +local dlstatus = require('moonloader').download_status +local lfs = require("lfs") + +-- +local CONFIG = { + iniFilename = 'RepFlowCFG.ini', + scriptVersion = "3.7.1 | Premium", + defaultKeyBind = 0x5A, + defaultKeyBindName = 'Z', + afkCooldown = 30, + tag = "{1E90FF} [RepFlow]: {FFFFFF}", + tagInfo = "{1E90FF} []: {FFFFFF}", +} + +-- ImGui +local new = imgui.new + +-- ChangeLog +local changelogEntries = { + { + version = "3.7.2", + date = "2025-03-20", + description = "- :\n" .. + " - ChangeLog.\n" .. + " - 'Ҹ ' .\n" .. + "- :\n" .. + " - ChangeLog: .\n" .. + " - `u8` .\n" .. + " - : .\n" .. + " - : `requests` `downloadUrlToFile`.\n" .. + "- :\n" .. + " - ChangeLog.\n" .. + " - .\n" .. + "- :\n" .. + " - .\n" .. + " - !" + }, + { + version = "3.7.1", + date = "2025-03-10", + description = "- :\n" .. + " - ' ' ''.\n" .. + "- :\n" .. + " - `/update`: .\n" .. + " - `update.ini`: .\n" .. + " - ImGui .\n" .. + "- :\n" .. + " - `update.ini` (cannot open file) .\n" .. + " - ' ' - .\n" .. + "- :\n" .. + " - MoonLoader .\n" .. + " - ." + }, + { + version = "3.7.0", + date = "2025-02-28", + description = "- :\n" .. + " - : `/ot`.\n" .. + " - .\n" .. + "- :\n" .. + " - : .\n" .. + " - ( ' ').\n" .. + " - .\n" .. + " - /: .\n" .. + " - '' : .\n" .. + "- :\n" .. + " - .\n" .. + " - .\n" .. + " - .\n" .. + " - .\n" .. + "- :\n" .. + " - ." + }, + { + version = "3.6.0", + date = "2025-02-15", + description = "- :\n" .. + " - blast.hk GitHub ''.\n" .. + "- :\n" .. + " - : .\n" .. + " - : .\n" .. + "- :\n" .. + " - CEF- , .\n" .. + "- :\n" .. + " - ." + }, + { + version = "3.5.0", + date = "2025-02-01", + description = "- :\n" .. + " - ''.\n" .. + " - ImGui.\n" .. + "- :\n" .. + " - ChangeLog: .\n" .. + " - : `u8` .\n" .. + "- :\n" .. + " - ." + }, +} + +-- - +local update_state = false +local update_found = false +local script_vers = 3.71 +local script_vers_text = "3.7.1" + +local update_url = "https://raw.githubusercontent.com/Zorahm/repflow/main/update.ini?t=" .. os.time() +local update_path = getWorkingDirectory() .. "/update.ini" + +local script_url = "https://raw.githubusercontent.com/Zorahm/repflow/main/!RepFlow.lua" +local script_path = thisScript().path + +-- +function check_update(callback) + local maxRetries = 3 + local retryCount = 0 + local downloadInProgress = false + local tempFilePath = os.tmpname() + + local function attemptDownload() + if downloadInProgress then return end + downloadInProgress = true + + logToFile(" update.ini URL: " .. update_url) + downloadUrlToFile(update_url, tempFilePath, function(id, status) + if status == dlstatus.STATUS_ENDDOWNLOADDATA then + logToFile("update.ini , : " .. tempFilePath) + local fileSize = lfs and lfs.attributes(tempFilePath, "size") or 1 + logToFile(" : " .. (fileSize or "/")) + if fileSize and fileSize > 0 then + local updateIni = inicfg.load(nil, tempFilePath) + if updateIni then + logToFile("update.ini ") + if updateIni.info and updateIni.info.vers and updateIni.info.vers_text then + local remoteVers = tonumber(updateIni.info.vers) + logToFile(" update.ini: vers=" .. updateIni.info.vers .. ", vers_text=" .. updateIni.info.vers_text) + logToFile(" : =" .. script_vers .. ", =" .. (remoteVers or "/")) + if remoteVers and remoteVers > script_vers then + sampAddChatMessage(CONFIG.tag .. "{FFFFFF} : {32CD32}" .. updateIni.info.vers_text .. ". {FFFFFF} /update.", -1) + update_found = true + logToFile(" : " .. updateIni.info.vers_text) + else + logToFile(" (" .. script_vers .. ") , (" .. (remoteVers or "/") .. ")") + end + else + logToFile(": update.ini vers/vers_text") + sampAddChatMessage(CONFIG.tag .. "{FF0000}: .", -1) + end + else + logToFile(": update.ini") + sampAddChatMessage(CONFIG.tag .. "{FF0000}: .", -1) + end + else + logToFile(": update.ini") + sampAddChatMessage(CONFIG.tag .. "{FF0000}: .", -1) + end + os.remove(tempFilePath) + downloadInProgress = false + if callback then callback(update_found) end + elseif status == dlstatus.STATUS_DOWNLOADERROR then + retryCount = retryCount + 1 + if retryCount < maxRetries then + logToFile(" update.ini. #" .. retryCount) + wait(1000) + attemptDownload() + else + logToFile(" update.ini " .. maxRetries .. " ") + sampAddChatMessage(CONFIG.tag .. "{FF0000} " .. maxRetries .. " .", -1) + os.remove(tempFilePath) + downloadInProgress = false + if callback then callback(false) end + end + elseif status == dlstatus.STATUS_CONNECTING or status == dlstatus.STATUS_REDIRECTING then + logToFile(" update.ini: " .. (status == dlstatus.STATUS_CONNECTING and "" or "")) + end + end) + end + + attemptDownload() +end + +-- +local STATE = { + keyBind = CONFIG.defaultKeyBind, + keyBindName = CONFIG.defaultKeyBindName, + lastDialogId = nil, + reportActive = false, + lastOtTime = 0, + active = false, + startTime = 0, + gameMinimized = false, + wasActiveBeforePause = false, + afkExitTime = 0, + changingKey = false, + moveWidget = false, + reportAnsweredCount = 0, + lastDialogTime = os.clock(), + manualDisable = false, + reportAttempts = 0, + floodCooldown = 0, + scriptStartTime = os.clock(), + floodCount = 0, + initialized = false, +} + +-- +local SETTINGS = { + otInterval = new.int(10), + dialogTimeout = new.int(600), + floodPause = new.int(10), + otIntervalBuffer = new.char[5](tostring(10)), + dialogTimeoutBuffer = new.char[5](tostring(600)), + floodPauseBuffer = new.char[5](tostring(10)), + useMilliseconds = new.bool(false), + hideFloodMsg = new.bool(true), + autoStartEnabled = new.bool(true), + dialogHandlerEnabled = new.bool(true), + infoWindowVisible = false, + cursorVisible = false, + mainWindowState = new.bool(false), + infoWindowState = new.bool(false), + activeState = new.bool(false), + disableAutoStartOnToggle = false, + selectedTheme = new.int(0), + useFloodPause = new.bool(true), + autoUpdateEnabled = new.bool(true), + logActionsEnabled = new.bool(true), + pauseOnFlood = new.bool(false), + activeTab = new.int(0), + selectedProfile = new.int(0), + playSoundOnReport = new.bool(true), +} + +-- +local COLOR_THEMES = { + { + name = "", + leftPanel = imgui.ImVec4(10 / 255, 15 / 255, 30 / 255, 1.0), -- - + rightPanel = imgui.ImVec4(15 / 255, 20 / 255, 40 / 255, 1.0), -- + childPanel = imgui.ImVec4(5 / 255, 10 / 255, 25 / 255, 1.0), -- + hover = imgui.ImVec4(50 / 255, 60 / 255, 100 / 255, 1.0), -- ˸ + button = imgui.ImVec4(30 / 255, 40 / 255, 80 / 255, 1.0), -- + buttonHovered = imgui.ImVec4(50 / 255, 60 / 255, 100 / 255, 1.0), -- + buttonActive = imgui.ImVec4(70 / 255, 80 / 255, 120 / 255, 1.0), -- + checkMark = imgui.ImVec4(150 / 255, 200 / 255, 255 / 255, 1.0), -- + frameBg = imgui.ImVec4(20 / 255, 25 / 255, 50 / 255, 1.0), -- + frameBgHovered = imgui.ImVec4(40 / 255, 45 / 255, 70 / 255, 1.0), -- + frameBgActive = imgui.ImVec4(60 / 255, 65 / 255, 90 / 255, 1.0), -- + text = imgui.ImVec4(200 / 255, 220 / 255, 255 / 255, 1.0), -- + }, + { + name = "", + leftPanel = imgui.ImVec4(50 / 255, 20 / 255, 10 / 255, 1.0), -- Ҹ + rightPanel = imgui.ImVec4(70 / 255, 30 / 255, 20 / 255, 1.0), -- Ҹ - + childPanel = imgui.ImVec4(40 / 255, 15 / 255, 5 / 255, 1.0), -- - + hover = imgui.ImVec4(120 / 255, 60 / 255, 40 / 255, 1.0), -- + button = imgui.ImVec4(100 / 255, 40 / 255, 30 / 255, 1.0), -- + buttonHovered = imgui.ImVec4(120 / 255, 60 / 255, 40 / 255, 1.0), -- + buttonActive = imgui.ImVec4(140 / 255, 80 / 255, 50 / 255, 1.0), -- + checkMark = imgui.ImVec4(255 / 255, 150 / 255, 100 / 255, 1.0), -- + frameBg = imgui.ImVec4(80 / 255, 30 / 255, 20 / 255, 1.0), -- + frameBgHovered = imgui.ImVec4(100 / 255, 50 / 255, 30 / 255, 1.0), -- + frameBgActive = imgui.ImVec4(120 / 255, 70 / 255, 40 / 255, 1.0), -- + text = imgui.ImVec4(255 / 255, 200 / 255, 180 / 255, 1.0), -- + }, + { + name = "", + leftPanel = imgui.ImVec4(20 / 255, 40 / 255, 20 / 255, 1.0), -- Ҹ + rightPanel = imgui.ImVec4(30 / 255, 50 / 255, 30 / 255, 1.0), -- + childPanel = imgui.ImVec4(15 / 255, 30 / 255, 15 / 255, 1.0), -- + hover = imgui.ImVec4(0 / 255, 200 / 255, 150 / 255, 1.0), -- + button = imgui.ImVec4(40 / 255, 80 / 255, 40 / 255, 1.0), -- + buttonHovered = imgui.ImVec4(0 / 255, 200 / 255, 150 / 255, 1.0), -- + buttonActive = imgui.ImVec4(0 / 255, 220 / 255, 170 / 255, 1.0), -- + checkMark = imgui.ImVec4(0 / 255, 255 / 255, 200 / 255, 1.0), -- + frameBg = imgui.ImVec4(30 / 255, 60 / 255, 30 / 255, 1.0), -- + frameBgHovered = imgui.ImVec4(40 / 255, 80 / 255, 40 / 255, 1.0), -- + frameBgActive = imgui.ImVec4(50 / 255, 100 / 255, 50 / 255, 1.0), -- + text = imgui.ImVec4(180 / 255, 255 / 255, 220 / 255, 1.0), -- + }, + { + name = "", + leftPanel = imgui.ImVec4(40 / 255, 30 / 255, 60 / 255, 1.0), -- Ҹ + rightPanel = imgui.ImVec4(50 / 255, 40 / 255, 80 / 255, 1.0), -- + childPanel = imgui.ImVec4(30 / 255, 20 / 255, 50 / 255, 1.0), -- Ҹ + hover = imgui.ImVec4(100 / 255, 80 / 255, 140 / 255, 1.0), -- + button = imgui.ImVec4(60 / 255, 50 / 255, 100 / 255, 1.0), -- + buttonHovered = imgui.ImVec4(100 / 255, 80 / 255, 140 / 255, 1.0), -- + buttonActive = imgui.ImVec4(120 / 255, 100 / 255, 160 / 255, 1.0), -- + checkMark = imgui.ImVec4(180 / 255, 150 / 255, 220 / 255, 1.0), -- + frameBg = imgui.ImVec4(40 / 255, 30 / 255, 70 / 255, 1.0), -- + frameBgHovered = imgui.ImVec4(60 / 255, 50 / 255, 90 / 255, 1.0), -- + frameBgActive = imgui.ImVec4(80 / 255, 70 / 255, 110 / 255, 1.0), -- + text = imgui.ImVec4(220 / 255, 200 / 255, 255 / 255, 1.0), -- + }, + { + name = "", + leftPanel = imgui.ImVec4(30 / 255, 30 / 255, 30 / 255, 1.0), -- Ҹ- + rightPanel = imgui.ImVec4(40 / 255, 40 / 255, 40 / 255, 1.0), -- + childPanel = imgui.ImVec4(20 / 255, 20 / 255, 20 / 255, 1.0), -- + hover = imgui.ImVec4(80 / 255, 80 / 255, 80 / 255, 1.0), -- - + button = imgui.ImVec4(50 / 255, 50 / 255, 50 / 255, 1.0), -- + buttonHovered = imgui.ImVec4(80 / 255, 80 / 255, 80 / 255, 1.0), -- + buttonActive = imgui.ImVec4(100 / 255, 100 / 255, 100 / 255, 1.0), -- + checkMark = imgui.ImVec4(180 / 255, 180 / 255, 180 / 255, 1.0), -- + frameBg = imgui.ImVec4(30 / 255, 30 / 255, 30 / 255, 1.0), -- + frameBgHovered = imgui.ImVec4(50 / 255, 50 / 255, 50 / 255, 1.0), -- + frameBgActive = imgui.ImVec4(70 / 255, 70 / 255, 70 / 255, 1.0), -- + text = imgui.ImVec4(200 / 255, 200 / 255, 200 / 255, 1.0), -- + }, +} + +-- ( - ) +local COLORS = COLOR_THEMES[1] + +-- +local sw, sh = getScreenResolution() + +-- +encoding.default = 'CP1251' +local u8 = encoding.UTF8 + +-- +local defaultConfig = { + main = { + keyBind = string.format("0x%X", CONFIG.defaultKeyBind), + keyBindName = CONFIG.defaultKeyBindName, + otInterval = 10, + useMilliseconds = false, + dialogTimeout = 600, + floodPause = 10, + dialogHandlerEnabled = true, + autoStartEnabled = true, + otklflud = false, + selectedTheme = 0, + useFloodPause = true, + autoUpdateEnabled = true, + logActionsEnabled = true, + selectedProfile = 0, + playSoundOnReport = true, + }, + widget = { + posX = 400, + posY = 400, + } +} + +-- +local ini = inicfg.load(defaultConfig, CONFIG.iniFilename) +STATE.keyBind = tonumber(ini.main.keyBind) or CONFIG.defaultKeyBind +STATE.keyBindName = ini.main.keyBindName or CONFIG.defaultKeyBindName +SETTINGS.otInterval[0] = tonumber(ini.main.otInterval) or 10 +SETTINGS.useMilliseconds[0] = ini.main.useMilliseconds == true +SETTINGS.dialogTimeout[0] = tonumber(ini.main.dialogTimeout) or 600 +SETTINGS.floodPause[0] = tonumber(ini.main.floodPause) or 10 +SETTINGS.dialogHandlerEnabled[0] = ini.main.dialogHandlerEnabled == true +SETTINGS.autoStartEnabled[0] = ini.main.autoStartEnabled == true +SETTINGS.hideFloodMsg[0] = ini.main.otklflud == true +SETTINGS.selectedTheme[0] = tonumber(ini.main.selectedTheme) or 0 +COLORS = COLOR_THEMES[SETTINGS.selectedTheme[0] + 1] -- +SETTINGS.useFloodPause[0] = ini.main.useFloodPause == true +SETTINGS.autoUpdateEnabled[0] = ini.main.autoUpdateEnabled == true +SETTINGS.logActionsEnabled[0] = ini.main.logActionsEnabled == true +SETTINGS.selectedProfile[0] = ini.main.selectedProfile or 0 +SETTINGS.playSoundOnReport[0] = ini.main.playSoundOnReport == true +SETTINGS.selectedTheme[0] = ini.main.selectedTheme or 0 + +-- +function main() + if not isSampLoaded() or not isSampfuncsLoaded() then return end + while not isSampAvailable() do wait(100) end + + if STATE.initialized then return end + STATE.initialized = true + + -- + local soundPath = getWorkingDirectory() .. "\\report_sound.mp3" + local soundUrl = "https://github.com/Zorahm/repflow/raw/refs/heads/main/report_sound.mp3" -- URL + + if doesFileExist(soundPath) then + reportSound = loadAudioStream(soundPath) + if reportSound then + sampAddChatMessage(CONFIG.tag .. " .", -1) + logToFile(" : " .. soundPath) + setAudioStreamVolume(reportSound, 1.0) + else + sampAddChatMessage(CONFIG.tag .. "{FF0000}: : " .. soundPath, -1) + logToFile(": : " .. soundPath) + end + else + sampAddChatMessage(CONFIG.tag .. " report_sound.mp3 . ...", -1) + logToFile(" report_sound.mp3 , " .. soundUrl) + + -- + downloadUrlToFile(soundUrl, soundPath, function(id, status) + if status == dlstatus.STATUS_ENDDOWNLOADDATA then + -- , + if doesFileExist(soundPath) then + reportSound = loadAudioStream(soundPath) + if reportSound then + sampAddChatMessage(CONFIG.tag .. " :" , -1) + logToFile(" : " .. soundPath) + setAudioStreamVolume(reportSound, 1.0) + else + sampAddChatMessage(CONFIG.tag .. "{FF0000}: : " .. soundPath, -1) + logToFile(": : " .. soundPath) + end + else + sampAddChatMessage(CONFIG.tag .. "{FF0000}: . report_sound.mp3 moonloader .", -1) + logToFile(": URL " .. soundUrl) + end + STATE.soundDownloadProgress = 0 -- + elseif status == dlstatus.STATUS_DOWNLOADERROR then + sampAddChatMessage(CONFIG.tag .. "{FF0000}: . report_sound.mp3 moonloader .", -1) + logToFile(": " .. soundUrl) + STATE.soundDownloadProgress = 0 -- + elseif status == dlstatus.STATUS_DOWNLOADINGDATA then + STATE.soundDownloadProgress = (id.progress * 100) + sampAddChatMessage(CONFIG.tag .. " : " .. math.floor(STATE.soundDownloadProgress) .. "%", -1) + end + end) + end + + sampRegisterChatCommand("arep", cmd_arep) + sampRegisterChatCommand("update", cmd_update) + sampRegisterChatCommand("showinfo", showInfoWindow) + sampRegisterChatCommand("hideinfo", showInfoWindowOff) + + sampAddChatMessage(CONFIG.tag .. " {00FF00}.{FFFFFF} : {00FF00}/arep{FFFFFF}", -1) + logToFile(" ") + + loadSettingsFromProfile() + + -- + if SETTINGS.autoUpdateEnabled[0] then + sampAddChatMessage(CONFIG.tag .. " ...", -1) + logToFile(" ") + check_update(function(found) + if not found then + sampAddChatMessage(CONFIG.tag .. " .", -1) + logToFile(" : ") + end + end) + end + + local lastMoveCheck = 0 + local moveCheckInterval = 50 + + while true do + wait(0) + checkPauseAndDisableAutoStart() + checkAutoStart() + imgui.Process = SETTINGS.mainWindowState[0] and not STATE.gameMinimized + + if update_state then + downloadUrlToFile(script_url, script_path, function(id, status) + if status == dlstatus.STATUS_ENDDOWNLOADDATA then + STATE.updateProgress = 100 + sampAddChatMessage(CONFIG.tag .. "{FFFFFF} {32CD32} {FFFFFF}. MoonLoader.", -1) + logToFile(" ") + thisScript():reload() + elseif status == dlstatus.STATUS_DOWNLOADERROR then + STATE.updateProgress = 0 + sampAddChatMessage(CONFIG.tag .. "{FF0000}: .", -1) + logToFile(" ") + update_state = false + elseif status == dlstatus.STATUS_DOWNLOADINGDATA then + STATE.updateProgress = (id.progress * 100) + sampAddChatMessage(CONFIG.tag .. ": " .. math.floor(STATE.updateProgress) .. "%", -1) + end + end) + end + + if STATE.moveWidget then + local cursorX, cursorY = getCursorPos() + ini.widget.posX = cursorX + ini.widget.posY = cursorY + local currentTime = os.clock() * 1000 + if currentTime - lastMoveCheck >= moveCheckInterval then + if isKeyJustPressed(0x20) then + STATE.moveWidget = false + sampToggleCursor(false) + saveWindowSettings() + end + lastMoveCheck = currentTime + end + end + + if STATE.active or STATE.moveWidget then + showInfoWindow() + else + showInfoWindowOff() + end + + if not STATE.changingKey and isKeyJustPressed(STATE.keyBind) and not isSampfuncsConsoleActive() and + not sampIsChatInputActive() and not sampIsDialogActive() and not isPauseMenuActive() then + onToggleActive() + end + + if STATE.active and STATE.floodCooldown < os.clock() * 1000 then + local currentTime = os.clock() * 1000 + local interval = SETTINGS.useMilliseconds[0] and SETTINGS.otInterval[0] or (SETTINGS.otInterval[0] * 1000) + if currentTime - STATE.lastOtTime >= interval then + STATE.reportAttempts = STATE.reportAttempts + 1 + sampSendChat('/ot') + logToFile(" /ot, #" .. STATE.reportAttempts) + STATE.lastOtTime = currentTime + end + else + STATE.startTime = os.clock() + end + end +end + +-- ImGui +function resetIO() + local io = imgui.GetIO() + for i = 0, 511 do io.KeysDown[i] = false end + for i = 0, 4 do io.MouseDown[i] = false end + io.KeyCtrl = false + io.KeyShift = false + io.KeyAlt = false + io.KeySuper = false +end + +function imgui.ShowHelpMarker(desc) + imgui.TextDisabled("(?)") + if imgui.IsItemHovered() then + imgui.BeginTooltip() + imgui.PushTextWrapPos(imgui.GetFontSize() * 35.0) + imgui.TextUnformatted(desc) + imgui.PopTextWrapPos() + imgui.EndTooltip() + end +end + +-- +function startMovingWindow() + STATE.moveWidget = true + showInfoWindow() + sampToggleCursor(true) + SETTINGS.mainWindowState[0] = false + sampAddChatMessage(CONFIG.tagInfo .. '{FFFF00} . "" .', -1) +end + +-- ImGui +imgui.OnInitialize(function() + imgui.GetIO().IniFilename = nil + local config = imgui.ImFontConfig() + config.MergeMode = true + config.PixelSnapH = true + local iconRanges = new.ImWchar[3](faicons.min_range, faicons.max_range, 0) + imgui.GetIO().Fonts:AddFontFromMemoryCompressedBase85TTF(faicons.get_font_data_base85('solid'), 14, config, iconRanges) + decor() +end) + +-- ImGui +function decor() + imgui.SwitchContext() + local style = imgui.GetStyle() + style.WindowPadding = imgui.ImVec2(12, 12) + style.WindowRounding = 12.0 + style.ChildRounding = 10.0 + style.FramePadding = imgui.ImVec2(8, 6) + style.FrameRounding = 10.0 + style.ItemSpacing = imgui.ImVec2(10, 10) + style.ItemInnerSpacing = imgui.ImVec2(10, 10) + style.ScrollbarSize = 12.0 + style.ScrollbarRounding = 10.0 + style.GrabRounding = 10.0 + style.PopupRounding = 10.0 + style.WindowTitleAlign = imgui.ImVec2(0.5, 0.5) + style.ButtonTextAlign = imgui.ImVec2(0.5, 0.5) +end + +-- +function sampev.onServerMessage(color, text) + if text:find('%[(%W+)%] (%w+_%w+)%[(%d+)%]:') and STATE.active then + sampSendChat('/ot') + end + return filterFloodMessage(text) +end + +-- +function onToggleActive() + STATE.active = not STATE.active + STATE.manualDisable = not STATE.active + SETTINGS.disableAutoStartOnToggle = not STATE.active + local status = STATE.active and '{00FF00}' or '{FF0000}' + local statusArz = STATE.active and '' or '' + logToFile(" " .. statusArz) + if SETTINGS.activeState then + SETTINGS.activeState[0] = STATE.active + end +end + +function onKeyDown(key) + if not STATE.changingKey and key == STATE.keyBind and not isSampfuncsConsoleActive() and + not sampIsChatInputActive() and not sampIsDialogActive() and not isPauseMenuActive() then + onToggleActive() + end +end + +-- +function saveWindowSettings() + ini.widget.posX = ini.widget.posX or 400 + ini.widget.posY = ini.widget.posY or 400 + inicfg.save(ini, CONFIG.iniFilename) + sampAddChatMessage(CONFIG.tagInfo .. '{00FF00} !', -1) +end + +function sampev.onShowDialog(dialogId, style, title, button1, button2, text) + if dialogId == 1334 then + if SETTINGS.dialogHandlerEnabled[0] then + STATE.lastDialogTime = os.clock() -- + STATE.reportAnsweredCount = STATE.reportAnsweredCount + 1 -- + + -- , + if SETTINGS.playSoundOnReport[0] and reportSound then + setAudioStreamState(reportSound, 1) -- + logToFile(" ") + elseif SETTINGS.playSoundOnReport[0] and not reportSound then + sampAddChatMessage(CONFIG.tag .. "{FF0000}: ( " .. getWorkingDirectory() .. "\\report_sound.mp3).", -1) + logToFile(": ( " .. getWorkingDirectory() .. "\\report_sound.mp3)") + end + + sampAddChatMessage(CONFIG.tag .. '{00FF00} . : ' .. STATE.reportAnsweredCount, -1) + logToFile(" , : " .. STATE.reportAnsweredCount) + -- + local playerName = text:match("(%w+_%w+)%[") or "" + logReport(playerName, text) + if STATE.active then + STATE.active = false + sampAddChatMessage(CONFIG.tag .. " - ", -1) + logToFile(" - ") + end + else + -- , , + logToFile(" 1334 ( ), : " .. tostring(STATE.active)) + sampAddChatMessage(CONFIG.tag .. " , . .", -1) + end + end +end + +-- +function checkAutoStart() + local currentTime = os.clock() + if SETTINGS.autoStartEnabled[0] and not STATE.active and not STATE.gameMinimized and + (STATE.afkExitTime == 0 or currentTime - STATE.afkExitTime >= CONFIG.afkCooldown) then + if not SETTINGS.disableAutoStartOnToggle and (currentTime - STATE.lastDialogTime) > SETTINGS.dialogTimeout[0] then + STATE.active = true + sampAddChatMessage(CONFIG.tag .. " -", -1) + logToFile(" ") + end + end +end + +-- +function saveSettings() + -- ini.main + ini.main.otInterval = SETTINGS.otInterval[0] + ini.main.dialogTimeout = SETTINGS.dialogTimeout[0] + ini.main.floodPause = SETTINGS.floodPause[0] + ini.main.useMilliseconds = SETTINGS.useMilliseconds[0] == true + ini.main.dialogHandlerEnabled = SETTINGS.dialogHandlerEnabled[0] == true + ini.main.autoStartEnabled = SETTINGS.autoStartEnabled[0] == true + ini.main.otklflud = SETTINGS.hideFloodMsg[0] == true + ini.main.selectedTheme = SETTINGS.selectedTheme[0] + ini.main.useFloodPause = SETTINGS.useFloodPause[0] == true + ini.main.autoUpdateEnabled = SETTINGS.autoUpdateEnabled[0] == true + ini.main.logActionsEnabled = SETTINGS.logActionsEnabled[0] == true + ini.main.selectedProfile = SETTINGS.selectedProfile[0] + ini.main.pauseOnFlood = SETTINGS.pauseOnFlood[0] == true + ini.main.playSoundOnReport = SETTINGS.playSoundOnReport[0] == true + + -- + inicfg.save(ini, CONFIG.iniFilename) + + -- + local profileName = "profile_" .. (SETTINGS.selectedProfile[0] + 1) + inicfg.save(ini, "moonloader/" .. profileName .. ".ini") + logToFile(" : " .. (SETTINGS.selectedProfile[0] + 1)) +end + +-- +function imgui.Link(link, text, customColors, icon, isActive) + -- + text = text or link + isActive = isActive == nil and true or isActive -- + customColors = customColors or { + isActive and COLORS.text or imgui.ImVec4(0.6, 0.6, 0.6, 1.0), -- + isActive and COLORS.hover or imgui.ImVec4(0.6, 0.6, 0.6, 1.0) -- + } + icon = icon or faicons('link') -- + + -- + local tSize = imgui.CalcTextSize(text) + local iconSize = icon and imgui.CalcTextSize(icon) or imgui.ImVec2(0, 0) + local totalSize = imgui.ImVec2(tSize.x + (icon and (iconSize.x + 5) or 0), tSize.y) + + -- DrawList + local p = imgui.GetCursorScreenPos() + local DL = imgui.GetWindowDrawList() + + -- ( ) + local isHovered = isActive and imgui.IsItemHovered() or false + local colorTransition = imgui.GetTime() * 5 -- + + -- + local r1, g1, b1, a1 + local r2, g2, b2, a2 + + -- , customColors + if type(customColors[1]) == "number" then + r1, g1, b1, a1 = imgui.ColorConvertU32ToFloat4(customColors[1]) + else + r1, g1, b1, a1 = customColors[1].x, customColors[1].y, customColors[1].z, customColors[1].w + end + + if type(customColors[2]) == "number" then + r2, g2, b2, a2 = imgui.ColorConvertU32ToFloat4(customColors[2]) + else + r2, g2, b2, a2 = customColors[2].x, customColors[2].y, customColors[2].z, customColors[2].w + end + + -- + local t = isHovered and (0.5 + math.sin(colorTransition) * 0.5) or (0.5 - math.sin(colorTransition) * 0.5) + local r = r1 + (r2 - r1) * t + local g = g1 + (g2 - g1) * t + local b = b1 + (b2 - b1) * t + local a = a1 + (a2 - a1) * t + + -- ImVec4 ColorConvertFloat4ToU32 + local animatedColorVec = imgui.ImVec4(r, g, b, a) + local animatedColor = imgui.ColorConvertFloat4ToU32(animatedColorVec) + + -- ( ) + if isActive then + if imgui.InvisibleButton('##' .. link, totalSize) then + -- + local success, err = pcall(function() + os.execute('explorer "' .. link .. '"') + end) + if not success then + sampAddChatMessage(CONFIG.tag .. "{FF0000}: : " .. link, -1) + logToFile(" : " .. link .. " | : " .. tostring(err)) + else + logToFile(" : " .. link) + end + end + else + -- , + imgui.Dummy(totalSize) + end + + -- , + if icon then + DL:AddText(p, animatedColor, icon) + p.x = p.x + iconSize.x + 5 -- + end + + -- + DL:AddText(p, animatedColor, text) + + -- + DL:AddLine( + imgui.ImVec2(p.x - (icon and (iconSize.x + 5) or 0), p.y + tSize.y), + imgui.ImVec2(p.x + tSize.x, p.y + tSize.y), + animatedColor, + 1.0 + ) + + -- + if isActive and imgui.IsItemHovered() then + imgui.BeginTooltip() + imgui.Text(u8", \n ") + imgui.EndTooltip() + elseif not isActive and imgui.IsItemHovered() then + imgui.BeginTooltip() + imgui.Text(u8" ") + imgui.EndTooltip() + end + + -- ( ) + if isActive then + if imgui.BeginPopupContextItem("LinkContextMenu##" .. link) then + if imgui.Selectable(u8" ") then -- MenuItem Selectable + imgui.SetClipboardText(link) + sampAddChatMessage(CONFIG.tag .. " : " .. link, -1) + logToFile(" : " .. link) + end + imgui.EndPopup() + end + + -- + if imgui.IsItemHovered() and imgui.IsMouseClicked(1) then + imgui.OpenPopup("LinkContextMenu##" .. link) + end + end +end + +-- +function cmd_arep(arg) + SETTINGS.mainWindowState[0] = not SETTINGS.mainWindowState[0] + imgui.Process = SETTINGS.mainWindowState[0] +end + +function cmd_update(arg) + if update_state then + sampAddChatMessage(CONFIG.tag .. "{FF0000} . .", -1) + return + end + + if not update_found then + sampAddChatMessage(CONFIG.tag .. " ...", -1) + logToFile(" /update") + check_update(function(found) + update_found = found + if found then + sampAddChatMessage(CONFIG.tag .. "{FFFFFF} . ...", -1) + logToFile(" , performUpdate") + performUpdate() + else + sampAddChatMessage(CONFIG.tag .. "{FF0000} .", -1) + logToFile(" ") + end + end) + else + sampAddChatMessage(CONFIG.tag .. "{FFFFFF} ...", -1) + logToFile(" ") + performUpdate() + end +end + +-- +function performUpdate() + update_state = true + STATE.updateProgress = 0 + local maxRetries = 3 + local retryCount = 0 + + local function attemptDownload() + downloadUrlToFile(script_url, script_path, function(id, status) + if status == dlstatus.STATUS_ENDDOWNLOADDATA then + STATE.updateProgress = 100 + sampAddChatMessage(CONFIG.tag .. "{FFFFFF} {32CD32} {FFFFFF}. MoonLoader.", -1) + logToFile(" ") + update_state = false + update_found = false -- + thisScript():reload() + elseif status == dlstatus.STATUS_DOWNLOADERROR then + retryCount = retryCount + 1 + if retryCount < maxRetries then + sampAddChatMessage(CONFIG.tag .. " . #" .. retryCount, -1) + logToFile(" , #" .. retryCount) + wait(1000) + attemptDownload() + else + STATE.updateProgress = 0 + sampAddChatMessage(CONFIG.tag .. "{FF0000} " .. maxRetries .. " .", -1) + logToFile(" " .. maxRetries .. " ") + update_state = false + end + elseif status == dlstatus.STATUS_DOWNLOADINGDATA then + STATE.updateProgress = (id.progress * 100) + sampAddChatMessage(CONFIG.tag .. ": " .. math.floor(STATE.updateProgress) .. "%", -1) + end + end) + end + + attemptDownload() +end + +-- "" +function drawMainTab() + local panelColor = COLORS.childPanel + imgui.Text(faicons('gear') .. u8" / " .. faicons('message') .. u8" ") + imgui.Separator() + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("Flooder", imgui.ImVec2(0, 150), true) then + imgui.PushItemWidth(100) + if imgui.Checkbox(u8' ', SETTINGS.useMilliseconds) then + ini.main.useMilliseconds = SETTINGS.useMilliseconds[0] + saveSettings() -- + end + imgui.PopItemWidth() + imgui.Text(u8' /ot (' .. (SETTINGS.useMilliseconds[0] and u8'' or u8'') .. '):') + imgui.Text(u8': ' .. SETTINGS.otInterval[0]) + imgui.PushItemWidth(45) + imgui.InputText(u8'##otIntervalInput', SETTINGS.otIntervalBuffer, ffi.sizeof(SETTINGS.otIntervalBuffer)) + imgui.SameLine() + if imgui.Button(faicons('floppy_disk') .. u8" ") then + local newValue = tonumber(ffi.string(SETTINGS.otIntervalBuffer)) + if newValue then + SETTINGS.otInterval[0] = newValue + saveSettings() -- + sampAddChatMessage(CONFIG.tagInfo .. " : {32CD32}" .. newValue, -1) + else + sampAddChatMessage(CONFIG.tagInfo .. ": {32CD32} .", -1) + end + end + imgui.PopItemWidth() + end + imgui.EndChild() + imgui.PopStyleColor() + + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("FloodPause", imgui.ImVec2(0, 100), true) then + imgui.Text(u8' ():') + imgui.Text(u8': ' .. SETTINGS.floodPause[0]) + imgui.PushItemWidth(45) + imgui.InputText(u8'##floodPauseInput', SETTINGS.floodPauseBuffer, ffi.sizeof(SETTINGS.floodPauseBuffer)) + imgui.SameLine() + if imgui.Button(faicons('floppy_disk') .. u8" ") then + local newValue = tonumber(ffi.string(SETTINGS.floodPauseBuffer)) + if newValue and newValue >= 1 and newValue <= 60 then + SETTINGS.floodPause[0] = newValue + saveSettings() -- + sampAddChatMessage(CONFIG.tagInfo .. " : {32CD32}" .. newValue .. " ", -1) + else + sampAddChatMessage(CONFIG.tagInfo .. ": {32CD32}1-60 .", -1) + end + end + imgui.PopItemWidth() + end + imgui.EndChild() + imgui.PopStyleColor() + + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("InfoFlooder", imgui.ImVec2(0, 65), true) then + imgui.Text(u8' [] _.') + imgui.Text(u8' .') + end + imgui.EndChild() + imgui.PopStyleColor() +end + +-- "" +function drawSettingsTab() + local panelColor = COLORS.childPanel + imgui.Text(faicons('gear') .. u8" / " .. faicons('sliders') .. u8" ") + imgui.Separator() + + -- + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("KeyBind", imgui.ImVec2(0, 60), true) then + imgui.Text(u8' :') + imgui.SameLine() + if imgui.Button(u8'' .. STATE.keyBindName) then + STATE.changingKey = true + sampAddChatMessage(CONFIG.tag .. " ", -1) + end + imgui.SameLine() + imgui.ShowHelpMarker(u8" / . , .") + end + imgui.EndChild() + imgui.PopStyleColor() + + -- + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("DialogOptions", imgui.ImVec2(0, 110), true) then + imgui.Text(u8" ") + if imgui.Checkbox(u8' ', SETTINGS.dialogHandlerEnabled) then + saveSettings() -- + end + imgui.SameLine() + imgui.ShowHelpMarker(u8" , (ID 1334) .") + if imgui.Checkbox(u8' ', SETTINGS.autoStartEnabled) then + saveSettings() -- + end + imgui.SameLine() + imgui.ShowHelpMarker(u8" -, .") + end + imgui.EndChild() + imgui.PopStyleColor() + + -- + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("FloodOptions", imgui.ImVec2(0, 110), true) then + imgui.Text(u8" ") + if imgui.Checkbox(u8' " "', SETTINGS.hideFloodMsg) then + saveSettings() -- + end + imgui.SameLine() + imgui.ShowHelpMarker(u8" .") + if imgui.Checkbox(u8' ', SETTINGS.useFloodPause) then + saveSettings() -- + sampAddChatMessage(CONFIG.tagInfo .. " : {32CD32}" .. (SETTINGS.useFloodPause[0] and "" or ""), -1) + end + imgui.SameLine() + imgui.ShowHelpMarker(u8" /ot .") + end + imgui.EndChild() + imgui.PopStyleColor() + + -- + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("SoundOptions", imgui.ImVec2(0, 80), true) then + imgui.Text(u8" ") + if imgui.Checkbox(u8' ', SETTINGS.playSoundOnReport) then + saveSettings() -- + sampAddChatMessage(CONFIG.tagInfo .. " : {32CD32}" .. (SETTINGS.playSoundOnReport[0] and "" or ""), -1) + end + imgui.SameLine() + imgui.ShowHelpMarker(u8" (report_sound.mp3) . moonloader.") + end + imgui.EndChild() + imgui.PopStyleColor() + + -- + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("UpdateOptions", imgui.ImVec2(0, 150), true) then + imgui.Text(u8"") + if imgui.Checkbox(u8' ', SETTINGS.autoUpdateEnabled) then + saveSettings() + sampAddChatMessage(CONFIG.tagInfo .. ": {32CD32}" .. (SETTINGS.autoUpdateEnabled[0] and "" or ""), -1) + end + imgui.SameLine() + imgui.ShowHelpMarker(u8" .") + imgui.Text(u8' :') + imgui.SameLine() + if imgui.Button(u8'') then + sampAddChatMessage(CONFIG.tag .. " ...", -1) + logToFile(" ") + check_update(function(found) + update_found = found -- + imgui.Process = true -- + if not found then + sampAddChatMessage(CONFIG.tag .. "{FF0000} .", -1) + logToFile(" : ") + end + end) + end + imgui.SameLine() + imgui.ShowHelpMarker(u8" .") + if update_state then + imgui.Text(u8" : " .. math.floor(STATE.updateProgress) .. "%") + imgui.ProgressBar(STATE.updateProgress / 100, imgui.ImVec2(-1, 15), u8"") + elseif update_found then + if imgui.Button(u8" ") then + sampAddChatMessage(CONFIG.tag .. "{FFFFFF} ...", -1) + logToFile(" ") + cmd_update("") + end + else + imgui.Text(u8" .") + end + end + imgui.EndChild() + imgui.PopStyleColor() + + -- + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("LoggingOptions", imgui.ImVec2(0, 80), true) then + imgui.Text(u8"") + if imgui.Checkbox(u8' ', SETTINGS.logActionsEnabled) then + saveSettings() -- + sampAddChatMessage(CONFIG.tagInfo .. " : {32CD32}" .. (SETTINGS.logActionsEnabled[0] and "" or ""), -1) + end + imgui.SameLine() + imgui.ShowHelpMarker(u8" repflow.log.") + end + imgui.EndChild() + imgui.PopStyleColor() + + -- - + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("AutoStartTimeout", imgui.ImVec2(0, 100), true) then + imgui.Text(u8'- ():') + imgui.SameLine() + imgui.ShowHelpMarker(u8" .") + imgui.Text(u8': ' .. SETTINGS.dialogTimeout[0]) + imgui.PushItemWidth(45) + imgui.InputText(u8'##dialogTimeoutInput', SETTINGS.dialogTimeoutBuffer, ffi.sizeof(SETTINGS.dialogTimeoutBuffer)) + imgui.SameLine() + if imgui.Button(faicons('floppy_disk') .. u8" ") then + local newValue = tonumber(ffi.string(SETTINGS.dialogTimeoutBuffer)) + if newValue and newValue >= 1 and newValue <= 9999 then + SETTINGS.dialogTimeout[0] = newValue + saveSettings() -- + sampAddChatMessage(CONFIG.tagInfo .. "- : {32CD32}" .. newValue .. " ", -1) + else + sampAddChatMessage(CONFIG.tagInfo .. ": {32CD32}1-9999.", -1) + end + end + imgui.PopItemWidth() + end + imgui.EndChild() + imgui.PopStyleColor() + + -- + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("WindowPosition", imgui.ImVec2(0, 60), true) then + imgui.Text(u8' -:') + imgui.SameLine() + if imgui.Button(u8'') then + startMovingWindow() + end + imgui.SameLine() + imgui.ShowHelpMarker(u8" .") + end + imgui.EndChild() + imgui.PopStyleColor() + + -- + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("ProfileOptions", imgui.ImVec2(0, 75), true) then + imgui.Text(u8' :') + imgui.SameLine() + imgui.ShowHelpMarker(u8" . .") + local profiles = { u8" 1", u8" 2", u8" 3" } + local cProfiles = ffi.new("const char*[?]", #profiles) + for i, name in ipairs(profiles) do cProfiles[i - 1] = name end + if imgui.Combo(u8'##ProfileSelector', SETTINGS.selectedProfile, cProfiles, #profiles) then + ini.main.selectedProfile = SETTINGS.selectedProfile[0] + saveSettings() -- + loadSettingsFromProfile() + sampAddChatMessage(CONFIG.tag .. " : " .. (SETTINGS.selectedProfile[0] + 1), -1) + end + end + imgui.EndChild() + imgui.PopStyleColor() + + -- + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("ColorScheme", imgui.ImVec2(0, 100), true) then + imgui.Text(u8' :') + imgui.SameLine() + imgui.ShowHelpMarker(u8" .") + local themeNames = {} + for i, theme in ipairs(COLOR_THEMES) do + themeNames[i] = u8(theme.name) + end + local cThemeNames = ffi.new("const char*[?]", #themeNames) + for i, name in ipairs(themeNames) do + cThemeNames[i - 1] = name + end + if imgui.Combo(u8'##ThemeSelector', SETTINGS.selectedTheme, cThemeNames, #themeNames) then + COLORS = COLOR_THEMES[SETTINGS.selectedTheme[0] + 1] + saveSettings() -- + sampAddChatMessage(CONFIG.tagInfo .. " : {32CD32}" .. COLOR_THEMES[SETTINGS.selectedTheme[0] + 1].name, -1) + logToFile(" : " .. COLOR_THEMES[SETTINGS.selectedTheme[0] + 1].name) + end + end + imgui.EndChild() + imgui.PopStyleColor() +end + +sampRegisterChatCommand("arepstats", function() + local elapsedTime = os.clock() - STATE.scriptStartTime + local successRate = STATE.reportAttempts > 0 and (STATE.reportAnsweredCount / STATE.reportAttempts * 100) or 0 + sampAddChatMessage(CONFIG.tag .. " RepFlow:", -1) + sampAddChatMessage(CONFIG.tag .. " : " .. string.format("%.2f ", elapsedTime), -1) + sampAddChatMessage(CONFIG.tag .. ": " .. STATE.reportAttempts, -1) + sampAddChatMessage(CONFIG.tag .. " : " .. STATE.reportAnsweredCount, -1) + sampAddChatMessage(CONFIG.tag .. ": " .. string.format("%.1f%%", successRate), -1) + sampAddChatMessage(CONFIG.tag .. " : " .. STATE.floodCount, -1) + logToFile(" /arepstats") +end) + +function logReport(playerName, reportText) + local reportFile = io.open("moonloader/repflow_reports.log", "a") + if reportFile then + local timestamp = os.date("%Y-%m-%d %H:%M:%S") + reportFile:write(string.format("[%s] : %s | : %s\n", timestamp, playerName, reportText)) + reportFile:close() + end +end + +function filterFloodMessage(text) + if SETTINGS.hideFloodMsg[0] and text:find("%[%] {FFFFFF} !") then + STATE.floodCount = STATE.floodCount + 1 + if SETTINGS.useFloodPause[0] then + STATE.floodCooldown = os.clock() * 1000 + (SETTINGS.floodPause[0] * 1000) + sampAddChatMessage(CONFIG.tag .. ", " .. SETTINGS.floodPause[0] .. " ", -1) + logToFile(", " .. SETTINGS.floodPause[0] .. " ") + end + if SETTINGS.pauseOnFlood[0] then + STATE.active = false -- + SETTINGS.activeState[0] = false + sampAddChatMessage(CONFIG.tag .. "{FF0000} - !", -1) + logToFile(" - ") + end + return false + end + return true +end + +-- +function checkPauseAndDisableAutoStart() + if isPauseMenuActive() then + if not STATE.gameMinimized then + STATE.wasActiveBeforePause = STATE.active + if STATE.active then STATE.active = false end + STATE.gameMinimized = true + end + else + if STATE.gameMinimized then + STATE.gameMinimized = false + STATE.afkExitTime = os.clock() + if STATE.wasActiveBeforePause then + sampAddChatMessage(CONFIG.tag .. '{FFFFFF} . - AFK!', -1) + logToFile(" , ") + end + end + end +end + +function drawInfoTab() + imgui.CenterText((faicons('star') .. u8" RepFlow / " .. faicons('circle_info') .. u8" "), { + color = COLORS.text + }) + imgui.Separator() + local panelColor = COLORS.childPanel + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("Info", imgui.ImVec2(0, -1), true) then + -- + imgui.Text(u8"RepFlow v" .. CONFIG.scriptVersion .. " by Matthew_McLaren[18]") + imgui.Text(u8" Arizona RP.") + + -- + imgui.Separator() + imgui.Text(u8" :") + imgui.BulletText(u8" /ot .") + imgui.BulletText(u8" '[] _'.") + imgui.BulletText(u8" 1334 .") + imgui.BulletText(u8" ( '').") + imgui.BulletText(u8" : , , .") + imgui.BulletText(u8" .") + + -- + imgui.Separator() + imgui.Text(u8" :") + imgui.TextWrapped(u8" report_sound.mp3 moonloader. ' ' .") + + -- + imgui.Separator() + imgui.Text(u8" :") + imgui.TextWrapped(u8": moonloader/repflow.log ( ), moonloader/repflow_reports.log (). : moonloader/config/profile_X.ini.") + + -- + imgui.Separator() + imgui.Text(u8":") + imgui.Link("https://blast.hk/threads/12345", u8" blast.hk", nil, faicons('CLOUD_EXCLAMATION'), false) -- + imgui.Link("https://github.com/Zorahm/RepFlow", u8"GitHub-", nil, faicons('MOON_STARS'), true) -- + + -- + imgui.Separator() + imgui.Text(u8":") + imgui.TextWrapped(u8": Carl_Mort[18], Sweet_Lemonte[18], Balenciaga_Collins[18].") + + -- + imgui.Text(u8" :") + imgui.SameLine() + imgui.Link("https://t.me/zorahm", u8"Telegram", nil, faicons('PAPER_PLANE'), true) + + -- + imgui.Text(u8" . !") + end + imgui.EndChild() + imgui.PopStyleColor() +end + +function drawStatsTab() + local panelColor = COLORS.childPanel + imgui.Text(faicons('star') .. u8" RepFlow / " .. faicons('chart_bar') .. u8" ") -- + imgui.Separator() + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + if imgui.BeginChild("Stats", imgui.ImVec2(0, -1), true) then + -- + imgui.Text(faicons('clock') .. u8" : " .. string.format("%.2f", os.clock() - STATE.scriptStartTime) .. u8" ") + + -- /ot + imgui.Text(faicons('paper_plane') .. u8" /ot: " .. STATE.reportAttempts) + + -- + imgui.Text(faicons('circle_check') .. u8" : " .. STATE.reportAnsweredCount) + + -- + imgui.Text(faicons('triangle_exclamation') .. u8" : " .. STATE.floodCount) + + -- + if STATE.reportAttempts > 0 then + local totalTime = os.clock() - STATE.scriptStartTime + local avgTime = totalTime / STATE.reportAttempts + imgui.Text(faicons('square_poll_horizontal') .. u8" /ot: " .. string.format("%.2f", avgTime) .. u8" ") + else + imgui.Text(faicons('square_poll_horizontal') .. u8" /ot: N/A") + end + + -- + if imgui.Button(u8" ", imgui.ImVec2(-1, 30)) then -- + STATE.reportAttempts = 0 + STATE.reportAnsweredCount = 0 + STATE.floodCount = 0 + STATE.scriptStartTime = os.clock() + sampAddChatMessage(CONFIG.tag .. " ", -1) + logToFile(" ") + end + + imgui.Dummy(imgui.ImVec2(0, 10)) + end + imgui.EndChild() + imgui.PopStyleColor() +end + +function drawChangeLogTab() + imgui.PushStyleColor(imgui.Col.Text, imgui.ImVec4(0.0, 0.8, 1.0, 1.0)) + imgui.CenterText(faicons('star') .. u8" RepFlow " .. faicons('bolt') .. u8" ChangeLog") + imgui.PopStyleColor() + + imgui.Dummy(imgui.ImVec2(0, 5)) + + imgui.Text(u8" GitHub:") + imgui.SameLine() + imgui.Link("https://github.com/Zorahm/RepFlow/blob/main/CHANGELOG.md", faicons('github') .. u8" ", nil, nil, true) + imgui.Separator() + + local panelColor = imgui.ImVec4(0.07, 0.07, 0.07, 1.0) + imgui.PushStyleColor(imgui.Col.ChildBg, panelColor) + + if imgui.BeginChild("ChangeLog", imgui.ImVec2(0, -1), true) then + for i, entry in ipairs(changelogEntries) do + imgui.PushStyleColor(imgui.Col.Text, imgui.ImVec4(0.9, 0.9, 0.9, 1.0)) + local headerText = faicons('tag') .. u8" " .. u8(entry.version) .. u8" (" .. u8(entry.date) .. u8")" + local isOpen = imgui.CollapsingHeader(headerText, nil, imgui.TreeNodeFlags.DefaultOpen) + imgui.PopStyleColor() + + if isOpen then + imgui.SameLine(imgui.GetWindowWidth() - 60) + if imgui.SmallButton(faicons('copy') .. u8"##copy_" .. i) then + imgui.SetClipboardText(entry.description) + sampAddChatMessage(CONFIG.tag .. " " .. entry.version .. " !", 0x1E90FF) + end + imgui.ShowHelpMarker(u8" ") + + imgui.PushTextWrapPos(imgui.GetWindowWidth() - 20) + + local categories = { + newFeatures = {}, + improvements = {}, + bugFixes = {}, + notes = {} + } + + local currentCategory = nil + for line in entry.description:gmatch("[^\n]+") do + line = line:match("^%s*(.-)%s*$") + if line:match("^%- :") then + currentCategory = categories.newFeatures + elseif line:match("^%- :") then + currentCategory = categories.improvements + elseif line:match("^%- :") then + currentCategory = categories.bugFixes + elseif line:match("^%- :") then + currentCategory = categories.notes + elseif line:match("^%s*-") and currentCategory then + table.insert(currentCategory, line:match("^%s*-(.+)%s*$")) + end + end + + local function drawCategory(categoryName, categoryData, icon, color) + if #categoryData > 0 then + imgui.PushStyleColor(imgui.Col.Text, color) + imgui.Text(icon .. u8" " .. u8(categoryName)) + imgui.PopStyleColor() + for _, item in ipairs(categoryData) do + imgui.BulletText(faicons('circle') .. u8" " .. u8(item)) + end + imgui.Dummy(imgui.ImVec2(0, 3)) + end + end + + drawCategory(" ", categories.newFeatures, faicons('plus'), imgui.ImVec4(0.2, 0.8, 0.2, 1.0)) + drawCategory("", categories.improvements, faicons('arrow-up'), imgui.ImVec4(0.0, 0.6, 1.0, 1.0)) + drawCategory("", categories.bugFixes, faicons('bug'), imgui.ImVec4(1.0, 0.3, 0.3, 1.0)) + drawCategory("", categories.notes, faicons('info-circle'), imgui.ImVec4(0.8, 0.8, 0.0, 1.0)) + + imgui.PopTextWrapPos() + imgui.Dummy(imgui.ImVec2(0, 5)) + end + end + end + imgui.EndChild() + imgui.PopStyleColor() +end + +-- +imgui.OnFrame(function() return SETTINGS.mainWindowState[0] end, function(player) + imgui.SetNextWindowSize(imgui.ImVec2(800, 500), imgui.Cond.FirstUseEver) + imgui.SetNextWindowPos(imgui.ImVec2(sw / 2, sh / 2), imgui.Cond.FirstUseEver, imgui.ImVec2(0.5, 0.5)) + -- + imgui.PushStyleColor(imgui.Col.WindowBg, COLORS.rightPanel) + imgui.PushStyleColor(imgui.Col.Text, COLORS.text) + imgui.PushStyleColor(imgui.Col.Button, COLORS.button) + imgui.PushStyleColor(imgui.Col.ButtonHovered, COLORS.buttonHovered) + imgui.PushStyleColor(imgui.Col.ButtonActive, COLORS.buttonActive) + imgui.PushStyleColor(imgui.Col.CheckMark, COLORS.checkMark) + imgui.PushStyleColor(imgui.Col.FrameBg, COLORS.frameBg) + imgui.PushStyleColor(imgui.Col.FrameBgHovered, COLORS.frameBgHovered) + imgui.PushStyleColor(imgui.Col.FrameBgActive, COLORS.frameBgActive) + resetIO() + + if imgui.Begin(faicons('bolt') .. u8' RepFlow | Premium', SETTINGS.mainWindowState, imgui.WindowFlags.NoResize + imgui.WindowFlags.NoCollapse) then + imgui.PushStyleColor(imgui.Col.ChildBg, COLORS.leftPanel) + if imgui.BeginChild("left_panel", imgui.ImVec2(125, -1), false) then + local tabNames = { "", "", "", "", "ChangeLog" } + for i, name in ipairs(tabNames) do + if i - 1 == SETTINGS.activeTab[0] then + imgui.PushStyleColor(imgui.Col.Button, COLORS.hover) + else + imgui.PushStyleColor(imgui.Col.Button, COLORS.leftPanel) + end + imgui.PushStyleColor(imgui.Col.ButtonHovered, COLORS.hover) + imgui.PushStyleColor(imgui.Col.ButtonActive, COLORS.hover) + if imgui.Button(u8(name), imgui.ImVec2(125, 40)) then + SETTINGS.activeTab[0] = i - 1 + end + imgui.PopStyleColor(3) + end + end + imgui.EndChild() + imgui.PopStyleColor() + + imgui.SameLine() + imgui.PushStyleColor(imgui.Col.ChildBg, COLORS.rightPanel) + if imgui.BeginChild("right_panel", imgui.ImVec2(-1, 0), false) then + if SETTINGS.activeTab[0] == 0 then drawMainTab() + elseif SETTINGS.activeTab[0] == 1 then drawSettingsTab() + elseif SETTINGS.activeTab[0] == 2 then drawInfoTab() + elseif SETTINGS.activeTab[0] == 3 then drawStatsTab() + elseif SETTINGS.activeTab[0] == 4 then drawChangeLogTab() + end + end + imgui.EndChild() + imgui.PopStyleColor() + end + imgui.End() + -- + imgui.PopStyleColor(9) +end) + +-- +function onWindowMessage(msg, wparam, lparam) + if STATE.changingKey then + if msg == 0x100 or msg == 0x101 then + STATE.keyBind = wparam + STATE.keyBindName = vkeys.id_to_name(STATE.keyBind) + STATE.changingKey = false + ini.main.keyBind = string.format("0x%X", STATE.keyBind) + ini.main.keyBindName = STATE.keyBindName + inicfg.save(ini, CONFIG.iniFilename) + sampAddChatMessage(string.format(CONFIG.tag .. '{FFFFFF} : {00FF00}%s', STATE.keyBindName), -1) + logToFile(" : " .. STATE.keyBindName) + return false + end + end +end + +-- +function imgui.CenterText(text, options) + -- + options = options or {} + local color = options.color or nil -- (ImVec4 nil) + local wrap = options.wrap or false -- (true/false) + local icon = options.icon or nil -- (, faicons('icon')) + + -- + local windowWidth = imgui.GetWindowWidth() + + -- , + local lines = {} + for line in text:gmatch("[^\n]+") do + table.insert(lines, line) + end + + -- , + if icon then + lines[1] = icon .. " " .. lines[1] + end + + -- + for _, line in ipairs(lines) do + local lineSize = imgui.CalcTextSize(line) + imgui.SetCursorPosX((windowWidth - lineSize.x) / 2) + if color then + if wrap then + imgui.PushTextWrapPos(windowWidth) + imgui.TextColored(color, line) + imgui.PopTextWrapPos() + else + imgui.TextColored(color, line) + end + else + if wrap then + imgui.PushTextWrapPos(windowWidth) + imgui.Text(line) + imgui.PopTextWrapPos() + else + imgui.Text(line) + end + end + end +end + +function loadSettingsFromProfile() + local profileName = "profile_" .. (SETTINGS.selectedProfile[0] + 1) + local profileIni = inicfg.load(defaultConfig, "moonloader/" .. profileName .. ".ini") + SETTINGS.otInterval[0] = profileIni.main.otInterval or 10 + SETTINGS.dialogTimeout[0] = profileIni.main.dialogTimeout or 600 + SETTINGS.floodPause[0] = profileIni.main.floodPause or 10 + SETTINGS.useMilliseconds[0] = profileIni.main.useMilliseconds == true + SETTINGS.hideFloodMsg[0] = profileIni.main.otklflud == true + SETTINGS.autoStartEnabled[0] = profileIni.main.autoStartEnabled == true + SETTINGS.dialogHandlerEnabled[0] = profileIni.main.dialogHandlerEnabled == true + SETTINGS.selectedTheme[0] = profileIni.main.selectedTheme or 0 + SETTINGS.useFloodPause[0] = profileIni.main.useFloodPause == true + SETTINGS.autoUpdateEnabled[0] = profileIni.main.autoUpdateEnabled == true + SETTINGS.playSoundOnReport[0] = profileIni.main.playSoundOnReport == true + SETTINGS.logActionsEnabled[0] = profileIni.main.logActionsEnabled == true + SETTINGS.selectedTheme[0] = profileIni.main.selectedTheme or 0 + COLORS = COLOR_THEMES[SETTINGS.selectedTheme[0] + 1] + ini.widget.posX = profileIni.widget.posX or 400 + ini.widget.posY = profileIni.widget.posY or 400 + + -- ini.main + ini.main = profileIni.main or {} + ini.main.otInterval = SETTINGS.otInterval[0] + ini.main.dialogTimeout = SETTINGS.dialogTimeout[0] + ini.main.floodPause = SETTINGS.floodPause[0] + ini.main.useMilliseconds = SETTINGS.useMilliseconds[0] + ini.main.dialogHandlerEnabled = SETTINGS.dialogHandlerEnabled[0] + ini.main.autoStartEnabled = SETTINGS.autoStartEnabled[0] + ini.main.otklflud = SETTINGS.hideFloodMsg[0] + ini.main.selectedTheme = SETTINGS.selectedTheme[0] + ini.main.useFloodPause = SETTINGS.useFloodPause[0] + ini.main.autoUpdateEnabled = SETTINGS.autoUpdateEnabled[0] + ini.main.logActionsEnabled = SETTINGS.logActionsEnabled[0] + ini.main.selectedProfile = SETTINGS.selectedProfile[0] + ini.main.pauseOnFlood = SETTINGS.pauseOnFlood[0] + ini.main.playSoundOnReport = SETTINGS.playSoundOnReport[0] + + inicfg.save(ini, CONFIG.iniFilename) + sampAddChatMessage(CONFIG.tag .. " : " .. (SETTINGS.selectedProfile[0] + 1), -1) + logToFile(" : " .. (SETTINGS.selectedProfile[0] + 1)) +end + +imgui.OnFrame(function() return SETTINGS.infoWindowState[0] end, function(self) + self.HideCursor = true + + imgui.SetNextWindowSize(imgui.ImVec2(260, 350), imgui.Cond.FirstUseEver) -- 350 + imgui.SetNextWindowPos(imgui.ImVec2(ini.widget.posX, ini.widget.posY), imgui.Cond.Always) + + imgui.Begin(faicons('star') .. u8" | RepFlow ", SETTINGS.infoWindowState, imgui.WindowFlags.NoResize + imgui.WindowFlags.NoCollapse) + + -- + imgui.PushStyleColor(imgui.Col.Text, COLORS.text) + imgui.Text(faicons('bolt') .. u8" ") + imgui.PopStyleColor() + imgui.SameLine() + imgui.SetCursorPosX(imgui.GetWindowWidth() - 30) -- HelpMarker + imgui.ShowHelpMarker(u8" .") + + -- + local statusColor = STATE.active and imgui.ImVec4(0.0, 1.0, 0.0, 1.0) or imgui.ImVec4(1.0, 0.0, 0.0, 1.0) + if STATE.active then + local alpha = 0.8 + math.sin(os.clock() * 4) * 0.2 + statusColor.w = alpha + end + imgui.PushStyleColor(imgui.Col.Text, statusColor) + if imgui.Button(u8(STATE.active and '' or ''), imgui.ImVec2(imgui.GetWindowWidth() - 40, 30)) then + STATE.active = not STATE.active + if SETTINGS.activeState then -- , activeState + SETTINGS.activeState[0] = STATE.active + end + if STATE.active then + STATE.startTime = os.clock() + STATE.lastOtTime = os.clock() * 1000 + sampAddChatMessage(CONFIG.tag .. " ", -1) + logToFile(" ") + else + sampAddChatMessage(CONFIG.tag .. " ", -1) + logToFile(" ") + end + end + imgui.SetCursorPosX((imgui.GetWindowWidth() - imgui.CalcTextSize(u8(STATE.active and '' or '')).x) / 2) + imgui.PopStyleColor() + + -- + local style = imgui.GetStyle() + imgui.SetCursorPosX(style.WindowPadding.x) -- + local elapsedTime = os.clock() - STATE.startTime + imgui.Text(faicons('clock') .. u8" : " .. string.format(u8'%.2f ', elapsedTime)) + + -- + imgui.Text(faicons('file') .. u8" : " .. STATE.reportAttempts) + imgui.Text(faicons('circle_check') .. u8" : " .. STATE.reportAnsweredCount) + + -- + local successRate = STATE.reportAttempts > 0 and (STATE.reportAnsweredCount / STATE.reportAttempts * 100) or 0 + imgui.Text(faicons('percent') .. u8" : " .. string.format(u8'%.1f%%', successRate)) + + imgui.Dummy(imgui.ImVec2(0, 10)) + + -- + imgui.Separator() + imgui.Text(faicons('window_maximize') .. u8" : " .. (SETTINGS.dialogHandlerEnabled[0] and u8'' or u8'')) + imgui.Text(faicons('circle_play') .. u8" : " .. (SETTINGS.autoStartEnabled[0] and u8'' or u8'')) + imgui.Text(faicons('clock') .. u8" : " .. SETTINGS.otInterval[0] .. u8' ') + + -- - + if STATE.floodCooldown > os.clock() * 1000 and SETTINGS.useFloodPause[0] then + local remainingTime = math.max(0, (STATE.floodCooldown - os.clock() * 1000) / 1000) + local totalTime = SETTINGS.floodPause[0] + local progress = 1.0 - (remainingTime / totalTime) + imgui.PushStyleColor(imgui.Col.PlotHistogram, imgui.ImVec4(1.0, 0.5, 0.0, 1.0)) + imgui.Text(faicons('circle_pause') .. u8" : " .. string.format(u8'%.1f ', remainingTime)) + imgui.ProgressBar(progress, imgui.ImVec2(-1, 15), u8"") + imgui.PopStyleColor() + end + imgui.End() +end) + +-- +function showInfoWindow() + if not SETTINGS.infoWindowState[0] then + SETTINGS.infoWindowState[0] = true + ini.widget.visible = true + inicfg.save(ini, CONFIG.iniFilename) + end +end + +function showInfoWindowOff() + if SETTINGS.infoWindowState[0] then + SETTINGS.infoWindowState[0] = false + ini.widget.visible = false + inicfg.save(ini, CONFIG.iniFilename) + end +end + +-- +function logToFile(message) + if not SETTINGS.logActionsEnabled[0] and not message:find("") then + return -- , , + end + local logFile = io.open("moonloader/repflow.log", "a") + if logFile then + local timestamp = os.date("%Y-%m-%d %H:%M:%S") + logFile:write(string.format("[%s] %s\n", timestamp, message)) + logFile:close() + end +end \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2441288 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,86 @@ +# RepFlow ChangeLog + +## RepFlow 3.6 | Premium - Улучшенная стабильность и новые функции +Рад представить вам RepFlow 3.6 | Premium! В этом обновлении мы сосредоточились на оптимизации работы скрипта, устранении проблем с сохранением настроек и добавлении полезных функций, таких как кликабельные ссылки. Ваш опыт использования становится ещё более надёжным и удобным. + +### Основные изменения и нововведения: +- **Обновление окна информации**: + - Добавлены кликабельные ссылки на форум blast.hk и GitHub-репозиторий в меню "Информация", что упрощает доступ к ресурсам и поддержке. +- **Оптимизация производительности**: + - Улучшена общая оптимизация скрипта: сокращено количество ненужных вызовов функций, оптимизирован цикл `main()` и уменьшена нагрузка на процессор. +- **Удаление ненужных уведомлений**: + - Удалены CEF-уведомления от Arizona RP, что устраняет лишние всплывающие окна и улучшает пользовательский опыт. + +### Исправления: +- **Сохранение настроек**: + - Исправлены потенциальные проблемы с сохранением настроек во вкладке "Настройки". Теперь все чекбоксы (например, "Обрабатывать диалоги", "Автостарт ловли", "Скрыть 'Не флуди'", "Использовать паузу после флуда", "Автообновление при запуске", "Логировать действия") корректно сохраняются и загружаются благодаря улучшенной синхронизации. +- **Дополнительные улучшения**: + - Устранены возможные сбои при обновлении зависимостей из репозитория. + +### Установка и обновление: +- Скачайте файл `!RepFlow.lua` из этого релиза и замените им существующий в папке `moonloader`. +- Убедитесь, что все зависимости (`mimgui`, `samp.events`, `vkeys`, `encoding`, `inicfg`, `ffi`, `fAwesome6`) установлены. +- Запустите игру и используйте команду `/arep` для активации меню или `/update` для проверки обновлений. +- Рекомендуется перезапустить MoonLoader после обновления. + +### Примечания: +- Проверьте правила сервера Arizona RP (или другого, где вы используете скрипт) на предмет соответствия. +- Спасибо за поддержку и обратную связь! Если возникнут вопросы или предложения, пишите в Telegram (@Zorahm). + +Удачной ловли репортов с RepFlow 3.6! + +--- + +## RepFlow 3.5 | Premium - Новый уровень удобства +Рад представить вам RepFlow 3.5 | Premium! В этом обновлении мы сосредоточились на улучшении интерфейса, повышении удобства работы с настройками и устранении багов. Теперь ваш опыт использования скрипта станет ещё более комфортным. + +### Основные изменения и нововведения: +- Кнопка сброса настроек: Добавлена возможность сбросить все настройки до значений по умолчанию через меню "Настройки". +- Отображение версии: Теперь текущая версия скрипта отображается в заголовке окна ImGui. +- Улучшенный ChangeLog: Добавлены отступы для пунктов списка, что делает текст более читаемым. +- Оптимизация производительности: Минимизированы вызовы `u8` в циклических функциях для снижения нагрузки. + +### Исправления: +- Устранены лишние пробелы в отображении ChangeLog. +- Исправлены лаги при перемещении окна информации (infowindow). +- Устранены проблемы с сохранением настроек: теперь все параметры корректно сохраняются через унифицированную функцию. + +### Установка и обновление: +- Скачайте файл `!RepFlow.lua` из этого релиза и замените им существующий в папке `moonloader`. +- Убедитесь, что все зависимости (`mimgui`, `samp.events`, `vkeys`, `encoding`, `inicfg`, `ffi`, `fAwesome6`) установлены. +- Запустите игру и используйте команду `/arep` для активации меню или `/update` для проверки обновлений. +- Рекомендуется перезапустить MoonLoader после обновления. + +### Примечания: +- Проверьте правила сервера Arizona RP (или другого, где вы используете скрипт) на предмет соответствия. +- Спасибо за поддержку и обратную связь! Если возникнут вопросы или предложения, пишите в Telegram. + +Удачной ловли репортов с RepFlow 3.5! + +--- + +### Версия 3.4 | Premium +- Обновлено меню настроек: чекбоксы распределены по категориям (Диалоги, Флуд, Обновления, Логирование). +- Добавлена возможность отключить логирование каждого действия (критические логи остаются). +- Добавлена поддержка профилей настроек: теперь можно сохранять и загружать до трёх профилей. +- Добавлено автоматическое логирование принятых репортов в файл `repflow_reports.log`. +- Добавлена новая вкладка "Статистика" с информацией о времени работы, попытках, репортах и флуде. +- Добавлена команда `/update` для ручного запуска обновления скрипта. + +### Версия 3.3 | Premium +- Добавлены новые стильные цветовые темы: "Космос", "Закат", "Неон", "Лаванда", "Графит". +- Обновлены существующие темы для более эстетичного вида. +- Улучшена читаемость интерфейса за счёт новых цветовых сочетаний. + +### Версия 3.2 | Premium +- Оптимизирована производительность (меньше вызовов `wait`). +- Добавлена статистика в информационное окно (попытки и принятые репорты). +- Улучшена защита от флуда с настраиваемой паузой. +- Добавлено логирование событий в файл `repflow.log`. +- Добавлен выбор цветовых тем. + +### Версия 3.1 | Premium +- Новый стиль меню. +- ChangeLog разделён на версии. +- **HF-1.0**: Исправлены грамматические ошибки. +- **HF-1.1**: Налажен цвет плиток, исправлены грамматические ошибки. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1a0d3cd --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# RepFlow - Скрипт для автоматической ловли репортов в SAMP + +**RepFlow** - это скрипт для MoonLoader (SAMP), который автоматизирует процесс отправки команды `/ot` для ловли репортов на серверах Arizona RP и подобных проектах. Скрипт предоставляет удобный интерфейс через ImGui, гибкие настройки, поддержку профилей и автоматическое управление процессом. + +**Версия**: 3.6 | Premium +**Автор**: Matthew_McLaren[18] +**Telegram**: [t.me/Zorahm](https://t.me/Zorahm) + +--- + +## Основные возможности + +- **Автоматическая отправка команды `/ot`** с настраиваемым интервалом (в секундах или миллисекундах). +- **Обнаружение репортов в чате** по ключевой фразе `[Репорт] от Имя_Фамилия`. +- **Обработка диалогов**: автоматический приём репортов (диалог 1334) с подсчётом принятых. +- **Автостарт ловли**: активация через заданный тайм-аут после последнего диалога. +- **Настраиваемая клавиша активации** (по умолчанию `Z`). +- **Информационное окно**: отображение статуса ловли, времени работы, количества попыток и принятых репортов. +- **Поддержка профилей настроек**: сохранение и загрузка до трёх профилей. +- **Сохранение настроек** в файл `RepFlowCFG.ini`. +- **Интерфейс ImGui**: меню с вкладками "Флудер", "Настройки", "Информация", "Статистика" и "ChangeLog". +- **Темы оформления**: выбор из нескольких цветовых схем (Космос, Закат, Неон, Лаванда, Графит). +- **Логирование**: запись событий в файл `repflow.log` и принятых репортов в `repflow_reports.log`. +- **Защита от флуда**: настройка паузы после обнаружения флуда. +- **Автообновление**: проверка и установка обновлений при запуске или через команду `/update`. +- **Сброс настроек**: возможность сбросить все настройки до значений по умолчанию через меню "Настройки". +- **Отображение версии**: текущая версия скрипта отображается в заголовке окна ImGui. + +--- + +## Требования + +- **SAMP**: установленный клиент San Andreas Multiplayer. +- **MoonLoader**: версия 0.26 или выше. +- **Библиотеки**: + - `mimgui` + - `samp.events` + - `vkeys` + - `encoding` + - `inicfg` + - `ffi` + - `fAwesome6` (иконки FontAwesome). + +Убедитесь, что все зависимости установлены через `mlupdater` или вручную. + +--- + +## Установка + +1. Скачайте скрипт `!RepFlow.lua` и поместите его в папку `moonloader`. +2. Убедитесь, что все необходимые библиотеки установлены в папке `moonloader/lib`. +3. Запустите игру. Скрипт автоматически загрузится и выведет сообщение: + `[RepFlow]: Скрипт загружен. Активация меню: /arep`. + +--- + +## Использование + +### Активация +- **Команда**: Введите `/arep` в чате, чтобы открыть меню настроек. +- **Клавиша**: Нажмите `Z` (по умолчанию) для включения/выключения ловли. +- **Обновление**: Введите `/update` для ручного запуска обновления (доступно при наличии новой версии). + +### Настройки +- **Интервал отправки `/ot`**: Установите в меню "Флудер" (от 1 до 9999 секунд/миллисекунд). +- **Пауза после флуда**: Настройте в меню "Флудер" (от 1 до 60 секунд). +- **Тайм-аут автостарта**: Настройте в меню "Настройки" (по умолчанию 600 секунд). +- **Перемещение окна**: В разделе "Настройки" выберите "Изменить положение" и используйте пробел для фиксации. +- **Смена клавиши**: Нажмите на текущую клавишу в меню "Настройки" и выберите новую. +- **Профили**: Выберите и сохраните один из трёх профилей настроек в меню "Настройки". +- **Логирование**: Включите/выключите логирование действий в меню "Настройки". +- **Сброс настроек**: Используйте кнопку "Сбросить все настройки" в меню "Настройки" для возврата к значениям по умолчанию. + +### Интерфейс +- **Флудер**: Настройка интервала, паузы после флуда и режима работы. +- **Настройки**: Основные параметры, управление окном, профилями и сброс настроек. +- **Информация**: Сведения об авторе, версии, функционале и благодарности тестировщикам. +- **Статистика**: Отображение времени работы, попыток `/ot`, принятых репортов и количества флудов с возможностью сброса. +- **ChangeLog**: История обновлений скрипта. + +--- + +[ChangeLog](https://github.com/Zorahm/repflow/blob/main/CHANGELOG.md) + +## Благодарности +- **Тестеры**: Carl_Mort[18], Sweet_Lemonte[18], Balenciaga_Collins[18]. + +--- + +## Примечания +- Скрипт разработан для использования на серверах Arizona RP. Убедитесь, что его использование соответствует правилам вашего сервера. +- Если возникают ошибки, проверьте наличие всех зависимостей и их актуальность. +- Логи сохраняются в файлы `repflow.log` (события) и `repflow_reports.log` (принятые репорты) в папке MoonLoader. + +**Связь с автором**: [Telegram](https://t.me/Zorahm) +Удачной ловли репортов с RepFlow! diff --git a/update.ini b/update.ini new file mode 100644 index 0000000..f66ab33 --- /dev/null +++ b/update.ini @@ -0,0 +1,3 @@ +[info] +vers=3.6 +vers_text=3.6