Загрузка данных


local author = 'eroticwwq'
-----------------------------------------------------------------------from @blackw1ndow special for @nyssonoff <3-----------------------------------------------------------------------------------------------------------------
local ev = require('lib.samp.events')
local imgui = require('mimgui')
local ia = require('ADDONS')
local hotkey = require('mimgui_hotkeys')
local ffi = require('ffi')
local encoding = require('encoding')
local faicons = require('fAwesome6')
local inicfg = require('inicfg')
local vkeys = require('vkeys')
local memory = require('memory')
local requests = require('requests')
local socket = require('socket')
encoding.default = 'CP1251'
local u8 = encoding.UTF8
local mX, mY = getScreenResolution()
local wm = require 'windows.message'
local new, str, sizeof = imgui.new, ffi.string, ffi.sizeof
local imgLogo = nil
local dlstatus = require('moonloader').download_status
local updates_url = 'https://raw.githubusercontent.com/blackw1ndow/project_fraktura/refs/heads/main/updates.json'
local script_url = 'https://raw.githubusercontent.com/blackw1ndow/project_fraktura/main/Fraktura.lua'
local permissions_url = 'https://raw.githubusercontent.com/blackw1ndow/project_fraktura/refs/heads/main/permissions.json'

serverPermissions = {}
resp = nil

textures = {
	map = nil,
	cursor = nil,
	marker = nil
}

permissions = {
    tracer     = true,
    fastmap    = true,
    spilkpk    = true,
    carejector = true,
    quickmap   = true,
}

local hourValue = 12
local weatherValue = 1
local servernoe = 0
local active_tab = 1
local zoom = 115.0
local interval = 100

local config = inicfg.load({
    main = {
        timer = false,
        jt = false,
        textlovlya = false,
        text = '',
        texttime = false,
        textcap = false,
        newtimer = false,
        kapcheb = false,
        kapchebX = 240,
        kapchebY = 125,
    },
    additional = {
        captrain = false,
        captrainkey = '[85]',
        customizeddelay = false,
        antibattlepass = false,
        antiwbook = false,
        fd = false,
        dist = 600,
        fdc = 'fogdist',
        settime = false,
        stc = 'stime',
        swc = 'sweather',
        servertime = false,
        timeX = mX / 2,
        timeY = mY / 2,
        timeColor = 'FFFFFF',
        timeStyle = 13,
        timeFont = 'Arial',
        timeSize = 20,
        showseconds = false,
        anothertime = false,
        timeServerPosX = 290,
        timeServerPosY = 100,
        servertimeColor = 'FFFFFF',
        servertimeStyle = 2,
        servertimeSizeX = 0.5,
        servertimeSizeY = 2.2,
        quickmap = false,
        quickmapkey = '[77]',
        domkrat = false,
        domkratkey = '{}',
        adrenaline = false,
        adrenalinekey = '{}',
        sport = false,
        sportkey = '{}',
        sbiv = false,
        sbivkey = '{}',
        popcardoors = false,
        carejector = false,
        wpline = false,
        wplinekey = '{}',
        wptogglekey = '{}',
        fastmap = false,
        fastmapkey = '[77]',
    },
    customization = {
        disableautoupdate = false,
        recolor = false,
    },
    messages = {
        silent = false,
        console = false,
        chat = true,
    },
    captcha = {
        record = 0,
        vcode = 0,
        ncode = 0,
        code = 0,
    }
}, "project_fraktura")

local captcha = ''
local captchaTable = {}
local t = 0
local trainingDlgId = 8812
local trButt = 0
local trInput = ''
local curDlgInput = ''
local trainbuff
local quickmapbuff
local domkratbuff
local adrenalinebuff
local sportbuff
local sbivbuff
local wplinebuff
local fastmapbuff
local wptogglebuff
local lasttry = 0
local prostoahyet = false --А ЗАЧЕМ?
buffer = imgui.new.char[256](u8(config.main.text))
kapchebbX = new.int(config.main.kapchebX)
kapchebbY = new.int(config.main.kapchebY)
fdbuf = imgui.new.char[64](u8(config.additional.fdc))
stbuf = imgui.new.char[64](u8(config.additional.stc))
swbuf = imgui.new.char[64](u8(config.additional.swc))
styleTimeinput = new.int(config.additional.timeStyle)
sizeTimeinput = new.int(config.additional.timeSize)
fontTimeinput = new.char[30](tostring(config.additional.timeFont))
timeColorinput = new.char[8](tostring(config.additional.timeColor))
styleServerTimeinput = new.int(config.additional.servertimeStyle)
sizeServerTimeXinput = new.float(config.additional.servertimeSizeX)
sizeServerTimeYinput = new.float(config.additional.servertimeSizeY)
timeServerPosXinput = new.int(config.additional.timeServerPosX)
timeServerPosYinput = new.int(config.additional.timeServerPosY)
timeServerColorinput = new.char[8](tostring(config.additional.servertimeColor))

local hasWp = false
local wpX, wpY, wpZ = 0.0, 0.0, 0.0
local wpFont = nil

resX, resY = getScreenResolution()
posX, posY = resX / 1.5, resY / 1.5
ffgoodcolor = '22A872'
ffbadcolor = 'A83022' 
goodcolor = '{22A872}'
badcolor = '{A83022}'
ownname = 'Fraktura'
btag = badcolor..'['..ownname..']: {FFFFFF}'
gtag = goodcolor..'['..ownname..']: {FFFFFF}'
version = '1.02'
version_n = 4
menu = 1
update_state = false
messageSettings = false
resetConfig = false
isLauncherActive = false
timeSettings = false
anotherSettings = false

-- Настройки WPLine (когда то в гуишку занесу)
local WP = {
    lineWidth       = 2,
    markerRadius    = 0.4,
    markerSegments  = 48,
    markerWidth     = 2,
    groundOffset    = 0.03,
    startHeight     = 0.05,
    hideDistance    = 200.0,
    showDistance    = true,
    distanceOffsetY = 22,
}

local function wpDist3(x1,y1,z1,x2,y2,z2)
    local dx,dy,dz = x2-x1,y2-y1,z2-z1
    return math.sqrt(dx*dx+dy*dy+dz*dz)
end

local function wpWorldToScreen(x,y,z)
    local a,b,c = convert3DCoordsToScreen(x,y,z)
    if type(a) == "boolean" then
        if not a or b == nil or c == nil then return nil,nil end
        return b,c
    end
    return a,b
end

local function wpInFrontOfCamera(tx,ty,tz)
    local cx,cy,cz = getActiveCameraCoordinates()
    local lx,ly,lz = getActiveCameraPointAt()
    local fx,fy,fz = lx-cx, ly-cy, lz-cz
    local vx,vy,vz = tx-cx, ty-cy, tz-cz
    return (fx*vx+fy*vy+fz*vz) > 0.0
end

local function wpOnScreen(sx,sy)
    local sw,sh = getScreenResolution()
    return sx >= 0 and sx <= sw and sy >= 0 and sy <= sh
end

local function wpGetGroundZ(x,y,z)
    local ok,gz = pcall(getGroundZFor3dCoord, x, y, z+5.0)
    if ok and type(gz) == "number" then return gz end
    return z
end

local function wpDrawCircle(cx,cy,cz)
    local segs = WP.markerSegments
    local r    = WP.markerRadius
    local firstSX, firstSY, prevSX, prevSY
    for i = 0, segs do
        local a  = (2.0 * math.pi) * (i / segs)
        local sx, sy = wpWorldToScreen(cx + math.cos(a)*r, cy + math.sin(a)*r, cz)
        if not (sx and sy) then return end
        if i == 0 then firstSX,firstSY,prevSX,prevSY = sx,sy,sx,sy
        else
            renderDrawLine(prevSX,prevSY,sx,sy, WP.markerWidth, 0xE0FFFFFF)
            prevSX,prevSY = sx,sy
        end
    end
    if firstSX then renderDrawLine(prevSX,prevSY,firstSX,firstSY, WP.markerWidth, 0xE0FFFFFF) end
end

function drawWpTracer()
    local interior
    local ok, val = pcall(getInteriorFromChar, PLAYER_PED)
    interior = ok and val or 0
    if interior ~= 0 then return end

    local px,py,pz = getCharCoordinates(PLAYER_PED)
    local gz = wpGetGroundZ(wpX, wpY, wpZ)
    local tx,ty,tz = wpX, wpY, gz + WP.groundOffset

    local d = wpDist3(px,py,pz, tx,ty,tz)
    if d > WP.hideDistance then return end
    if not wpInFrontOfCamera(tx,ty,tz) then return end

    local startSX, startSY = wpWorldToScreen(px, py, pz + WP.startHeight)
    if not (startSX and startSY) then return end

    local targetSX, targetSY = wpWorldToScreen(tx, ty, tz)
    if not (targetSX and targetSY) then return end
    if not wpOnScreen(targetSX, targetSY) then return end

    renderDrawLine(startSX, startSY, targetSX, targetSY, WP.lineWidth, 0x90FFFFFF)
    wpDrawCircle(tx, ty, tz)

    if WP.showDistance and wpFont then
        local text  = string.format("%.0f m", d)
        local halfW = math.floor((#text * 6) / 2)
        renderFontDrawText(wpFont, text, targetSX - halfW, targetSY - WP.distanceOffsetY, 0xFFFFFFFF)
    end
end

-- Quisquis hoc legerit, in paupertate et ignorantia in perpetuum manebit. Tormentum et ignominia spiritualis hic eos exspectant.

function checkUpdates()
    if config.customization.disableautoupdate then return end

    local ok, response = pcall(requests.get, updates_url)
    if not ok or response.status_code ~= 200 then
        sampfuncsLog("[u] Не удалось проверить обновления!")
        return
    end

    resp = decodeJson(response.text)
    if not resp then
        sampfuncsLog("[u] Ошибка парсинга JSON!")
        return
    end
    if tonumber(resp.info["version_n"]) > version_n then
        oldVersion = true
        msg("Есть обновление! Версия: " .. resp.info["version"], "good")
        msg("Что бы обновить, откройте Настройки.", "good")
    end
end

function checkPermissions()
    local ok, response = pcall(requests.get, permissions_url)

    if not ok or response.status_code ~= 200 then
        sampfuncsLog("[p] Не удалось загрузить список разрешений!")
        return false
    end

    local data = decodeJson(response.text)
    if not data then
        sampfuncsLog("[p] Ошибка парсинга JSON!")
        return false
    end

    serverPermissions = data
    return true
end

function msg(mtext, tag)
    if config.messages.chat then 
        if tag == "good" then 
            sampAddChatMessage(gtag ..""..mtext.."", -1)
        elseif tag == "bad" then
            sampAddChatMessage(btag ..""..mtext.."", -1)
        else
            sampAddChatMessage(mtext, -1)
        end
    elseif config.messages.console then 
        if tag == "good" then 
            sampfuncsLog(gtag .. ""..mtext.."")
        elseif tag == "bad" then
            sampfuncsLog(btag .. ""..mtext.."")
        else
            sampfuncsLog(mtext)
        end
    elseif config.messages.silent then
        return false
    end
end

function setWorldTime(hour)
	hour = tonumber(hour)
    hourValue = tonumber(hour)
	if hour ~= nil and (hour >= 0 and hour <= 23) then
		local bs = raknetNewBitStream()
		raknetBitStreamWriteInt8(bs, hour)
		raknetEmulRpcReceiveBitStream(94, bs)
		raknetDeleteBitStream(bs)
        msg("Время изменено на: "..goodcolor..""..hour.."", "bad")
		return nil
	end
	msg("Используйте: "..goodcolor.."/"..config.additional.stc.."{ffffff} [0 - 23]", "bad")
end

function checkCarParts(model)
    local parts = {
        581,
        509,
        481,
        462,
        521,
        463,
        510,
        522,
        461,
        448,
        468,
        586,
    }
    for i = 1, #parts do
        if parts[i] == model then
            return false
        end
    end
    return true
end

function setWorldWeather(id)
	id = tonumber(id)
    weatherValue = tonumber(id)
	if id ~= nil and (id >= 0 and id <= 45) then
		local bs = raknetNewBitStream()
		raknetBitStreamWriteInt8(bs, id)
		raknetEmulRpcReceiveBitStream(152, bs)
		raknetDeleteBitStream(bs)
        msg("Погода изменена на: "..goodcolor..""..id.."", "bad")
		return nil
	end
	msg("Используйте: "..goodcolor.."/"..config.additional.swc.."{ffffff} [0 - 45]", "bad")
end

function getSelfName()
    result, id = sampGetPlayerIdByCharHandle(PLAYER_PED)
    if result then
        sampSendChat('/cl '..id)
        name = sampGetPlayerNickname(id)
    else
        name = "Unavailable"
    end
    return name
end

function parseServer()
    local raw = sampGetCurrentServerName()
    local city = string.match(raw, '|%s*([^|]+)')
    if city then city = city:match('^(.-)%s*$') end --сиси масиси
    local serverName = city and city or raw

    local perms = serverPermissions[serverName]
    if perms then
        for key, value in pairs(perms) do
            permissions[key] = value
        end
    else
        for key in pairs(permissions) do
            permissions[key] = false
        end
    end

    return serverName
end

function setFogDistance(arg)
    if not prostoahyet then  
        memory.write(5499541, 12044272, 4, false)
        memory.write(8381985, 13213544, 4, false)
        prostoahyet = true
    end
    local dist = arg:match("(%d+)")
    dist = tonumber(dist)
    if type(dist) ~= 'number' or dist > 3600 or dist < 0 then
        msg('Используйте: '..goodcolor..'/'..config.additional.fdc..' [0-3600]', "bad")
    else
        config.additional.dist = dist
        if not isLauncherActive then
            memory.setfloat(12044272, config.additional.dist, false)
            inicfg.save(config, "project_fraktura")
            msg('Установлена дальность прорисовки: '..badcolor..''..config.additional.dist, "good")
        end
    end
end

function drawAvatar(dl, cx, cy, radius, nickname)
    dl:AddCircleFilled(imgui.ImVec2(cx, cy), radius, 0xFF2A2A2A, 32)
    dl:AddCircle(imgui.ImVec2(cx, cy), radius, 0xFF404040, 32, 1.5)
    local letter = string.upper(string.sub(nickname, 1, 1))
    dl:AddText(
        imgui.ImVec2(cx - 4.0, cy - 6.5),
        0xFF4D444A,
        letter
    )
end

function switchButton(label, config_table, config_key)
    imgui.SetCursorPosX(5)
    if ia.ToggleButton(u8(label), imgui.new.bool(config_table[config_key])) then
        config_table[config_key] = not config_table[config_key]
    end
end

function tabButton(label, id, width)
    local is_active = (active_tab == id)
    imgui.SetCursorPosX(3)

    if is_active then
        imgui.PushStyleColor(imgui.Col.Button,       imgui.ImVec4(0.20, 0.10, 0.10, 1.00))
        imgui.PushStyleColor(imgui.Col.ButtonHovered,imgui.ImVec4(0.30, 0.10, 0.10, 1.00))
    else
        imgui.PushStyleColor(imgui.Col.Button,       imgui.ImVec4(0.05, 0.05, 0.05, 0.00))
        imgui.PushStyleColor(imgui.Col.ButtonHovered,imgui.ImVec4(0.30, 0.10, 0.10, 1.00))
    end
    imgui.PushStyleColor(imgui.Col.ButtonActive, imgui.ImVec4(0.40, 0.10, 0.10, 1.00))

    if ia.MaterialButton(label, imgui.ImVec2(width, 38)) then
        active_tab = id
    end

    imgui.PopStyleColor(3)
end

function initResources()
    textures.map    = renderLoadTextureFromFileInMemory(memory.strptr(map_data), #map_data)
    textures.cursor = renderLoadTextureFromFileInMemory(memory.strptr(cursor_data), #cursor_data)
    textures.marker = renderLoadTextureFromFileInMemory(memory.strptr(marker_data), #marker_data)
    wpFont          = renderCreateFont("Tahoma", 10, 5)
    renderTimeFont  = renderCreateFont(config.additional.timeFont, config.additional.timeSize, config.additional.timeStyle)
end

function initHotkeys()
    trainbuff = hotkey.RegisterHotKey('trainbuff', false, decodeJson(config.additional.captrainkey), function()
        lua_thread.create(function()
            if isPlayerActive() then
                if config.additional.captrain then
                    wait(config.additional.customizeddelay and math.random() * (340 - 40) + 40 or 34)
                    showCaptcha()
                end
            end
        end)
    end)
    wplinebuff = hotkey.RegisterHotKey('wplinebuff', false, decodeJson(config.additional.wplinekey), function()
        if config.additional.wpline then
            wpX, wpY, wpZ = getCharCoordinates(PLAYER_PED)
            hasWp = true
        end
    end)
    wptogglebuff = hotkey.RegisterHotKey('wptogglebuff', false, decodeJson(config.additional.wptogglekey), function()
        if config.additional.wpline then hasWp = not hasWp end
    end)
    fastmapbuff  = hotkey.RegisterHotKey('fastmapbuff',  false, decodeJson(config.additional.fastmapkey),  function() end)
    quickmapbuff = hotkey.RegisterHotKey('quickmapbuff', false, decodeJson(config.additional.quickmapkey), function() end)
    domkratbuff  = hotkey.RegisterHotKey('domkratbuff',  false, decodeJson(config.additional.domkratkey), function()
        if config.additional.domkrat and not sampIsCursorActive() and isPlayerPlaying(PLAYER_PED) and isCharInAnyCar(PLAYER_PED) then
            sampSendChat('/domkrat')
        end
    end)
    adrenalinebuff = hotkey.RegisterHotKey('adrenalinebuff', false, decodeJson(config.additional.adrenalinekey), function()
        if config.additional.adrenaline and not sampIsCursorActive() and isPlayerPlaying(PLAYER_PED) and not isCharInAnyCar(PLAYER_PED) then
            sampSendChat('/adrenaline')
        end
    end)
    sportbuff = hotkey.RegisterHotKey('sportbuff', false, decodeJson(config.additional.sportkey), function()
        if config.additional.sport and not sampIsCursorActive() and isPlayerPlaying(PLAYER_PED) and isCharInAnyCar(PLAYER_PED) then
            sampSendChat("/style")
        end
    end)
    sbivbuff = hotkey.RegisterHotKey('sbivbuff', false, decodeJson(config.additional.sbivkey), function()
        if config.additional.sbiv and not sampIsCursorActive() and isPlayerPlaying(PLAYER_PED) and not isCharInAnyCar(PLAYER_PED) then
            sampSendChat("/piss")
        end
    end)
    hotkey.Text.NoKey = u8'Пусто'
    hotkey.Text.WaitForKey = u8'Ждем клавишу...'
end

function unregisterHotkeys()
    hotkey.unRegisterHotKey(trainbuff)
    hotkey.unRegisterHotKey(wplinebuff)
    hotkey.unRegisterHotKey(wptogglebuff)
    hotkey.unRegisterHotKey(fastmapbuff)
    hotkey.unRegisterHotKey(quickmapbuff)
    hotkey.unRegisterHotKey(domkratbuff)
    hotkey.unRegisterHotKey(adrenalinebuff)
    hotkey.unRegisterHotKey(sportbuff)
    hotkey.unRegisterHotKey(sbivbuff)
end

function tickCarDoors()
    if not (isCharInAnyCar(PLAYER_PED) and not isCharOnAnyBike(PLAYER_PED)
        and not isCharInAnyHeli(PLAYER_PED) and not isCharInAnyTrain(PLAYER_PED)
        and not isCharInAnyBoat(PLAYER_PED)) then return end

    local veh = storeCarCharIsInNoSave(PLAYER_PED)
    if checkCarParts(getCarModel(veh)) then
        if config.additional.popcardoors then
            popCarDoor(veh, 2) popCarDoor(veh, 3)
        else
            fixCarDoor(veh, 2) fixCarDoor(veh, 3)
        end
    end
end

function getFirstKey(keyJson) --честное какое-то постное дерьмо которое в реальных условиях не сработает, но мало ли вдруг бля кто-то заумнфый в конфиг зайдёт и хуйни насрет. только в конфиге {} не ставьте, смешно будет, посмеёмся
    local key = decodeJson(keyJson)
    local vk = type(key) == 'table' and key[1] or key
    return (vk and vk > 0) and vk or nil
end

function isPlayerActive()
    return not sampIsCursorActive()
        and isPlayerPlaying(playerHandle)
        and not sampIsChatInputActive()
        and not sampIsDialogActive()
        and not isPauseMenuActive()
end

function tickQuickmap()
    if not config.additional.quickmap then return end
    local vk = getFirstKey(config.additional.quickmapkey)
    if not vk or not isPlayerActive() or not isKeyDown(vk) then return end

    if not isLauncherActive then
        local menuPtr = 0x00BA6748
        writeMemory(menuPtr + 0x33,  1, 1, false)
        wait(0)
        writeMemory(menuPtr + 0x15C, 1, 1, false)
        writeMemory(menuPtr + 0x15D, 1, 5, false)
        writeMemory(menuPtr + 0x64,  4, representFloatAsInt(300.0), false)
        while isKeyDown(vk) do wait(50) end
        writeMemory(menuPtr + 0x32,  1, 1, false)
    else
        return
    end
end

function tickFastmap()
    if not config.additional.fastmap then map_render = false return end
    local vk = getFirstKey(config.additional.fastmapkey)
    map_render = vk and isPlayerActive() and isKeyDown(vk) or false
end

function tickServerTime()
    if not config.additional.servertime then return end
    local date_table = os.date("*t", getTime)
    local ms         = tonumber(string.sub(tostring(math.ceil(socket.gettime() * 1000)), -3))
    local renderTime = os.date("%H:%M:%S", getTime)
    local renderSecondsTime = string.format("%02d:%02d:%02d{%s}:%03d",
        date_table.hour, date_table.min, date_table.sec, config.additional.timeColor, ms)

    local text = config.additional.showseconds and renderSecondsTime or renderTime

    if changePos then
        showCursor(true)
        local cX, cY = getCursorPos()
        renderFontDrawText(renderTimeFont, text, cX, cY, 0xFFFFFFFF, false)
        if isKeyJustPressed(vkeys.VK_LBUTTON) then
            config.additional.timeX, config.additional.timeY = cX, cY
            changePos = false
            msg("Расположение времени сохранено", "good")
            showCursor(false)
        end
    else
        renderFontDrawText(renderTimeFont, text, config.additional.timeX, config.additional.timeY,
            tonumber("0xFF" .. config.additional.timeColor), false)
    end
end

function tickAnotherTime()
    if not config.additional.anothertime then
        if sampTextdrawIsExists(223) then sampTextdrawDelete(223) end
        return
    end
    local date_table = os.date("*t", getTime)
    local ms         = tonumber(string.sub(tostring(math.ceil(socket.gettime() * 1000)), -3))
    local text       = config.additional.showseconds
        and string.format("%02d:%02d:%02d:%03d", date_table.hour, date_table.min, date_table.sec, ms)
        or os.date("%H:%M:%S", getTime)

    sampTextdrawCreate(223, text, config.additional.timeServerPosX, config.additional.timeServerPosY)
    sampTextdrawSetLetterSizeAndColor(223, config.additional.servertimeSizeX, config.additional.servertimeSizeY, "0xFF" .. config.additional.servertimeColor)
    sampTextdrawSetOutlineColor(223, 0.5, 0xFF000000)
    sampTextdrawSetAlign(223, 1)
    sampTextdrawSetStyle(223, config.additional.servertimeStyle)
end

function tickCaptcha()
    local result, button, _, input = sampHasDialogRespond(trainingDlgId)
    if not result then return end
    time2 = string.format("%.3f", os.clock() - captime)
    button, input = trButt, trInput -- от слова жопа

    if button == 1 then
        if input == captcha .. '0' then
            config.captcha.vcode = config.captcha.vcode + 1
            if config.main.newtimer then
                printStyledString('~g~' .. time2 .. ' sec!', 1000, 7)
            else
                msg(string.format('{ffffff}Верный код! [%.3f]', time2), "good")
            end
            if os.clock() - captime < config.captcha.record or config.captcha.record == 0 then
                config.captcha.record = floorStep(os.clock() - captime, 0.001)
            end
        else
            config.captcha.ncode = config.captcha.ncode + 1
            if config.main.newtimer then
                printStyledString('~r~' .. time2 .. ' sec!', 1000, 7)
            else
                msg(string.format('{ffffff}Неверный код! [%.3f] (' .. captcha .. '0|' .. input .. ')', time2), "bad")
            end
        end
    end

    trButt, trInput = 0, ''
    removeTextdraws()
end

function show_arz_notify(type, title, text, time)
    local function escape_js(s)
        return s:gsub("\\", "\\\\"):gsub('"', '\\"')
    end
    local safe_type = escape_js(type)
    local safe_title = escape_js(title)
    local safe_text = escape_js(text)
    local safe_time = tostring(time)
    local str = ('window.executeEvent("event.notify.initialize", "[\\"%s\\", \\"%s\\", \\"%s\\", \\"%s\\"]");'):format(safe_type, safe_title, safe_text, safe_time)
    visualCEF(str, true)
end
function visualCEF(str, is_encoded)
    local bs = raknetNewBitStream()
    raknetBitStreamWriteInt8(bs, 17)
    raknetBitStreamWriteInt32(bs, 0)
    raknetBitStreamWriteInt16(bs, #str)
    raknetBitStreamWriteInt8(bs, is_encoded and 1 or 0)
    if is_encoded then
        raknetBitStreamEncodeString(bs, str)
    else
        raknetBitStreamWriteString(bs, str)
    end
    raknetEmulPacketReceiveBitStream(220, bs)
    raknetDeleteBitStream(bs)
end

function imgui.Link(link, text)
    text = text or link
    local tSize = imgui.CalcTextSize(text)
    local p = imgui.GetCursorScreenPos()
    local DL = imgui.GetWindowDrawList()
    local col = { 0xFFFF7700, 0xFFFF9900 }
    if imgui.InvisibleButton("##" .. link, tSize) then os.execute("explorer " .. link) end
    local color = imgui.IsItemHovered() and col[1] or col[2]
    DL:AddText(p, color, text)
    DL:AddLine(imgui.ImVec2(p.x, p.y + tSize.y), imgui.ImVec2(p.x + tSize.x, p.y + tSize.y), color)
end

function imgui.Theme()
    imgui.SwitchContext()
    local style = imgui.GetStyle()
    local colors = style.Colors
    --==[ STYLE ]==--
    imgui.GetStyle().WindowPadding = imgui.ImVec2(5, 5)
    imgui.GetStyle().FramePadding = imgui.ImVec2(5, 5)
    imgui.GetStyle().ItemSpacing = imgui.ImVec2(5, 5)
    imgui.GetStyle().ItemInnerSpacing = imgui.ImVec2(2, 2)
    imgui.GetStyle().TouchExtraPadding = imgui.ImVec2(0, 0)
    imgui.GetStyle().IndentSpacing = 0
    imgui.GetStyle().ScrollbarSize = 10
    imgui.GetStyle().GrabMinSize = 10

    --==[ BORDER ]==--
    imgui.GetStyle().WindowBorderSize = 1
    imgui.GetStyle().ChildBorderSize = 1
    imgui.GetStyle().PopupBorderSize = 1
    imgui.GetStyle().FrameBorderSize = 1
    imgui.GetStyle().TabBorderSize = 1

    --==[ ROUNDING ]==--
    imgui.GetStyle().WindowRounding = 5
    imgui.GetStyle().ChildRounding = 0
    imgui.GetStyle().FrameRounding = 5
    imgui.GetStyle().PopupRounding = 5
    imgui.GetStyle().ScrollbarRounding = 5
    imgui.GetStyle().GrabRounding = 5
    imgui.GetStyle().TabRounding = 5

    --==[ ALIGN ]==--
    imgui.GetStyle().WindowTitleAlign = imgui.ImVec2(0.5, 0.5)
    imgui.GetStyle().ButtonTextAlign = imgui.ImVec2(0.5, 0.5)
    imgui.GetStyle().SelectableTextAlign = imgui.ImVec2(0.5, 0.5)
    
    --==[ COLORS ]==--
    colors[imgui.Col.Text] = imgui.ImVec4(0.95, 0.96, 0.98, 1.00)
    colors[imgui.Col.TextDisabled] = imgui.ImVec4(0.60, 0.60, 0.60, 1.00)
    colors[imgui.Col.WindowBg] = imgui.ImVec4(0.15, 0.10, 0.10, 1.00)
    colors[imgui.Col.ChildBg] = imgui.ImVec4(0.15, 0.10, 0.10, 1.00)
    colors[imgui.Col.PopupBg] = imgui.ImVec4(0.15, 0.10, 0.10, 0.95)
    colors[imgui.Col.Border] = imgui.ImVec4(0.50, 0.50, 0.50, 0.50)
    colors[imgui.Col.BorderShadow] = imgui.ImVec4(0.00, 0.00, 0.00, 0.00)
    colors[imgui.Col.FrameBg] = imgui.ImVec4(0.25, 0.10, 0.10, 1.00)
    colors[imgui.Col.FrameBgHovered] = imgui.ImVec4(0.40, 0.10, 0.10, 1.00)
    colors[imgui.Col.FrameBgActive] = imgui.ImVec4(0.60, 0.10, 0.10, 1.00)
    colors[imgui.Col.TitleBg] = imgui.ImVec4(0.15, 0.10, 0.10, 1.00)
    colors[imgui.Col.TitleBgActive] = imgui.ImVec4(0.25, 0.10, 0.10, 1.00)
    colors[imgui.Col.TitleBgCollapsed] = imgui.ImVec4(0.15, 0.10, 0.10, 0.75)
    colors[imgui.Col.ScrollbarBg] = imgui.ImVec4(0.15, 0.10, 0.10, 0.60)
    colors[imgui.Col.ScrollbarGrab] = imgui.ImVec4(0.80, 0.20, 0.20, 0.80)
    colors[imgui.Col.ScrollbarGrabHovered] = imgui.ImVec4(0.90, 0.30, 0.30, 0.80)
    colors[imgui.Col.ScrollbarGrabActive] = imgui.ImVec4(0.95, 0.40, 0.40, 1.00)
    colors[imgui.Col.Button] = imgui.ImVec4(0.20, 0.10, 0.10, 1.00)
    colors[imgui.Col.ButtonHovered] = imgui.ImVec4(0.30, 0.10, 0.10, 1.00)
    colors[imgui.Col.ButtonActive] = imgui.ImVec4(0.40, 0.10, 0.10, 1.00)
    colors[imgui.Col.Header] = imgui.ImVec4(0.20, 0.10, 0.10, 1.00)
    colors[imgui.Col.HeaderHovered] = imgui.ImVec4(0.40, 0.10, 0.10, 1.00)
    colors[imgui.Col.HeaderActive] = imgui.ImVec4(0.60, 0.10, 0.10, 1.00)
    colors[imgui.Col.Tab] = imgui.ImVec4(0.20, 0.10, 0.10, 1.00)
    colors[imgui.Col.TabHovered] = imgui.ImVec4(0.40, 0.10, 0.10, 1.00)
    colors[imgui.Col.TabActive] = imgui.ImVec4(0.60, 0.10, 0.10, 1.00)
    colors[imgui.Col.PlotLines] = imgui.ImVec4(0.80, 0.20, 0.20, 1.00)
    colors[imgui.Col.PlotLinesHovered] = imgui.ImVec4(0.90, 0.30, 0.30, 1.00)
    colors[imgui.Col.PlotHistogram] = imgui.ImVec4(0.80, 0.20, 0.20, 1.00)
    colors[imgui.Col.PlotHistogramHovered] = imgui.ImVec4(0.90, 0.30, 0.30, 1.00)
    colors[imgui.Col.TextSelectedBg] = imgui.ImVec4(0.30, 0.60, 0.85, 0.35)
    colors[imgui.Col.DragDropTarget] = imgui.ImVec4(0.85, 0.60, 0.40, 0.90)
    colors[imgui.Col.NavHighlight] = imgui.ImVec4(0.80, 0.30, 0.30, 1.00)
    colors[imgui.Col.NavWindowingHighlight] = imgui.ImVec4(0.90, 0.50, 0.50, 0.70)
    colors[imgui.Col.NavWindowingDimBg] = imgui.ImVec4(0.20, 0.20, 0.25, 0.20)
    colors[imgui.Col.CheckMark] = imgui.ImVec4(0.90, 0.10, 0.10, 1.00)
    colors[imgui.Col.ModalWindowDimBg] = imgui.ImVec4(0.20, 0.20, 0.25, 0.35)

    ffgoodcolor = '22A872'
    ffbadcolor = 'A83022' 
    goodcolor = '{22A872}'
    badcolor = '{A83022}'
    tag = badcolor..'['..ownname..']: {FFFFFF}' 
    gtag = goodcolor..'['..ownname..']: {FFFFFF}'
end

local renderWindow = new.bool()

imgui.OnInitialize(function()
    local glyph_ranges = imgui.GetIO().Fonts:GetGlyphRangesCyrillic()
    imgui.GetIO().Fonts:Clear()
    -- imgLogo = imgui.CreateTextureFromFileInMemory(imgui.new('const char*', logo_data), #logo_data)

    local function mergeIcons(size)
        local cfg = imgui.ImFontConfig()
        cfg.MergeMode = true
        cfg.PixelSnapH = true
        imgui.GetIO().Fonts:AddFontFromMemoryCompressedBase85TTF(
            faicons.get_font_data_base85('solid'), size, cfg, iconRanges)
    end

    iconRanges = imgui.new.ImWchar[3](faicons.min_range, faicons.max_range, 0)

    imgui.GetIO().Fonts:AddFontFromMemoryCompressedBase85TTF(default_compressed_data_base85, 14.0, nil, glyph_ranges)
    mergeIcons(14)
    font_small = imgui.GetIO().Fonts:AddFontFromMemoryCompressedBase85TTF(default_compressed_data_base85, 12.0, nil, glyph_ranges)
    mergeIcons(12)
    font_title = imgui.GetIO().Fonts:AddFontFromMemoryCompressedBase85TTF(bold_compressed_data_base85, 19.0, nil, glyph_ranges)
    mergeIcons(19)

    imgui.GetIO().IniFilename = nil
    imgui.Theme()
end)

local newFrame = imgui.OnFrame(
    function() return renderWindow[0] end,
    function(player)
        imgui.SetNextWindowPos(imgui.ImVec2(posX, posY), imgui.Cond.FirstUseEver, imgui.ImVec2(1, 1))
        imgui.SetNextWindowSize(imgui.ImVec2(520, 360), imgui.Cond.Always)
        imgui.Begin('##main', renderWindow, imgui.WindowFlags.NoResize + imgui.WindowFlags.NoCollapse)
            local dl      = imgui.GetWindowDrawList()
            imgui.BeginChild('##sidebar', imgui.ImVec2(190, -1), false)

            imgui.SetCursorPos(imgui.ImVec2(14, 14))
            imgui.PushFont(font_title)
            imgui.Text(ownname)
            imgui.PopFont()

            imgui.SetCursorPos(imgui.ImVec2(14, 32))
            imgui.Text(u8('v' .. version))
            imgui.SetCursorPosY(58)
            imgui.Separator()
            imgui.Dummy(imgui.ImVec2(0, 15))

            tabButton(u8'  Работа с капчей',       1, 180)
            tabButton(u8'  Визуальные скрипты', 2, 180)
            tabButton(u8'  Помощь в ловле',        3, 180)
            tabButton(u8'  Настройки',                4, 180)

            imgui.SetCursorPosY(imgui.GetWindowHeight() - 56)
            imgui.Separator()

            imgui.SetCursorPosY(imgui.GetWindowHeight() - 40)
            drawAvatar(dl, imgui.GetCursorScreenPos().x + 20, imgui.GetCursorScreenPos().y + 14, 14, nickname)

            imgui.SetCursorPos(imgui.ImVec2(43, imgui.GetWindowHeight() - 42))
            imgui.PushFont(font_title)
            imgui.Text(u8(nickname))
            imgui.PopFont()

            imgui.SetCursorPos(imgui.ImVec2(43, imgui.GetWindowHeight() - 26))
            imgui.PushFont(font_small)
            imgui.Text(u8(server_city .. ' · ' .. date_str))
            imgui.PopFont()

        imgui.EndChild()

        dl:AddLine(
            imgui.ImVec2(imgui.GetWindowPos().x + 195, imgui.GetWindowPos().y + 24),
            imgui.ImVec2(imgui.GetWindowPos().x + 195, imgui.GetWindowPos().y + imgui.GetWindowHeight() - 1),
            0xFF4D444A, 1.0
        )

        dl:AddRectFilled(
            imgui.ImVec2(imgui.GetWindowPos().x + 197, imgui.GetWindowPos().y - 2),
            imgui.ImVec2(imgui.GetWindowPos().x + imgui.GetWindowWidth() + 1, imgui.GetWindowPos().y + imgui.GetWindowHeight() - 2),
            0xFF191926
        )

        imgui.SameLine()
        imgui.SetCursorPosX(200)
        imgui.PushStyleColor(imgui.Col.ChildBg, imgui.ImVec4(0, 0, 0, 0))
        imgui.BeginChild('##content', imgui.ImVec2(-1, -1), false)
        imgui.Dummy(imgui.ImVec2(0, 5))
            if active_tab == 1 then
                SEP_FISRT_COLUMN = 200
                imgui.Separator()
                imgui.Dummy(imgui.ImVec2(0, 5))
                switchButton('Таймер ввода капчи', config.main, 'timer')
                if not isLauncherActive then switchButton('Таймер на экране', config.main, 'newtimer') end -- аРИЗОНА ГОВНО
                switchButton('Время ввода в строке покупки', config.main, 'jt')
                switchButton('Сообщение после покупки имущества', config.main, 'textlovlya')
                if config.main.textlovlya then
                    SEP_FISRT_COLUMN = SEP_FISRT_COLUMN + 80
                    if imgui.InputText('##textbuffer', buffer, sizeof(buffer) - 1) then
                        config.main.text = u8:decode(str(buffer))
                    end
                    switchButton('Время в сообщении', config.main, 'texttime')
                    switchButton('Капча в сообщении', config.main, 'textcap')
                end
                imgui.Dummy(imgui.ImVec2(0, 1))
                imgui.Separator()
                imgui.Dummy(imgui.ImVec2(0, 3))
                switchButton('Центрирование капчи', config.main, 'kapcheb')
                if config.main.kapcheb then
                    SEP_FISRT_COLUMN = SEP_FISRT_COLUMN + 30
                    imgui.PushItemWidth(100)
                    imgui.SetCursorPosX(5)
                    if imgui.InputInt('##kapchebX', kapchebbX, 10, 0) then
                        config.main.kapchebX = tonumber(kapchebbX[0])
                        inicfg.save(config, "project_fraktura")
                    end
                    imgui.SameLine()
                    if imgui.InputInt('##kapchebY', kapchebbY, 10, 0) then
                        config.main.kapchebY = tonumber(kapchebbY[0])
                        inicfg.save(config, "project_fraktura")
                    end
                    imgui.PopItemWidth()
                end
                ia.Hint('##kapcheb', u8'При появлении капчи, она будет выставляться точно по центру', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                switchButton('Тренировка капчи', config.additional, 'captrain')
                if config.additional.captrain then
                    SEP_FISRT_COLUMN = SEP_FISRT_COLUMN + 30
                    imgui.SameLine()
                    if trainbuff:ShowHotKey() then 
                        config.additional.captrainkey = encodeJson(trainbuff:GetHotKey()) 
                        inicfg.save(config, "project_fraktura")
                    end
                    switchButton('Рандомная задержка', config.additional, 'customizeddelay')
                    ia.Hint('##customizeddelay', u8'Задержка после нажатия клавиши открытия капчи', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                end
                imgui.SetCursorPosY(SEP_FISRT_COLUMN)
                imgui.Separator()
            elseif active_tab == 2 then
                SEP_SECOND_COLUMN = 160
                imgui.Separator()
                imgui.Dummy(imgui.ImVec2(0, 5))
                switchButton('Анти баттл-пас', config.additional, 'antibattlepass')
                ia.Hint('##antibp', u8'Не открывает баттл-пас при нажатии на B', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                switchButton('Анти /wbook', config.additional, 'antiwbook')
                ia.Hint('##antiwb', u8'Не открывает меню организации при нажатии на M, команда всё ещё работает', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                if not isLauncherActive then
                    SEP_SECOND_COLUMN = SEP_SECOND_COLUMN + 30
                    switchButton('Fogdist', config.additional, 'fd')
                    ia.Hint('##fogdist', u8'Смена прорисовки по команде в чат. На лаунчере не работает', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                    if config.additional.fd then
                        if not isLauncherActive then
                            imgui.SameLine()
                            imgui.PushItemWidth(90)
                            if imgui.InputText('##fdcommand', fdbuf, sizeof(fdbuf) - 1) then
                                config.additional.fdc = u8:decode(str(fdbuf))
                            end
                            imgui.PopItemWidth()
                            ia.Hint('##fdcom', u8'Команда для смены прорисовки. Для её активации - введите команду, а затем выключите и включите переключатель', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                        end
                    end
                else
                    config.additional.fd = false
                end
                switchButton('Смена погоды и времени', config.additional, 'settime')
                ia.Hint('##settime', u8'Смена времени и погоды', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                if config.additional.settime then
                    SEP_SECOND_COLUMN = SEP_SECOND_COLUMN + 30
                    imgui.PushItemWidth(75)
                    if imgui.InputText('##stcommand', stbuf, sizeof(stbuf) - 1) then
                        config.additional.stc = u8:decode(str(stbuf))
                    end
                    imgui.PopItemWidth()
                    ia.Hint('##stcom', u8'Команда для смены времени. Для её активации - введите команду, а затем выключите и включите переключатель', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                    imgui.SameLine()
                    imgui.PushItemWidth(75)
                    if imgui.InputText('##swcommand', swbuf, sizeof(swbuf) - 1) then
                        config.additional.swc = u8:decode(str(swbuf))
                    end
                    imgui.PopItemWidth()
                    ia.Hint('##swcom', u8'Команда для смены погоды. Для её активации - введите команду, а затем выключите и включите переключатель', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                end
                switchButton('CTime', config.additional, 'servertime')
                ia.Hint('##ctime', u8'ЦТайм, или же время на экран', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                if config.additional.servertime then
                    imgui.SameLine()
                    if imgui.Button(faicons('gear')) then timeSettings = true end
                end
                switchButton('Server time', config.additional, 'anothertime')
                ia.Hint('##servertime', u8'Время на экране, но в другом стиле', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                if config.additional.anothertime then
                    imgui.SameLine()
                    if imgui.Button(faicons('gears')) then anotherSettings = true end
                end
                imgui.SetCursorPosY(SEP_SECOND_COLUMN)
                imgui.Separator()
            elseif active_tab == 3 then
                SEP_THIRD_COLUMN = 280
                imgui.Separator()
                imgui.Dummy(imgui.ImVec2(0, 5))
                if permissions.spilkpk then
                    switchButton('Спиливание передний дверей', config.additional, 'popcardoors') 
                    ia.Hint('##kpkgobombmyhouse', u8'При активации, у транспорта в который вы сядете спилятся двери', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                else
                    config.additional.popcardoors = false
                end
                if permissions.carejector then
                    switchButton('Авто-выкидывание людей из транспорта', config.additional, 'carejector')
                    ia.Hint('##autoejector', u8'При активации, будет выкидывать всех, кто сел в машину', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                else
                    config.additional.carejector = false
                end
                if not isLauncherActive then
                    if permissions.quickmap then
                        switchButton('Быстрое открытие карты', config.additional, 'quickmap')
                        ia.Hint('##quickmap', u8'При зажатии заданной клавиши, у вас откроется мапа', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                        if config.additional.quickmap then
                            imgui.SameLine()
                            if quickmapbuff:ShowHotKey() then 
                                config.additional.quickmapkey = encodeJson(quickmapbuff:GetHotKey()) 
                                inicfg.save(config, "project_fraktura")
                            end
                        end
                    else
                        config.additional.quickmap = false
                    end
                end
                if permissions.fastmap then
                    switchButton('Фаст-мап', config.additional, 'fastmap')
                    ia.Hint('##fastmap', u8'При активации, при зажатии назначенной клавиши будет рендерится карта', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                    if config.additional.fastmap then
                        imgui.SameLine()
                        if fastmapbuff:ShowHotKey() then 
                            config.additional.fastmapkey = encodeJson(fastmapbuff:GetHotKey()) 
                            inicfg.save(config, "project_fraktura")
                        end
                    end
                else
                    config.additional.fastmap = false
                end
                if permissions.tracer then
                    switchButton('Трейсер до метки', config.additional, 'wpline')
                    ia.Hint('##wplinehaha', u8'Рисует трейсер при нажатии на первую клавишу. Вторая включает/выключает её отображение', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
                    if config.additional.wpline then
                        imgui.SameLine()
                        if wplinebuff:ShowHotKey() then 
                            config.additional.wplinekey = encodeJson(wplinebuff:GetHotKey()) 
                            inicfg.save(config, "project_fraktura")
                        end
                        imgui.SameLine()
                        if wptogglebuff:ShowHotKey() then 
                            config.additional.wptogglekey = encodeJson(wptogglebuff:GetHotKey()) 
                            inicfg.save(config, "project_fraktura")
                        end
                    end
                else
                    config.additional.wpline = false
                end
                imgui.Dummy(imgui.ImVec2(0, 1))
                imgui.SetCursorPosY(160)
                imgui.Separator()
                imgui.Dummy(imgui.ImVec2(0, 3))
                switchButton('Хот-кей на домкрат', config.additional, 'domkrat')
                if config.additional.domkrat then
                    imgui.SameLine()
                    if domkratbuff:ShowHotKey() then 
                        config.additional.domkratkey = encodeJson(domkratbuff:GetHotKey()) 
                        inicfg.save(config, "project_fraktura")
                    end
                end
                switchButton('Хот-кей на адреналин', config.additional, 'adrenaline')
                if config.additional.adrenaline then
                    imgui.SameLine()
                    if adrenalinebuff:ShowHotKey() then 
                        config.additional.adrenalinekey = encodeJson(adrenalinebuff:GetHotKey()) 
                        inicfg.save(config, "project_fraktura")
                    end
                end
                switchButton('Хот-кей на спорт-режим', config.additional, 'sport')
                if config.additional.sport then
                    imgui.SameLine()
                    if sportbuff:ShowHotKey() then 
                        config.additional.sportkey = encodeJson(sportbuff:GetHotKey()) 
                        inicfg.save(config, "project_fraktura")
                    end
                end
                switchButton('Хот-кей на сбив анимации', config.additional, 'sbiv')
                if config.additional.sbiv then
                    imgui.SameLine()
                    if sbivbuff:ShowHotKey() then 
                        config.additional.sbivkey = encodeJson(sbivbuff:GetHotKey()) 
                        inicfg.save(config, "project_fraktura")
                    end
                end
                imgui.SetCursorPosY(SEP_THIRD_COLUMN)
                imgui.Separator()
            elseif active_tab == 4 then
                SEP_LAST_COLUMN = 290
                imgui.SetCursorPosX(5)
                imgui.Link("https://www.youtube.com/@Nysson13", u8"Ссылка на мой ютуб-канал")
                imgui.SameLine()
                imgui.SetCursorPosX(240)
                imgui.Link("https://t.me/blackw1ndow", u8"разработчик")
                imgui.Dummy(imgui.ImVec2(0, 2))
                imgui.Separator()
                imgui.Dummy(imgui.ImVec2(0, 5))
                switchButton('Выключить авто-обновления', config.customization, 'disableautoupdate')
                switchButton('Перекраска сообщений о покупке имущества', config.customization, 'recolor')
                imgui.Dummy(imgui.ImVec2(0, 1))
                imgui.Separator()
                imgui.Dummy(imgui.ImVec2(0, 3))
                imgui.Text(u8'Информация по вводу капчи:')
                imgui.Text(u8'Всего капч введено: '..config.captcha.vcode + config.captcha.ncode)
                imgui.Text(u8'Верных кодов: '..config.captcha.vcode)
                imgui.Text(u8'Неверных кодов: '..config.captcha.ncode)
                imgui.Text(u8'Рекорд ввода капчи: '..config.captcha.record)
                imgui.SetCursorPosY(SEP_LAST_COLUMN)
                imgui.Separator()
                imgui.SetCursorPosX(5)
                if imgui.Button(faicons('power_off')) then thisScript():unload() end
                ia.Hint('##offnitvar', u8'Выключить скрипт.') 
                if imgui.Button(faicons('trash'), imgui.SameLine()) then
                    if doesFileExist(thisScript().filename)then
                        os.remove(thisScript().filename)
                    end
                    if doesFileExist('moonloader\\config\\project_fraktura.ini') then
                        os.remove('moonloader\\config\\project_fraktura.ini')
                    end
                    thisScript():unload()
                end
                ia.Hint('##nenadadyadya', u8'Удалить скрипт.') 
                if imgui.Button(faicons('rotate'), imgui.SameLine()) then thisScript():reload() end
                ia.Hint('rebut', u8'Перезагрузить скрипт.') 
                if imgui.Button(faicons('floppy_disk'), imgui.SameLine()) then msg("Настройки успешно сохранены", "good") inicfg.save(config, "project_fraktura")  end
                ia.Hint('##sohrnastr', u8'Сохранить настройки.')  
                if imgui.Button(faicons('file_contract'), imgui.SameLine()) then 
                    if doesFileExist('moonloader\\config\\project_fraktura.ini') then
                        resetConfig = true
                        thisScript():reload()
                    end
                    update_state = true
                    msg("Настройки успешно сброшены", "good")
                end
                ia.Hint('stoknastr', u8'Вернуть скрипт к стандартным настройкам.') 
                if resp and tonumber(resp.info["version_n"]) > version_n then 
                    if ia.MaterialButton(faicons('cloud'), imgui.SameLine()) then 
                        update_state = true
                        msg("Обновляемся, подождите", "good")
                    end
                    ia.Hint('##updaaaaaaate', u8'Кнопка для обновления скрипта, если вы этого ещё не сделали.') 
                end
                if ia.MaterialButton(faicons('comments')..'', imgui.SameLine(285)) then 
                    messageSettings = true
                end  
                ia.Hint('##messagesettings', u8'Кнопка для настройки сообщений', imgui.GetStyle().Colors[imgui.Col.TextDisabled])
            end
        imgui.EndChild()
        imgui.PopStyleColor()
        imgui.End()
        if messageSettings then imgui.OpenPopup(u8'Настройки сообщений в чат или консоль')
            if (imgui.BeginPopupModal(u8'Настройки сообщений в чат или консоль', _, imgui.WindowFlags.AlwaysAutoResize + imgui.WindowFlags.NoMove + imgui.WindowFlags.NoResize + imgui.WindowFlags.NoCollapse)) then
            if imgui.Checkbox(u8'Включить все сообщения в чат', imgui.new.bool(config.messages.chat)) then config.messages.console = false config.messages.silent = false config.messages.chat = not config.messages.chat end
            if imgui.Checkbox(u8'Вывод всех сообщения в консоль', imgui.new.bool(config.messages.console)) then config.messages.chat = false config.messages.silent = false config.messages.console = not config.messages.console end
            if imgui.Checkbox(u8'Выключить любые сообщения от скрипта', imgui.new.bool(config.messages.silent)) then config.messages.chat = false config.messages.console = false  config.messages.silent = not config.messages.silent end
                imgui.Separator()
                if ia.MaterialButton(u8'Закрыть') then
                    imgui.CloseCurrentPopup()
                    messageSettings = false
                end
                imgui.EndPopup()
            end
        end
        if timeSettings then imgui.OpenPopup(u8'Настройки показа времени')
            if (imgui.BeginPopupModal(u8'Настройки показа времени', _, imgui.WindowFlags.AlwaysAutoResize + imgui.WindowFlags.NoMove + imgui.WindowFlags.NoResize + imgui.WindowFlags.NoCollapse)) then
                imgui.Text(u8'Шрифт')
                imgui.PushItemWidth(170)
                imgui.SameLine()
                    if imgui.InputText('##11', fontTimeinput, sizeof(fontTimeinput) - 1) then
                        config.additional.timeFont = u8:decode(str(fontTimeinput))
                        renderTimeFont = renderCreateFont(config.additional.timeFont, config.additional.timeSize, config.additional.timeStyle)
                    end
                    imgui.PopItemWidth()
                    imgui.Text(u8'Стиль')
                    imgui.PushItemWidth(90)
                    imgui.SameLine()
                    if imgui.InputInt('##12', styleTimeinput, 0, 0) then
                        config.additional.timeStyle = tonumber(styleTimeinput[0])
                        renderTimeFont = renderCreateFont(config.additional.timeFont, config.additional.timeSize, config.additional.timeStyle)
                        inicfg.save(config, "project_fraktura")
                    end
                    imgui.PopItemWidth()
                    imgui.Text(u8'Размер')
                    imgui.PushItemWidth(90)
                    imgui.SameLine()
                    if imgui.InputInt('##13', sizeTimeinput, 0, 0) then
                        config.additional.timeSize = tonumber(sizeTimeinput[0])
                        renderTimeFont = renderCreateFont(config.additional.timeFont, config.additional.timeSize, config.additional.timeStyle)
                        inicfg.save(config, "project_fraktura")
                    end
                    imgui.PopItemWidth()
                    imgui.Text(u8'Цвет')
                    imgui.PushItemWidth(90)
                    imgui.SameLine()
                    if imgui.InputText('##14', timeColorinput, sizeof(timeColorinput) - 1, 0) then
                        config.additional.timeColor = u8:decode(str(timeColorinput))
                        inicfg.save(config, "project_fraktura")
                    end
                    imgui.PopItemWidth()
                    if ia.ToggleButton(u8'Показывать миллисекунды', imgui.new.bool(config.additional.showseconds)) then 
                        config.additional.showseconds = not config.additional.showseconds 
                    end
                    if imgui.Button(u8'Изменить расположение') then
                        if styleTimeinput[0] < 0 then styleTimeinput[0] = 0 end
                        if sizeTimeinput[0] < 0 then sizeTimeinput[0] = 0 end
                        changePos = true
                        imgui.CloseCurrentPopup() 
                        timeSettings = false
                        renderWindow[0] = false
                        msg("Измените расположение времени курсором мыши и нажмите ЛКМ", "bad")
                    end
                    if imgui.Button(u8'Закрыть') then
                        imgui.CloseCurrentPopup() 
                        timeSettings = false
                    end
                imgui.EndPopup()
            end
        end
        if anotherSettings then imgui.OpenPopup(u8'Настройки показа серверного времени')
            if (imgui.BeginPopupModal(u8'Настройки показа серверного времени', _, imgui.WindowFlags.AlwaysAutoResize + imgui.WindowFlags.NoMove + imgui.WindowFlags.NoResize + imgui.WindowFlags.NoCollapse)) then
                    if imgui.RadioButtonIntPtr(u8"Стиль 1", styleServerTimeinput, 1, imgui.SameLine()) then
                        config.additional.servertimeStyle = tonumber(styleServerTimeinput[0])
                        inicfg.save(config, "project_fraktura")
                    elseif 
                        imgui.RadioButtonIntPtr(u8"Стиль 2", styleServerTimeinput, 2, imgui.SameLine()) then
                        config.additional.servertimeStyle = tonumber(styleServerTimeinput[0]) 
                        inicfg.save(config, "project_fraktura")
                    elseif
                        imgui.RadioButtonIntPtr(u8"Стиль 3", styleServerTimeinput, 3, imgui.SameLine()) then
                        config.additional.servertimeStyle = tonumber(styleServerTimeinput[0])
                        inicfg.save(config, "project_fraktura")
                    end
                    imgui.Text(u8'Размер X')
                    imgui.PushItemWidth(120)
                    imgui.SameLine()
                    if imgui.InputFloat('##16', sizeServerTimeXinput, 0.1, 0) then
                        config.additional.servertimeSizeX = tonumber(sizeServerTimeXinput[0])
                        inicfg.save(config, "project_fraktura")
                    end
                    imgui.Text(u8'Размер Y')
                    imgui.SameLine()
                    if imgui.InputFloat('##20', sizeServerTimeYinput, 0.1, 0) then
                        config.additional.servertimeSizeY = tonumber(sizeServerTimeYinput[0])
                        inicfg.save(config, "project_fraktura")
                    end
                    imgui.PopItemWidth()
                    imgui.Text(u8'Цвет')
                    imgui.PushItemWidth(90)
                    imgui.SameLine()
                    if imgui.InputText('##17', timeServerColorinput, sizeof(timeServerColorinput) - 1, 0) then
                        config.additional.servertimeColor = u8:decode(str(timeServerColorinput))
                        inicfg.save(config, "project_fraktura")
                    end
                    imgui.PopItemWidth()
                    if ia.ToggleButton(u8'Показывать миллисекунды', imgui.new.bool(config.additional.showseconds)) then 
                        config.additional.showseconds = not config.additional.showseconds 
                    end
                    imgui.PushItemWidth(120)
                    if imgui.InputInt('##18', timeServerPosXinput, 5, 0) then
                        config.additional.timeServerPosX = tonumber(timeServerPosXinput[0])
                        inicfg.save(config, "project_fraktura")
                    end
                    if imgui.InputInt('##19', timeServerPosYinput, 5, 0) then
                        config.additional.timeServerPosY = tonumber(timeServerPosYinput[0])
                        inicfg.save(config, "project_fraktura")
                    end
                    imgui.PopItemWidth()
                    if imgui.Button(u8'Закрыть') then
                        imgui.CloseCurrentPopup() 
                        anotherSettings = false
                    end
                imgui.EndPopup()
            end
        end
    end
)

function main()
    while not isSampAvailable() do wait(0) end

    checkUpdates()
    checkPermissions()
    initResources()
    initHotkeys()

    sampRegisterChatCommand('fraktura', function() 
        nickname    = getSelfName()
        server_city = parseServer()
        date_str    = os.date('%a') .. ', ' .. os.date('%d') .. ' ' .. os.date('%B')
        renderWindow[0] = not renderWindow[0]
    end)

    if thisScript().filename ~= 'Fraktura.lua' then
        os.rename(getGameDirectory() .. "/moonloader/" .. thisScript().filename,
                  getGameDirectory() .. "/moonloader/Fraktura.lua")
    end
    msg('Загружен! Активация: ' .. badcolor .. 'F2 {ffffff}или ' ..  badcolor ..'/fraktura', "bad")

    addEventHandler('onWindowMessage', function(var_msg, wparam, lparam)
        if var_msg == wm.WM_KEYDOWN or var_msg == wm.WM_SYSKEYDOWN then
            if wparam == vkeys.VK_F2 then
                nickname    = getSelfName()
                server_city = parseServer()
                date_str    = os.date('%a') .. ', ' .. os.date('%d') .. ' ' .. os.date('%B')
                renderWindow[0] = not renderWindow[0]
            end
        end
        if var_msg == 0x001C and wparam == 1 then
            hotkey.ActiveKeys = {}
        end
        --if var_msg == 641 or var_msg == 642 or lparam == -1073741809 then hotkey.ActiveKeys = {} end -- один раз китайца выебали так его и назвали, Хуяо
        if var_msg == 0x100 or var_msg == 0x101 then
            if wparam == vkeys.VK_ESCAPE and renderWindow[0] and not isPauseMenuActive() then
                consumeWindowMessage(true, false)
                renderWindow[0] = false
            end
            if config.additional.antibattlepass and wparam == vkeys.VK_B then
                consumeWindowMessage(true, false)
            end
            if config.additional.antiwbook and wparam == vkeys.VK_M then
                consumeWindowMessage(true, false)
            end
        end
    end)

    while true do
        wait(0)
        getTime = os.time() + servernoe

        if update_state then
            downloadUrlToFile(script_url, thisScript().path, function(id, status, err)
                if status == dlstatus.STATUS_ENDDOWNLOADDATA then
                    msg("Скрипт успешно обновлен!", "good")
                    thisScript():reload()
                end
            end)
            break
        end

        tickCarDoors()
        tickQuickmap()
        tickFastmap()
        tickServerTime()
        tickAnotherTime()
        tickCaptcha()

        if config.additional.wpline and hasWp then drawWpTracer() end
        if config.additional.fastmap then renderMap() end
    end
end

function ev.onPassengerSync(player_id, passengersyncdata)
    local nick = sampGetPlayerNickname(player_id)
    if config.additional.carejector and ((os.clock() - lasttry) > (interval/1000)) then
        if player_id ~= select(2,sampGetPlayerIdByCharHandle(PLAYER_HANDLE)) then
            lasttry = os.clock()
            sampSendChat(("/eject %s"):format(player_id))
        end
    end
end

function ev.onShowTextDraw(textdrawId, data)
	if config.main.kapcheb then
		if data.boxColor == -2139062144 and data.backgroundColor == -16777216 and data.letterColor == -6054539 then
			kapchebX = data.position.x
			kapchebY = data.position.y
			for kapchebId = 2000, 2400 do
				if sampTextdrawIsExists(kapchebId) then
					posX, posY = sampTextdrawGetPos(kapchebId)
					if posX < 450 and posY < 170 then
						posX = posX - kapchebX + config.main.kapchebX
						posY = posY - kapchebY + config.main.kapchebY
						letSizeX, letSizeY, color1 = sampTextdrawGetLetterSizeAndColor(kapchebId)
						box, color2, sizeX, sizeY = sampTextdrawGetBoxEnabledColorAndSize(kapchebId)
						if tonumber(box) == tonumber(1) and tonumber(color1) == tonumber(4292993505) and tonumber(color2) == tonumber(4279903282) then
							sizeX = sizeX - kapchebX + config.main.kapchebX + 1
							sampTextdrawSetBoxColorAndSize(kapchebId, box, color2, sizeX, sizeY)
						end
						sampTextdrawSetPos(kapchebId, posX, posY)
					end
				end
			end
			data.position.x,data.position.y = config.main.kapchebX, config.main.kapchebY
			return {textdrawId, data}
		end
	end
end

function ev.onShowDialog(id, style, title, b1, b2, text)
    if title:find("Откуда вы о нас узнали") then
        show_arz_notify('info','Информация','При регистрации на ник Nysson_Inmayhearti ты получишь 50КК на 6 уровне!', 5000)
    end 
    if title:find("Введите ник пригласившего") then
        show_arz_notify('info','Информация','За получением отписывать сюда: @prokachkamirage', 5000)
    end
    if title:find("Проверка на робота") then
        did = id
        start = os.clock()
    end
    if string.match(text, "Текущее время") then -- by valerii kalashnikov
		chislo, mesyac, god = string.match(text, "Сегодняшняя дата: 	{2EA42E}(%d+):(%d+):(%d+)")
		chas, minuti, sekundi = string.match(text, "Текущее время: 	{345690}(%d+):(%d+):(%d+)")
		datetime = {year = god,month = mesyac,day = chislo,hour = chas,min = minuti,sec = sekundi}
		servernoe = tostring(os.time(datetime)) - os.time()
	end
end

function ev.onSendDialogResponse(id, but, lis, input)
    if id == trainingDlgId then
        trButt = but
        trInput = input
    end
    curDlgInput = input
    if id == did then
        time = os.clock() - start
        time1 = string.format("%.3f", time)
        if config.main.timer and time ~= nil then
            msg('You entered captcha: '.. goodcolor ..'['..input..']{FFFFFF}, time: '.. goodcolor ..'['..time1..']{FFFFFF}', "good")
        end
        if config.main.newtimer and time ~= nil then
            printStyledString('~y~'..time1..' sec!', 3000, 7)
        end
    end
end

function ev.onSendCommand(str)
    local cmd, args = str:match('^/(%S+)%s*(.*)')
    if not cmd then return end
    cmd = cmd:lower()

    if config.additional.fd and not isLauncherActive then
        if cmd == config.additional.fdc:lower() then
            setFogDistance(args)
            return false
        end
    end

    if config.additional.settime then
        if cmd == config.additional.stc:lower() then
            setWorldTime(args)
            return false
        end
        if cmd == config.additional.swc:lower() then
            setWorldWeather(args)
            return false
        end
    end
end

function ev.onSendClientJoin(Ver, mod, nick, response, authKey, clientver, unk)
	if clientver == 'Arizona PC' then isLauncherActive = true end
end

function ev.onServerMessage(color, text)
    if text:find('{90EE90}Поздравляем! Теперь этот транспорт принадлежит вам!') then
      config.captcha.vcode = config.captcha.vcode + 1
      if time < config.captcha.record or config.captcha.record == 0 then
          config.captcha.record = floorStep(time, 0.001)
      end
      if config.main.textlovlya then
          if config.main.texttime and config.main.textcap then sampSendChat(string.format('%s [%.3f] ['..curDlgInput..']', config.main.text, time))
              elseif config.main.texttime then sampSendChat(string.format('%s [%.3f]', config.main.text, time))
              elseif config.main.textcap then sampSendChat(string.format('%s ['..curDlgInput..']', config.main.text))
          else sampSendChat((config.main.text)) end
      end
      if config.customization.recolor and config.main.jt and time ~= nil then 
        sampAddChatMessage(string.format(goodcolor ..'Поздравляем! Теперь этот транспорт принадлежит вам! [%.3f]', time), -1)
        return false
    elseif config.main.jt and time ~= nil then 
        sampAddChatMessage(string.format('{90EE90}Поздравляем! Теперь этот транспорт принадлежит вам! [%.3f]', time), -1)
        return false
    elseif config.customization.recolor then
          sampAddChatMessage(goodcolor ..'Поздравляем! Теперь этот транспорт принадлежит вам!', -1)
          return false
      end
    end
    if string.find(text, 'PC LAUNCHER') then isLauncherActive = true return false end
    if text:find("выкинул") then
        if config.additional.carejector then
            config.additional.carejector = false
        end
    end
    if text:find('этот бизнес ваш!') and color == 1941201407 then
      config.captcha.vcode = config.captcha.vcode + 1
      if time < config.captcha.record or config.captcha.record == 0 then
          config.captcha.record = floorStep(time, 0.001)
      end
      if config.main.textlovlya then
          if config.main.texttime and config.main.textcap then sampSendChat(string.format('%s [%.3f] ['..curDlgInput..']', config.main.text, time))
              elseif config.main.texttime then sampSendChat(string.format('%s [%.3f]', config.main.text, time))
              elseif config.main.textcap then sampSendChat(string.format('%s ['..curDlgInput..']', config.main.text))
          else sampSendChat((config.main.text)) end
      end
      if config.customization.recolor and config.main.jt and time ~= nil then 
        sampAddChatMessage(string.format(goodcolor ..'[Информация] {FFFFFF}Поздравляю! Теперь этот бизнес ваш! [%.3f]', time), -1)
        return false
    elseif config.main.jt and time ~= nil then 
        sampAddChatMessage(string.format('{73B461}[Информация] {FFFFFF}Поздравляю! Теперь этот бизнес ваш! [%.3f]', time), -1)
        return false
    elseif config.customization.recolor then
          sampAddChatMessage(goodcolor ..'[Информация] {FFFFFF}Поздравляю! Теперь этот бизнес ваш!', -1)
          return false
      end
    end
    if text:find('этот дом ваш!') and color == 1941201407 then 
      config.captcha.vcode = config.captcha.vcode + 1
      if time < config.captcha.record or config.captcha.record == 0 then
          config.captcha.record = floorStep(time, 0.001)
      end
      if config.main.textlovlya then
          if config.main.texttime and config.main.textcap then sampSendChat(string.format('%s [%.3f] ['..curDlgInput..']', config.main.text, time))
              elseif config.main.texttime then sampSendChat(string.format('%s [%.3f]', config.main.text, time))
              elseif config.main.textcap then sampSendChat(string.format('%s ['..curDlgInput..']', config.main.text))
          else sampSendChat((config.main.text)) end
      end
      if config.customization.recolor and config.main.jt and time ~= nil then 
        sampAddChatMessage(string.format(goodcolor ..'[Информация] {FFFFFF}Поздравляю! Теперь этот дом ваш! [%.3f]', time), -1)
        return false
    elseif config.main.jt and time ~= nil then 
        sampAddChatMessage(string.format('{73B461}[Информация] {FFFFFF}Поздравляю! Теперь этот дом ваш! [%.3f]', time), -1)
        return false
    elseif config.customization.recolor then
          sampAddChatMessage(goodcolor ..'[Информация] {FFFFFF}Поздравляю! Теперь этот дом ваш!', -1)
          return false
      end
    end
    if text:find('Ответ неверный!') and color == -10270721 then 
      config.captcha.ncode = config.captcha.ncode + 1
      if config.customization.recolor and config.main.jt and time ~= nil then
          sampAddChatMessage(string.format(badcolor ..'[Ошибка] {FFFFFF}Ответ неверный! [%.3f]', time), -1)
          return false
      elseif config.main.jt and time ~= nil then
          sampAddChatMessage(string.format('{FF6347}[Ошибка] {FFFFFF}Ответ неверный! [%.3f]', time), -1)
          return false
      elseif config.customization.recolor then
          sampAddChatMessage(badcolor ..'[Ошибка] {FFFFFF}Ответ неверный!', -1)
          return false
      end
    end
    if text:find('Неверный код!') and color == -10270721 then 
      config.captcha.ncode = config.captcha.ncode + 1
      if config.customization.recolor and config.main.jt and time ~= nil then 
          sampAddChatMessage(string.format(badcolor ..'[Ошибка] {FFFFFF}Неверный код! [%.3f]', time), -1)
          return false
      elseif config.main.jt and time ~= nil then 
          sampAddChatMessage(string.format('{FF6347}[Ошибка] {FFFFFF}Неверный код! [%.3f]', time), -1)
          return false
      elseif config.customization.recolor then
          sampAddChatMessage(badcolor ..'[Ошибка] {FFFFFF}Неверный код!', -1)
          return false
      end
    end
    if text:find('Этот дом уже куплен!') and color == -10270721 then 
      if config.customization.recolor and config.main.jt and time ~= nil then 
          sampAddChatMessage(string.format(badcolor ..'[Ошибка] {FFFFFF}Этот дом уже куплен! [%.3f]', time), -1)
          return false
      elseif config.main.jt and time ~= nil then 
          sampAddChatMessage(string.format('{FF6347}[Ошибка] {FFFFFF}Этот дом уже куплен! [%.3f]', time), -1)
          return false
      elseif config.customization.recolor then
          sampAddChatMessage(badcolor ..'[Ошибка] {FFFFFF}Этот дом уже куплен!', -1)
          return false
      end
    end
    if text:find('Этот дом уже кем то куплен!') and color == -10270721 then 
      if config.customization.recolor and config.main.jt and time ~= nil then
          sampAddChatMessage(string.format(badcolor ..'[Ошибка] {FFFFFF}Этот дом уже кем то куплен! [%.3f]', time), -1)
          return false
      elseif config.main.jt and time ~= nil then
          sampAddChatMessage(string.format('{FF6347}[Ошибка] {FFFFFF}Этот дом уже кем то куплен! [%.3f]', time), -1)
          return false
      elseif config.customization.recolor then
          sampAddChatMessage(badcolor ..'[Ошибка] {FFFFFF}Этот дом уже кем то куплен!', -1)
          return false
      end
    end
    if text:find('Этот бизнес уже кем то куплен') and (color == -10270721 or color == -1347440641) then
      if config.customization.recolor and config.main.jt and time ~= nil then
          sampAddChatMessage(string.format(badcolor ..'[Ошибка] {FFFFFF}Этот бизнес уже кем то куплен! [%.3f]', time), -1)
          return false
      elseif config.main.jt and time ~= nil then
          sampAddChatMessage(string.format('{FF6347}[Ошибка] {FFFFFF}Этот бизнес уже кем то куплен! [%.3f]', time), -1)
          return false
      elseif config.customization.recolor then
          sampAddChatMessage(badcolor ..'[Ошибка] {FFFFFF}Этот бизнес уже кем то куплен!', -1)
          return false
      end
    end
    if text:find('Не флуди!') and color == -10270721 then
      if config.customization.recolor then
          sampAddChatMessage(badcolor ..'[Ошибка] {FFFFFF}Не флуди!', -1)
          return false
      end
    end
    if text:find('Вы продали ваш бизнес') and color == 1941201407 then
      if config.customization.recolor then
          sampAddChatMessage(goodcolor ..'[Информация] {FFFFFF}Вы продали ваш бизнес!', -1)
          return false
      end
    end
    if text:find('Вы продали ваш дом') and color == 1941201407 then
      if config.customization.recolor then
          sampAddChatMessage(goodcolor ..'[Информация] {FFFFFF}Вы продали ваш дом!', -1)
          return false
      end
    end
    if text:find('Этот дом только недавно слетел, он будет доступен для покупки в течении 3 часов.') and color == -10270721 then
      if config.customization.recolor then
          sampAddChatMessage(badcolor ..'[Ошибка] {FFFFFF}Этот дом только недавно слетел, он будет доступен для покупки в течении 3 часов.', -1)
          return false
      end
    end
    if text:find('Этот бизнес только недавно слетел, он будет доступен для покупки в течении 3 часов.') and color == -10270721 then
      if config.customization.recolor then
          sampAddChatMessage(badcolor ..'[Ошибка] {FFFFFF}Этот дом только недавно слетел, он будет доступен для покупки в течении 3 часов.', -1)
          return false
      end
    end
    if text:find('Капча введена верно!') and color == 1941201407 then
      if config.customization.recolor then
          sampAddChatMessage(goodcolor ..'[Информация] {FFFFFF}Капча введена верно!', -1)
          return false
      end
    end
end

function floorStep(num, step)
    return num - num % step
end

function showCaptcha()
    removeTextdraws()
    math.randomseed(os.time() * os.clock() * os.clock() * math.random(1, 9999))

    local r = math.random()^0.4
    if math.random(0, 1) == 1 then r = 1 - r end
    local scale = 0.93 + r * (1.06 - 0.94)

    t = t + 1
    sampTextdrawCreate(t, "LD_SPAC:white", 240, 116)
    sampTextdrawSetLetterSizeAndColor(t, 0, 7.3 * scale, 0x80808080)
    sampTextdrawSetBoxColorAndSize(t, 1, 0xFF1A2432, 404, 0.000000)

    t = t + 1
    sampTextdrawCreate(t, "LD_SPAC:white", 242, 119)
    sampTextdrawSetLetterSizeAndColor(t, 0, 6.5 * scale, 0x80808080)
    sampTextdrawSetBoxColorAndSize(t, 1, 0xFF759DA3, 400, 0.000000)

    nextPos = math.random() * (31 - 30) - 30.5

    math.randomseed(os.time() * os.clock())
    for i = 1, 4 do
        a = math.random(0, 9)
        table.insert(captchaTable, a)
        captcha = captcha..a
    end

    for i = 0, 4 do
        nextPos = nextPos + math.random() * (31 - 30) + 29.5
        t = t + 1
        sampTextdrawCreate(t, "usebox", 259 + nextPos, 124)
        sampTextdrawSetLetterSizeAndColor(t, 0, (math.random() * (5.3 - 5) + 5) * scale, 0x80808080)
        sampTextdrawSetBoxColorAndSize(t, 1, 0xFF1A2432, 30, math.random() * (26.5 - 25) + 25)
        sampTextdrawSetAlign(t, 2)
        if i < 4 then GenerateTextDraw(captchaTable[i + 1], 259 + nextPos, 124, 3 + i * 2, scale)
        else GenerateTextDraw(0, 259 + nextPos, 124, 3 + i * 10, scale) end
    end
    captchaTable = {}
    sampShowDialog(trainingDlgId, '{F89168}Тренировка капчи', '{FFFFFF}Введите {C6FB4A}5{FFFFFF} символов, которые\nпоявились у Вас {C6FB4A}на{FFFFFF} экране.', 'Принять', 'Отмена', 1)
    captime = os.clock()
end

function removeTextdraws()
  if t > 0 then
    for i = 1, t do sampTextdrawDelete(i) end
    t = 0
    captcha = ''
    captime = nil
  end
end

function GenerateTextDraw(id, PosX, PosY, _, scale)
    scale = scale or 1.0

    if id == 0 then
        t = t + 1
        sampTextdrawCreate(t, "LD_SPAC:white", PosX - 5, PosY + 5)
        sampTextdrawSetLetterSizeAndColor(t, 0, (math.random()*(4-3)+3)*scale, 0x80808080)
        sampTextdrawSetBoxColorAndSize(t, 1, 0xFF759DA3, PosX+8, 0.000000)

    elseif id == 1 then
        for i = 0, 1 do
            t = t + 1
            if i == 0 then offsetX=2; offsetBX=17 else offsetX=-3; offsetBX=-17 end
            sampTextdrawCreate(t, "LD_SPAC:white", PosX-offsetX, PosY)
            sampTextdrawSetLetterSizeAndColor(t, 0, 5.7*scale, 0x80808080)
            sampTextdrawSetBoxColorAndSize(t, 1, 0xFF759DA3, PosX-offsetBX, 0.000000)
        end

    elseif id == 2 then
        for i = 0, 1 do
            t = t + 1
            if i == 0 then offsetX=-8; offsetY=8; offsetBX=16
            else offsetX=8; offsetY=28; offsetBX=-15 end
            sampTextdrawCreate(t, "LD_SPAC:white", PosX-offsetX, PosY+offsetY)
            sampTextdrawSetLetterSizeAndColor(t, 0, 0.85*scale, 0x80808080)
            sampTextdrawSetBoxColorAndSize(t, 1, 0xFF759DA3, PosX-offsetBX, 0.000000)
        end

    elseif id == 3 then
        for i = 0, 1 do
            t = t + 1
            if i == 0 then size=1.1; offsetY=6 else size=1.3; offsetY=25 end
            sampTextdrawCreate(t, "LD_SPAC:white", PosX+10, PosY+offsetY)
            sampTextdrawSetLetterSizeAndColor(t, 0, size*scale, 0x80808080)
            sampTextdrawSetBoxColorAndSize(t, 1, 0xFF759DA3, PosX-16.5, 0.000000)
        end

    elseif id == 4 then
        for i = 0, 1 do
            t = t + 1
            if i == 0 then size=1.6; offsetX=-10; offsetY=0; offsetBX=7
            else size=2.75; offsetX=-10; offsetY=25; offsetBX=16 end
            sampTextdrawCreate(t, "LD_SPAC:white", PosX-offsetX, PosY+offsetY)
            sampTextdrawSetLetterSizeAndColor(t, 0, size*scale, 0x80808080)
            sampTextdrawSetBoxColorAndSize(t, 1, 0xFF759DA3, PosX-offsetBX, 0.000000)
        end

    elseif id == 5 then
        for i = 0, 1 do
            t = t + 1
            if i == 0 then size=0.8; offsetX=6; offsetY=7; offsetBX=-15
            else size=1.2; offsetX=-10; offsetY=26; offsetBX=16 end
            sampTextdrawCreate(t, "LD_SPAC:white", PosX-offsetX, PosY+offsetY)
            sampTextdrawSetLetterSizeAndColor(t, 0, size*scale, 0x80808080)
            sampTextdrawSetBoxColorAndSize(t, 1, 0xFF759DA3, PosX-offsetBX, 0.000000)
        end

    elseif id == 6 then
        for i = 0, 1 do
            t = t + 1
            if i == 0 then size=1; offsetX=7.5; offsetY=7; offsetBX=-15
            else size=1.1; offsetX=-10; offsetY=math.random()*(29-27)+27; offsetBX=10 end
            sampTextdrawCreate(t, "LD_SPAC:white", PosX-offsetX, PosY+offsetY)
            sampTextdrawSetLetterSizeAndColor(t, 0, size*scale, 0x80808080)
            sampTextdrawSetBoxColorAndSize(t, 1, 0xFF759DA3, PosX-offsetBX, 0.000000)
        end

    elseif id == 7 then
        t = t + 1
        sampTextdrawCreate(t, "LD_SPAC:white", PosX-14, PosY+6)
        sampTextdrawSetLetterSizeAndColor(t, 0, 5.1*scale, 0x80808080)
        sampTextdrawSetBoxColorAndSize(t, 1, 0xFF759DA3, PosX+7, 0.000000)

    elseif id == 8 then
        local sizefor8 = math.random()*(9-8)+8
        for i = 0, 1 do
            t = t + 1
            if i == 0 then size=1.275; offsetY=8 else size=1.2; offsetY=28 end
            sampTextdrawCreate(t, "LD_SPAC:white", PosX+sizefor8, PosY+offsetY)
            sampTextdrawSetLetterSizeAndColor(t, 0, size*scale, 0x80808080)
            sampTextdrawSetBoxColorAndSize(t, 1, 0xFF759DA3, PosX-6, 0.000000)
        end

    elseif id == 9 then
        for i = 0, 1 do
            t = t + 1
            if i == 0 then size=1.25; offsetY=7; offsetBX=7
            else size=1.275; offsetY=26; offsetBX=16 end
            sampTextdrawCreate(t, "LD_SPAC:white", PosX+7, PosY+offsetY)
            sampTextdrawSetLetterSizeAndColor(t, 0, size*scale, 0x80808080)
            sampTextdrawSetBoxColorAndSize(t, 1, 0xFF759DA3, PosX-offsetBX, 0.000000)
        end
    end
end

function onScriptTerminate(script, quitGame)
    if script == thisScript() then
        inicfg.save(config, "project_fraktura")
    end
    if resetConfig then 
        if doesFileExist('moonloader\\config\\project_fraktura.ini') then
            os.remove('moonloader\\config\\project_fraktura.ini')
        end
    end
end

function onQuitGame()
    inicfg.save(config, "project_fraktura")
end

function isPlayerInWorld(interior_id)
	local ip, port = sampGetCurrentServerAddress()
	local address = ("%s:%s"):format(ip, port)
	if address == "80.66.82.147:7777" then -- Vice City
		return (interior_id == 20)
	end
	return (interior_id == 0)
end

function join_argb(a, r, g, b) local argb = b argb = bit.bor(argb, bit.lshift(g, 8)) argb = bit.bor(argb, bit.lshift(r, 16)) argb = bit.bor(argb, bit.lshift(a, 24)) return argb end
function explode_argb(argb) local a = bit.band(bit.rshift(argb, 24), 0xFF) local r = bit.band(bit.rshift(argb, 16), 0xFF) local g = bit.band(bit.rshift(argb, 8), 0xFF) local b = bit.band(argb, 0xFF) return a, r, g, b end
function convertCoord(xx,yy)
	local function limitToMap(x, y)
		if x > 3000 then
			x = 3000
		elseif x < -3000 then
			x = -3000
		end
		if y > 3000 then
			y = 3000
		elseif y < -3000 then
			y = -3000
		end
		return x, y
	end

	local mapSize = {getMapSize()}

	local multiplier = mapSize[3] / 6000

	local bottom_y = mapSize[2] + mapSize[3]
	local bottom_x = mapSize[1] + mapSize[3]
	local wx, wy = limitToMap((xx), (yy))
	local x = mapSize[1] + (wx + 3000) * multiplier
	local y = mapSize[2] + mapSize[3] - (wy + 3000) * multiplier
	return x,y
end

function getMapSize()
	local sh, sw = getScreenResolution()
	local map_size = math.floor(sw * 0.8)
	sw = sw - (zoom*4.5)
	map_size = map_size + zoom
	local top_x = sh / 2 - map_size / 2
	local top_y = sw * 0.1

	return top_x,top_y,map_size
end

function SearchMarker(isRace)
    local ret_posX = 0.0
    local ret_posY = 0.0
    local ret_posZ = 0.0
    local isFind = false

    for id = 0, 31 do
        local MarkerStruct = 0
        if isRace then MarkerStruct = 0xC7F168 + id * 56
        else MarkerStruct = 0xC7DD88 + id * 160 end
        local MarkerPosX = representIntAsFloat(readMemory(MarkerStruct + 0, 4, false))
        local MarkerPosY = representIntAsFloat(readMemory(MarkerStruct + 4, 4, false))
        local MarkerPosZ = representIntAsFloat(readMemory(MarkerStruct + 8, 4, false))

        if MarkerPosX ~= 0.0 or MarkerPosY ~= 0.0 or MarkerPosZ ~= 0.0 then
            ret_posX = MarkerPosX
            ret_posY = MarkerPosY
            ret_posZ = MarkerPosZ
            isFind = true
        end
    end

    return isFind, ret_posX, ret_posY, ret_posZ
end

function renderMap(mode)
	if map_render then
		if not sampIsDialogActive() and not sampIsScoreboardOpen() then
			local mapSize = {getMapSize()}

			local myPos = {getCharCoordinates(playerPed)}

			renderDrawTexture(textures.map, mapSize[1], mapSize[2], mapSize[3], mapSize[3], 0, join_argb(155,255,255,255))

			local my_x,my_y = convertCoord(myPos[1],myPos[2])
			renderDrawTexture(textures.cursor, my_x-7.5,my_y-7.5, 15,15, (getCharHeading(PLAYER_PED)-getCharHeading(PLAYER_PED)-360-getCharHeading(PLAYER_PED)), join_argb(255,255,255,255))

			local blip = {getTargetBlipCoordinates()}
			if blip[1] then
				local x,y = convertCoord(blip[2],blip[3])
				renderDrawTexture(textures.marker, x-12.5,y-12.5, 25,25, 0, join_argb(155,255,255,255))	
				-- renderDrawBox(x-1, y-1, 12, 12, 0xFF000000)
				-- renderDrawBox(x, y, 10, 10, join_argb(155,204,0,0))
			end

			local marker = {SearchMarker(true)}
			if marker[1] then
				local marker_x,marker_y = convertCoord(marker[2],marker[3])
				renderDrawBox(marker_x-1, marker_y-1, 12, 12, 0xFF000000)
				renderDrawBox(marker_x, marker_y, 10, 10, join_argb(155,250,60,60))
			end

		end

	end
end