local imgui = require 'mimgui' local ffi = require 'ffi' local sampev = require 'samp.events' local encoding = require 'encoding' encoding.default = "CP1251" local u8 = encoding.UTF8 local fa_status, faicons = pcall(require, 'fAwesome6') local new = imgui.new local win_state = new.bool(false) local show_cursor_state = new.bool(true) local saved_size = imgui.ImVec2(350, 450) local function loadJsonFile(path) local fPath = getWorkingDirectory() .. path local file = io.open(fPath, "r") local data = "[]" if file then data = file:read("*a") file:close() end return decodeJson(data) end local daily = loadJsonFile("\\resource\\BPdaily.json") local premium = loadJsonFile("\\resource\\BPpremium.json") local quests = {} local premiumBP = false imgui.OnInitialize(function() local config = imgui.ImFontConfig() config.MergeMode = true config.PixelSnapH = true if fa_status then 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) end imgui.GetIO().MouseDrawCursor = false apply_custom_style() end) local function getIcon(name) if not fa_status then return "?" end return faicons(name) end function main() while not isSampAvailable() do wait(100) end sampAddChatMessage("{32CD32}[BP Helper] {FFFFFF}Скрипт успешно загружен!", -1) sampAddChatMessage("{32CD32}[BP Helper] {FFFFFF}Команда: {32CD32}/bph {FFFFFF}| Прозрачный режим: {32CD32}F9", -1) sampRegisterChatCommand('bph', function () win_state[0] = not win_state[0] if win_state[0] then show_cursor_state[0] = true else local player = new('struct player') player.HideCursor = true end end) while true do wait(0) if win_state[0] and isKeyJustPressed(120) then show_cursor_state[0] = not show_cursor_state[0] end end end imgui.OnFrame(function() return win_state[0] end, function(player) local is_active = show_cursor_state[0] local style = imgui.GetStyle() local alpha_bg = is_active and 0.98 or 0.0 local alpha_items = is_active and 1.0 or 0.0 style.Colors[imgui.Col.WindowBg].w = alpha_bg style.Colors[imgui.Col.TitleBg].w = alpha_bg style.Colors[imgui.Col.TitleBgActive].w = alpha_bg style.Colors[imgui.Col.ScrollbarBg].w = alpha_bg style.Colors[imgui.Col.FrameBg].w = alpha_bg style.Colors[imgui.Col.ScrollbarGrab].w = alpha_items style.Colors[imgui.Col.Border].w = alpha_items style.Colors[imgui.Col.Separator].w = alpha_items style.Colors[imgui.Col.ResizeGrip].w = alpha_items local win_flags = imgui.WindowFlags.NoCollapse if not is_active then imgui.GetIO().WantCaptureMouse = false win_flags = imgui.WindowFlags.NoTitleBar + imgui.WindowFlags.NoResize + imgui.WindowFlags.NoMove + imgui.WindowFlags.NoInputs + imgui.WindowFlags.NoBackground + imgui.WindowFlags.NoNav + imgui.WindowFlags.NoFocusOnAppearing end if is_active then player.HideCursor = false else player.HideCursor = not sampIsCursorActive() end if is_active then imgui.SetNextWindowSize(saved_size, imgui.Cond.FirstUseEver) else imgui.SetNextWindowSize(saved_size, imgui.Cond.Always) end local title_icon = getIcon('scroll') if imgui.Begin(title_icon .. u8' BP Helper', win_state, win_flags) then if is_active then saved_size = imgui.GetWindowSize() end local count_total = #quests local count_done = 0 for _, q in ipairs(quests) do if q.progress then count_done = count_done + 1 end end local progress_pct = (count_total > 0) and (count_done / count_total) or 0 imgui.PushStyleColor(imgui.Col.PlotHistogram, imgui.ImVec4(0.26, 0.59, 0.98, is_active and 1.0 or 0.8)) imgui.PushStyleColor(imgui.Col.Text, imgui.ImVec4(1.0, 1.0, 1.0, 1.0)) local label_text = string.format(u8"Выполнено: %d из %d", count_done, count_total) imgui.ProgressBar(progress_pct, imgui.ImVec2(-1, is_active and 15 or 12), label_text) imgui.PopStyleColor(2) if is_active then imgui.Spacing() else imgui.Dummy(imgui.ImVec2(0, 5)) end local sorted_quests = {} local pinned, active, completed, hidden = {}, {}, {}, {} for _, quest in ipairs(quests) do if quest.pinned[0] and not quest.progress then table.insert(pinned, quest) elseif not quest.show[0] and not quest.progress then table.insert(hidden, quest) elseif not quest.progress then table.insert(active, quest) else table.insert(completed, quest) end end for _, q in ipairs(pinned) do table.insert(sorted_quests, q) end for _, q in ipairs(active) do table.insert(sorted_quests, q) end for _, q in ipairs(hidden) do table.insert(sorted_quests, q) end if not is_active then imgui.PushStyleColor(imgui.Col.ChildBg, imgui.ImVec4(0,0,0,0)) end imgui.BeginChild("QuestsList", imgui.ImVec2(0, 0), true) if #sorted_quests == 0 then imgui.Dummy(imgui.ImVec2(0, 10)) imgui.PushStyleColor(imgui.Col.Text, imgui.ImVec4(1, 1, 0, 1)) imgui.Text(getIcon('circle-info') .. u8" Инструкция:") imgui.PopStyleColor() imgui.Separator() imgui.Dummy(imgui.ImVec2(0, 5)) imgui.TextWrapped(u8"1. Откройте Боевой Пропуск (клавиша B), чтобы скрипт просканировал ваши задания.") imgui.Dummy(imgui.ImVec2(0, 5)) imgui.TextWrapped(u8"2. Используйте /bph чтобы скрывать или показывать это окно.") imgui.Dummy(imgui.ImVec2(0, 5)) imgui.TextWrapped(u8"3. Нажмите F9, чтобы включить Прозрачный режим.") imgui.Dummy(imgui.ImVec2(0, 5)) imgui.TextWrapped(u8"4. В Прозрачном режиме вы можете управлять персонажем и кликать по интерфейсу игры (CEF).") else for index, quest in ipairs(sorted_quests) do imgui.PushIDInt(index) drawQuestCard(quest, is_active) imgui.PopID() if not is_active then local last_y = imgui.GetCursorPosY() imgui.SetCursorPosY(last_y - 4) end end end imgui.EndChild() if not is_active then imgui.PopStyleColor() end imgui.End() end end) function drawQuestCard(quest, is_active) local style = imgui.GetStyle() local displayText = quest.text if not quest.progress and quest.max > 1 then displayText = string.format("%s (%d/%d)", quest.text, quest.curr, quest.max) end if is_active then local draw_list = imgui.GetWindowDrawList() local availWidth = imgui.GetContentRegionAvail().x local buttonsAreaWidth = 60 local textWidth = availWidth - buttonsAreaWidth local text_color = style.Colors[imgui.Col.Text] local accent_color = imgui.ImVec4(0.25, 0.25, 0.28, 1.0) if quest.pinned[0] and not quest.progress then accent_color = imgui.ImVec4(1.0, 0.75, 0.0, 1.0) elseif quest.progress then accent_color = imgui.ImVec4(0.2, 0.7, 0.3, 1.0) text_color = imgui.ImVec4(0.6, 0.6, 0.6, 1.0) elseif not quest.show[0] then text_color = imgui.ImVec4(0.4, 0.4, 0.4, 1.0) end local startPos = imgui.GetCursorPos() local p = imgui.GetCursorScreenPos() imgui.BeginGroup() imgui.PushStyleColor(imgui.Col.Text, text_color) imgui.PushTextWrapPos(imgui.GetCursorPosX() + textWidth) imgui.Text(displayText) imgui.PopTextWrapPos() imgui.PopStyleColor() local textHeight = imgui.GetItemRectSize().y local finalHeight = math.max(textHeight, 28) imgui.SetCursorPos(imgui.ImVec2(imgui.GetWindowContentRegionMax().x - buttonsAreaWidth, startPos.y)) if quest.pinned[0] then imgui.PushStyleColor(imgui.Col.Button, imgui.ImVec4(0.6, 0.5, 0.1, 0.5)) imgui.PushStyleColor(imgui.Col.Text, imgui.ImVec4(1.0, 0.8, 0.2, 1.0)) else imgui.PushStyleColor(imgui.Col.Button, imgui.ImVec4(0.2, 0.2, 0.2, 0.3)) imgui.PushStyleColor(imgui.Col.Text, imgui.ImVec4(0.5, 0.5, 0.5, 1.0)) end if imgui.Button(getIcon('thumbtack') .. "##pin", imgui.ImVec2(24, 24)) then quest.pinned[0] = not quest.pinned[0] end imgui.PopStyleColor(2) imgui.SameLine() imgui.PushStyleColor(imgui.Col.Button, imgui.ImVec4(0.2, 0.2, 0.2, 0.3)) imgui.PushStyleColor(imgui.Col.Text, imgui.ImVec4(0.7, 0.3, 0.3, 1.0)) local hideIcon = quest.show[0] and getIcon('eye_slash') or getIcon('eye') if imgui.Button(hideIcon .. "##hide", imgui.ImVec2(24, 24)) then quest.show[0] = not quest.show[0] end imgui.PopStyleColor(2) local col_u32 = imgui.ColorConvertFloat4ToU32(accent_color) draw_list:AddRectFilled(imgui.ImVec2(p.x - 5, p.y), imgui.ImVec2(p.x - 2, p.y + finalHeight), col_u32) if textHeight < 28 then imgui.SetCursorPosY(startPos.y + 28) end imgui.EndGroup() imgui.Dummy(imgui.ImVec2(0, 4)) imgui.Separator() imgui.Dummy(imgui.ImVec2(0, 4)) else if not quest.show[0] then return end local text_color = imgui.ImVec4(1.0, 1.0, 1.0, 1.0) if quest.pinned[0] and not quest.progress then text_color = imgui.ImVec4(1.0, 0.84, 0.0, 1.0) elseif quest.progress then text_color = imgui.ImVec4(0.5, 0.8, 0.5, 1.0) end imgui.PushStyleColor(imgui.Col.Text, text_color) imgui.PushTextWrapPos(imgui.GetWindowWidth() - 20) imgui.Text(displayText) imgui.PopTextWrapPos() imgui.PopStyleColor() end end function findItemById(array, id) if not array then return nil end for _, item in ipairs(array) do if item.id == id then return item end end return nil end function updateQuestData(array, target, itemData) for index, item in ipairs(array) do if item.id == target.id and item.categoryId == target.categoryId then item.progress = itemData.progress >= target.totalProgress item.curr = itemData.progress item.max = target.totalProgress return true end end return false end function sampev.onServerMessage(color, text) if text:find("%[Боевой Пропуск%]%{ffffff%} Вы успешно выполнили задание") then local result = text:match("'(.-)'") if result then result = u8(result) for _, quest in ipairs(quests) do if quest.title == result then quest.progress = true quest.pinned[0] = false quest.curr = quest.max break end end end end end addEventHandler('onReceivePacket', function (id, bs) if id == 220 then raknetBitStreamIgnoreBits(bs, 8) if (raknetBitStreamReadInt8(bs) == 17) then raknetBitStreamIgnoreBits(bs, 32) local length = raknetBitStreamReadInt16(bs) local encoded = raknetBitStreamReadInt8(bs) local str = (encoded ~= 0) and raknetBitStreamDecodeString(bs, length + encoded) or raknetBitStreamReadString(bs, length) if str:find("event.battlePass.initializeBattlePassData") then local battlePassData = str:match("`(.+)`") if battlePassData then local battlePassDataParsed = decodeJson(battlePassData) if battlePassDataParsed[1] and battlePassDataParsed[1].premium ~= 0 then premiumBP = true end end elseif str:find("event.battlePass.updateQuestsProgress") then local innerList = string.match(str, "%[%[(.-)%]%]") if innerList then innerList = "[" .. innerList .. "]" local data = decodeJson(innerList) if data then for i, item in ipairs(data) do local source = (item.categoryId == "daily" and daily) or (item.categoryId == "premium" and premiumBP and premium) or nil if source then local matchedItem = findItemById(source, item.id) if matchedItem then if not updateQuestData(quests, matchedItem, item) then table.insert(quests, { id = matchedItem.id, categoryId = matchedItem.categoryId, text = u8(matchedItem.description), title = u8(matchedItem.title), progress = item.progress >= matchedItem.totalProgress, curr = item.progress, max = matchedItem.totalProgress, show = new.bool(true), pinned = new.bool(false) }) end end end end end end end end end end) function apply_custom_style() local style = imgui.GetStyle() local colors = style.Colors local clr = imgui.Col local ImVec4 = imgui.ImVec4 style.WindowRounding = 8.0 style.ChildRounding = 6.0 style.FrameRounding = 5.0 style.ItemSpacing = imgui.ImVec2(8, 6) style.WindowPadding = imgui.ImVec2(10, 10) style.ScrollbarSize = 10.0 style.ScrollbarRounding = 9.0 colors[clr.Text] = ImVec4(0.90, 0.90, 0.93, 1.00) colors[clr.WindowBg] = ImVec4(0.12, 0.12, 0.14, 0.98) colors[clr.ChildBg] = ImVec4(0.00, 0.00, 0.00, 0.00) colors[clr.Border] = ImVec4(0.25, 0.25, 0.27, 0.50) colors[clr.FrameBg] = ImVec4(0.18, 0.18, 0.20, 1.00) colors[clr.TitleBg] = ImVec4(0.10, 0.10, 0.12, 1.00) colors[clr.TitleBgActive] = ImVec4(0.10, 0.10, 0.12, 1.00) colors[clr.Button] = ImVec4(0.20, 0.20, 0.23, 1.00) colors[clr.ButtonHovered] = ImVec4(0.25, 0.25, 0.28, 1.00) colors[clr.ButtonActive] = ImVec4(0.15, 0.15, 0.17, 1.00) colors[clr.Header] = ImVec4(0.22, 0.22, 0.25, 1.00) colors[clr.HeaderHovered] = ImVec4(0.26, 0.26, 0.29, 1.00) colors[clr.HeaderActive] = ImVec4(0.20, 0.20, 0.23, 1.00) end