Помогите разобраться со скриптом

legadega

Новичок
Автор темы
4
0
Версия MoonLoader
.027.0-preview
Нашел помощника на садовода Rodina rp,такой вопрос когда остается в инветаре 100 саженцев,он больше не сажает сам.и не как не запускается пока не купешь еще саженцев.вот код


garnen:
local sampEvents = require("samp.events")
local vkeys = require("vkeys")
local gkeys = require("game.keys")
local imgui = require("mimgui")
local ffi = require("ffi")
local faicons = require("fAwesome6")
local wm = require("windows.message")
local encoding = require("encoding")
local u8 = encoding.UTF8
encoding.default = "CP1251"

ffi.cdef[[
void mouse_event(int dwFlags, int dx, int dx, int cButtons, int dwExtraInfo);
]]

local MOUSEEVENTF_LEFTDOWN = 0x0002
local MOUSEEVENTF_LEFTUP = 0x0004

state = true
click = false
last_click_time = 0

function sendLeftClick()
    ffi.C.mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
    ffi.C.mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
end

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

    while true do
        wait(50)
        if click then
            local current_time = os.clock()
            if (current_time - last_click_time) > 0.1 then
                sendLeftClick()
                last_click_time = current_time
            end
        end
    end
end

do
    function chat(...)
        sampAddChatMessageEx(0x1F618D, {
            { "{HEX}", "{1F618D}" },
            { "{}", "{ebebeb}" },
            { "%[", "[{1F618D}"},
            { "%]", "{ebebeb}]" },
            { "%(", "({1F618D}" },
            { "%)", "{ebebeb})" }
        }, "|{}", ...)
    end

    function sampAddChatMessageEx(color, patterns, ...)
        local output
        for key, value in pairs({...}) do
            if output then
                output = string.format("%s %s", output, tostring(value))
            else
                output = tostring(value)
            end
        end
        for key, value in ipairs(patterns) do
            if string.match(output, value[1]) then
                output = string.gsub(output, value[1], value[2])
            end
        end
        sampAddChatMessage(output, color)
    end
end

local cef = (function()
    local this = {}

    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)
                this:onDisplay(str)
            end
        end
    end)

    addEventHandler("onSendPacket", function (id, bs, priority, reliability, orderingChannel)
        if id == 220 then
            local id = raknetBitStreamReadInt8(bs)
            local packettype = raknetBitStreamReadInt8(bs)
            local strlen = raknetBitStreamReadInt16(bs)
            local str = raknetBitStreamReadString(bs, strlen)
            if packettype ~= 0 and packettype ~= 1 and #str > 2 then
                this:onSend(str)
            end
        end
    end)

    function this:convertJs(data)
        data = data:gsub("(.?)([A-z]-): ", "%1\"%2\": "):gsub("['`]", "\"")
        for value in data:gmatch(":\"(.-)\"[,$]") do
            local new = value:gsub("\n", "\\n"):gsub("\"\":", ":"):gsub("\"", "\\\"")
            data = data:gsub(value, new)
        end
        return pcall(decodeJson, u8(data))
    end

    function this:onDisplay(str)
        return str
    end

    function this:onSend(str)
        return str
    end

    return this
end)()

local sampEventHandler = (function()
    local this = {
        __samp = require("samp.events"),
        __events = {},
        __meta = { __index = {} }
    }

    function this.__meta.__index:unsubscribe()
        this:unsubscribe(self.event, self.handler)
    end

    function this:subcribe(event, handler)
        if not self.__events[event] then
            self.__events[event] = {}
            self.__samp[event] = function(...)
                for index, data in ipairs(self.__events[event]) do
                    local result = data.handler(...)
                    if type(result) == "table" or type(result) == "boolean" then
                        return result
                    end
                end
            end
        end
        local new = setmetatable({ event = event, handler = handler }, self.__meta)
        table.insert(self.__events[event], new)
        return new
    end

    function this:unsubscribe(event, handler)
        if not self.__events[event] then
            return false
        end

        for index, data in ipairs(self.__events[event]) do
            if data.handler == handler then
                table.remove(self.__events[event], index)
                break
            end
        end

        if #self.__events[event] == 0 then
            self.__events[event] = nil
            self.__samp[event] = nil
        end

        return true
    end

    function this:call(...)
        return self:subcribe(...)
    end

    return setmetatable(this, { __call = this.call })
end)()

local json = (function()
    local this = {
        path = getWorkingDirectory() .. "\\config"
    }

    if not doesDirectoryExist(this.path) then
        createDirectory(this.path)
    end

    function this:read()
        if not doesFileExist(self.__path) then
            return setmetatable(self.__template, getmetatable(self))
        end

        local file = io.open(self.__path, "r+")
        if not file then
            return false
        end

        local result, data = pcall(decodeJson, file:read("*a"))
        file:close()

        return result and setmetatable(type(data) == "table" and data or {}, getmetatable(self))
    end

    function this:save()
        local result, content = pcall(encodeJson, self)
        if not result then
            return false
        end

        local file = io.open(self.__path, "w+")
        if not file then
            return false
        end
        file:write(content)
        file:close()

        return true
    end

    function this:new(path, template)
        local metatable = {
            __index = {
                __path = path,
                __template = template
            }
        }

        metatable.__index.read = self.read
        metatable.__index.save = self.save

        return setmetatable({}, metatable):read()
    end

    return setmetatable(this, { __call = this.new })
end)()

local mimguiFrame = (function()
    local this = {
        frames = {},
        metatable = {
            __index = {}
        }
    }

    function this.metatable.__index:condition()
        return self.status[0]
    end

    function this.metatable.__index:isActive()
        return self.pointerFrame and self.pointerFrame:IsActive()
    end

    function this.metatable.__index:switch(status)
        if type(status) == "boolean" then
            self.status[0] = status
        else
            self.status[0] = not self.status[0]
        end
    end

    function this.metatable.__index:subscribe()
        if self.unsubscribedFrame and self.draw then
            self.pointerFrame = imgui.OnFrame(
                function() return self:condition() end,
                (self.draw and self.before) and function(data) self:before(data) end or function(data) self:draw(data) end,
                (self.draw and self.before) and function(data) self:draw(data) end
            )
            self.unsubscribedFrame = false
            table.insert(this.frames, self)
            return true
        else
            return false
        end
    end

    function this.metatable.__index:unsubscribe()
        if self.unsubscribedFrame then
            return false
        else
            if self.pointerFrame then
                self.pointerFrame:Unsubscribe()
            end
            for index, frame in ipairs(this.frames) do
                if frame == self then
                    table.remove(this.frames, index)
                    break
                end
            end
            self.unsubscribedFrame = true
            return true
        end
    end

    function this.metatable:__newindex(key, value)
        if key == "draw" then
            self.pointerFrame = imgui.OnFrame(
                function() return self:condition() end,
                (self.before) and function(data) self:before(data) end or function(data) self:draw(data) end,
                (self.before) and function(data) self:draw(data) end
            )
        end
        rawset(self, key, value)
    end

    function this:close(status)
        local weight = -1
        for index, frame in ipairs(self.frames) do
            if frame.status[0] and frame.weight ~= 0 and frame.weight > weight then
                weight = frame.weight
            end
        end
        if weight ~= -1 and status then
            return true, 0
        end
        for index, frame in ipairs(self.frames) do
            if frame.status[0] and frame.weight == weight then
                frame.status[0] = false
            end
        end
        return weight ~= -1, 1
    end

    function this:insert(frame)
        frame = type(frame) == "table" and frame or {}
        frame.status = frame.status or imgui.new.bool(false)
        frame.weight = frame.weight or 1
        frame.unsubscribedFrame = false
        setmetatable(frame, self.metatable)
        table.insert(self.frames, frame)
        return frame
    end

    addEventHandler("onWindowMessage", function(message, wparameters, lparameters)
        local down = message == wm.WM_KEYDOWN
        if down or message == wm.WM_KEYUP then
            if wparameters == vkeys.VK_ESCAPE and this:close(down) then
                consumeWindowMessage(true, false)
            end
        end
    end)

    return setmetatable(this, { __call = this.insert })
end)()

local applyCustomStyle = (function()
    local this = {
        static = {
        [imgui.Col.ChildBg]              = { 0.15, -1.00 },
        [imgui.Col.FrameBg]              = { 0.20, 0.20 },
        [imgui.Col.FrameBgHovered]       = { 0.30, 0.20 },
        [imgui.Col.FrameBgActive]        = { 0.45, 0.30 },
        [imgui.Col.WindowBg]             = { 0.00, 0.00 },
        [imgui.Col.PopupBg]              = { 0.08, 0.94 },
        [imgui.Col.Border]               = { 0.43, 0.50 },
        [imgui.Col.BorderShadow]         = { 0.00, 0.00 },
        [imgui.Col.TitleBg]              = { 0.04, 1.00 },
        [imgui.Col.TitleBgActive]        = { 0.16, 1.00 },
        [imgui.Col.TitleBgCollapsed]     = { 0.00, 0.60 },
        [imgui.Col.MenuBarBg]            = { 0.14, 1.00 },
        [imgui.Col.ScrollbarBg]          = { 0.02, 0.53 },
        [imgui.Col.ScrollbarGrab]        = { 0.31, 1.00 },
        [imgui.Col.ScrollbarGrabHovered] = { 0.41, 1.00 },
        [imgui.Col.ScrollbarGrabActive]  = { 0.51, 1.00 },
        [imgui.Col.ResizeGrip]           = { 0.13, 0.40 },
        [imgui.Col.ResizeGripHovered]    = { 0.13, 0.60 },
        [imgui.Col.ResizeGripActive]     = { 0.13, 0.80 },
        [imgui.Col.PlotLines]            = { 0.61, 1.00 },
        [imgui.Col.PlotLinesHovered]     = { 1.00, 1.00 },
        [imgui.Col.PlotHistogram]        = { 0.90, 1.00 },
        [imgui.Col.PlotHistogramHovered] = { 1.00, 1.00 },
        [imgui.Col.TextSelectedBg]       = { 0.26, 0.35 }
      },
      colored = {
        [imgui.Col.Button]           = 0.00,
        [imgui.Col.ButtonHovered]    = 0.10,
        [imgui.Col.ButtonActive]     = 0.15,
        [imgui.Col.Header]           = 0.00,
        [imgui.Col.HeaderHovered]    = 0.10,
        [imgui.Col.HeaderActive]     = 0.15,
        [imgui.Col.Separator]        = 0.00,
        [imgui.Col.SeparatorHovered] = 0.10,
        [imgui.Col.SeparatorActive]  = 0.15,
        [imgui.Col.SliderGrab]       = 0.00,
        [imgui.Col.SliderGrabActive] = 0.15,
        [imgui.Col.CheckMark]        = 0.00
      }
    }

    function this:explodeARGB(argb)
      return bit.band(bit.rshift(argb, 24), 0xFF),
          bit.band(bit.rshift(argb, 16), 0xFF),
          bit.band(bit.rshift(argb, 8), 0xFF),
          bit.band(argb, 0xFF)
    end

    function this:map(t, c)
        local r = {}
        for k, v in ipairs(t) do
            r[k] = c(v, k)
        end
        return r
    end

    function this:call(elements, infobar, text)
        local style = imgui.GetStyle()
      local colors = style.Colors

      style.WindowBorderSize       = 0.0
      style.WindowRounding         = 10.0
      style.PopupBorderSize        = 0.0
      style.PopupRounding          = 10.0
      style.ChildRounding          = 10.0
      style.WindowTitleAlign       = imgui.ImVec2(0.5, 0.5)
      style.FrameRounding          = 7.0
      style.ItemSpacing            = imgui.ImVec2(10, 5)
      style.ScrollbarSize          = 3.5
      style.ScrollbarRounding      = 10.0
      style.GrabMinSize            = 5.0
      style.GrabRounding           = 10.0
      style.WindowPadding          = imgui.ImVec2(10, 10)
      style.FramePadding           = imgui.ImVec2(5, 0)
      style.DisplayWindowPadding   = imgui.ImVec2(27, 27)
      style.DisplaySafeAreaPadding = imgui.ImVec2(5, 5)
      style.ButtonTextAlign        = imgui.ImVec2(0.5, 0.5)
      style.IndentSpacing          = 10.0
      style.Alpha                  = 1.0

      local colored = self:map({ self:explodeARGB(elements) }, function(x) return x / 255 end)
      local infobar = self:map({ self:explodeARGB(infobar) }, function(x) return x / 255 end)
      local text = self:map({ self:explodeARGB(text) }, function(x) return x / 255 end)

      for key, value in pairs(self.colored) do
        colors[key] = imgui.ImVec4(colored[2] + value, colored[3] + value, colored[4] + value, 1 - value)
      end

      for key, value in pairs(self.static) do
        colors[key] = imgui.ImVec4(
          infobar[2] + (infobar[2] >= 0.5 and -value[1] or value[1]),
          infobar[3] + (infobar[3] >= 0.5 and -value[1] or value[1]),
          infobar[4] + (infobar[4] >= 0.5 and -value[1] or value[1]),
          infobar[1] + value[2]
        )
      end

      colors[imgui.Col.Text] = imgui.ImVec4(text[2], text[3], text[4], 1)
      colors[imgui.Col.TextDisabled] = imgui.ImVec4(
        text[2] > 0.5 and text[2] - 0.3 or text[2] + 0.3,
        text[3] > 0.5 and text[3] - 0.3 or text[3] + 0.3,
        text[4] > 0.5 and text[4] - 0.3 or text[4] + 0.3,
        text[1]
      )
    end

    return setmetatable(this, { __call = this.call })
end)()

imgui.OnInitialize(function()
    local fontPath = getFolderPath(0x14) .. "\\trebucbd.ttf"
  local glyphRanges = imgui.GetIO().Fonts:GetGlyphRangesCyrillic()
  local iconRanges = imgui.new.ImWchar[3](faicons.min_range, faicons.max_range, 0)
  local base = faicons.get_font_data_base85("solid")
  fontSize = {}

  local config = imgui.ImFontConfig()
  config.MergeMode = true
  config.PixelSnapH = true

  imgui.GetIO().Fonts:Clear()
  imgui.GetIO().Fonts:AddFontFromFileTTF(fontPath, 16, nil, glyphRanges)
  imgui.GetIO().Fonts:AddFontFromMemoryCompressedBase85TTF(base, 15, config, iconRanges)

  for k, v in ipairs({ 18 }) do
    fontSize[v] = imgui.GetIO().Fonts:AddFontFromFileTTF(fontPath, v, nil, glyphRanges)
  end

    applyCustomStyle(0xFF515A5A, 0xE0121212, 0xFFCECECE)
end)

local settings = (function()
    local this = json(json.path .. "\\garden.json", {
        items = {},
        planting = {},
        sell = {},
        buy = {}
    })

    this.items = this.items or {}
    this.planting = this.planting or {}
    this.sell = this.sell or {}
    this.buy = this.buy or {}

    return this
end)()

local inventory = (function()
    local this = {
        sleep = 25,
        headersPattern = {
            ["њ®ўe®¦ap©"] = "RU"
        },
        samp = {}
    }

    local function await(sleep, call, ...)
        wait(sleep)
        call(...)
    end

    function this:open()
        sampSendChat("/invent")
        this.status = true
    end

    function this:scanning()
        if self.data then
            if self.data.items and self.data.items.filled then
                for index, value in ipairs(self.data.items) do
                    if value.status then
                        if not (settings.items[value.model] and settings.items[value.model][value.zoom]) then
                            wait(self.sleep)

                            sampSendClickTextdraw(value.id)
                            self.data.info = true
                            local clock = os.clock()
                            while self.data and type(self.data.info) == "boolean" and os.clock() - clock < 30 do wait(0) end

                            if not self.data or type(self.data.info) == "boolean" then
                                chat("Произошла ошибка при попытке просканировать инвентарь. Код: {HEX}timeout{}.")
                                return
                            end

                            value.name = self.data.info.name
                            self.data.info = nil

                            if not settings.items[value.model] then
                                settings.items[value.model] = {}
                            end

                            if not settings.items[value.model][value.zoom] then
                                settings.items[value.model][value.zoom] = value.name
                            end
                        else
                            value.name = settings.items[value.model][value.zoom]
                        end
                    end
                end

                settings:save()
            end
        end
    end

    this.samp.onShowDialog = sampEventHandler("onShowDialog", function(id, style, title, button1, button2, text)
        if this.data and this.data.info then
            if title:find("{BE433D}Информация о предмете") then
                local item = text:match("^(.-)\n"):match(".*{%x+}(.-){%x+}")
                lua_thread.create(await, this.sleep, function()
                    sampSendDialogResponse(id, 0, 0xFFFF, 0xFFFF)
                    this.data.info = {
                        name = u8(item)
                    }
                end)
                return false
            end
        end
    end)

    this.samp.onShowTextDraw = sampEventHandler("onShowTextDraw", function(id, data)
        if this.status or (this.data and this.data.info) then
            if this.headersPattern[data.text] then
                this.data = { base = id, id = id + 55, items = {} }
            elseif this.data then
                if this.data.info == true then
                    if data.text == "INFO" then
                        lua_thread.create(await, this.sleep, sampSendClickTextdraw, id)
                    end
                elseif this.data.id <= id and data.text == "ld_spac:white" then
                    if #this.data.items < 36 then
                        table.insert(this.data.items, {
                            status = data.modelId ~= 0 and data.modelId ~= 1649,
                            id = id,
                            model = tostring(data.modelId),
                            zoom = ("%.6f"):format(data.zoom / 100)
                        })
                    elseif not this.data.items.filled then
                        this.data.items.filled = true
                        this.status = false
                    end
                end
            end
        end
    end)

    this.samp.onSendClickTextDraw = sampEventHandler("onSendClickTextDraw", function(id)
        if id ~= 0xFFFF and this.data then
            if this.data.items and this.data.items.filled then
                if sampTextdrawGetString(id) == "LD_BEAT:CHIT" then
                    this.data.items = {}
                    this.status = true
                end
            end
        end
    end)

    this.samp.onTextDrawHide = sampEventHandler("onTextDrawHide", function(id)
        if this.data then
            if this.data.base == id then
                this.data = false
            end
        end
    end)

    return this
end)()

local inventoryFrame = (function()
    local this = mimguiFrame {
        search = imgui.new.char[256]()
    }

    function this:filter(list)
        local search = u8:decode(ffi.string(self.search)):nlower()
        if search == "" then
            for index, value in ipairs(list) do
                value.hide = false
            end
        else
            for index, value in ipairs(list) do
                if value.name and value.status then
                    value.hide = not u8:decode(value.name):nlower():find(search)
                end
            end
        end
    end

    function this:click(table_ptr, value)
        if value.name then
            local ptr = table_ptr[value.model]

            if not ptr then
                table_ptr[value.model] = {}
                ptr = table_ptr[value.model]
            end

            if type(ptr[value.zoom]) ~= "string" then
                ptr[value.zoom] = value.name
            else
                ptr[value.zoom] = nil
            end

            settings:save()
        end
    end

    function this:draw(data)
        imgui.Begin("##inventory", self.status, imgui.WindowFlags.AlwaysAutoResize + imgui.WindowFlags.NoCollapse + imgui.WindowFlags.NoTitleBar)
            if not inventory.data then
                if imgui.Button(u8"Открыть инвентарь", imgui.ImVec2(235, 20)) then
                    inventory:open()
                end
                imgui.CenterTextDisabledX(u8"Инвентарь должен быть закрыт")
            elseif inventory.data.items then
                if inventory.data.items.filled then
                    if imgui.Button(u8"Просканировать инвентарь", imgui.ImVec2(235, 20)) then
                        if not inventory.thread or inventory.thread.dead then
                            inventory.thread = lua_thread.create(inventory.scanning, inventory)
                        end
                    end
                end

                imgui.PushItemWidth(235)
                imgui.PushStyleVarVec2(imgui.StyleVar.FramePadding, imgui.ImVec2(5, 2.5))
                if imgui.InputTextWithHint("##search", u8"Введите название предмета", self.search, 255) then
                    self:filter(inventory.data.items)
                end
                imgui.PopStyleVar(1)

                imgui.Spacing()

                imgui.BeginChild("##items", imgui.ImVec2(235, 198))
                    local colorTextDisabled = imgui.GetStyle().Colors[imgui.Col.TextDisabled]
                    local colorButton = imgui.GetStyle().Colors[imgui.Col.Button]

                    local planting = settings.planting
                    local sell = settings.sell
                    local buy = settings.buy

                    for index, value in ipairs(inventory.data.items) do
                        if value.status and not value.hide then
                            imgui.PushFont(fontSize[18])
                            imgui.PushStyleVarVec2(imgui.StyleVar.ItemSpacing, imgui.ImVec2(5, 0))
                            imgui.Text(value.name or u8"Неизвестный предмет")
                            imgui.PopStyleVar(1)
                            imgui.PopFont()
                            imgui.TextDisabled((u8"Слот %s модель %s"):format(index, value.model))

                            if imgui.CustomButton(("%s##%s"):format(faicons("SEEDLING"), index), imgui.ImVec2(70, 20), (planting[value.model] and planting[value.model][value.zoom]) and colorTextDisabled or colorButton) then
                                self:click(settings.planting, value)
                            end
                            imgui.SameLine(nil, 5)
                            if imgui.CustomButton(("%s##%s"):format(faicons("SACK_DOLLAR"), index), imgui.ImVec2(70, 20), (sell[value.model] and sell[value.model][value.zoom]) and colorTextDisabled or colorButton) then
                                self:click(settings.sell, value)
                            end
                            imgui.SameLine(nil, 5)
                            if imgui.CustomButton(("%s##%s"):format(faicons("WALLET"), index), imgui.ImVec2(70, 20), (buy[value.model] and buy[value.model][value.zoom]) and colorTextDisabled or colorButton) then
                                self:click(settings.buy, value)
                            end

                            if index ~= #inventory.data.items then
                                imgui.Spacing()
                            end
                        end
                    end
                imgui.EndChild()
            end
        imgui.End()
    end

    return this
end)()

local planting = (function()
    local this = mimguiFrame {
        sleep = 35,
        mode = 0,
        last = -1,
        data = {},
        samp = {},
        auto = imgui.new.bool(false),

        actions = {
            size = imgui.ImVec2(34, 34),
            faicons("FLASK"),
            faicons("HAND_HOLDING_SEEDLING"),
            faicons("BOX_OPEN_FULL"),
            faicons("PERSON_DIGGING")
        }
    }

    local function await(sleep, call, ...)
        wait(sleep)
        call(...)
    end

    local function sampSendKey(key)
        setGameKeyState(key, 255)
        sampForceOnfootSync()
    end

    local function pairsClick(id, text, pattern)
        local index = 0
        for line in text:gmatch("[^\n]+") do
            if line:find(pattern) then
                lua_thread.create(await, this.sleep, sampSendDialogResponse, id, 1, index, "")
                lua_thread.create(await, this.sleep * 2, sampSendKey, gkeys.player.WALK)
                return false
            end
            index = index + 1
        end
    end

    function this:update()
        self.data = {}
        for model, data in pairs(settings.planting) do
            for zoom, name in pairs(data) do
                table.insert(self.data, {
                    model = model,
                    zoom = zoom,
                    name = name
                })
            end
        end
    end

    this:update()

    function this:draw(data)
        data.HideCursor = isKeyDown(vkeys.VK_RBUTTON)

        imgui.Begin("##planting", self.status, imgui.WindowFlags.AlwaysAutoResize + imgui.WindowFlags.NoCollapse + imgui.WindowFlags.NoTitleBar)
            imgui.BeginGroup()
                local colorTextDisabled = imgui.GetStyle().Colors[imgui.Col.TextDisabled]
                local colorButton = imgui.GetStyle().Colors[imgui.Col.Button]

                for index, icon in ipairs(self.actions) do
                    if imgui.CustomButton(icon, self.actions.size, index == self.mode and colorTextDisabled or colorButton) then
                        self.mode = self.mode ~= 0 and 0 or index
                        self.last = -1
                        if self.mode ~= 0 then
                            lua_thread.create(await, this.sleep, sampSendKey, gkeys.player.WALK)
                        end
                    end
                    if index ~= #self.actions then
                        imgui.SameLine(nil, 5)
                    end
                end
            imgui.EndGroup()

            imgui.Spacing()

            if imgui.Button(u8"Обновить список", imgui.ImVec2(150, 20)) then
                self:update()
            end

            local skip = false

            imgui.Checkbox(u8"Автоповторение", self.auto)

            if self.current then
                imgui.Spacing()
                if imgui.Button((u8"%s Остановить"):format(faicons("BAN")), imgui.ImVec2(150, 20)) then
                    skip = self.current
                    self.current = nil
                    self.use = nil
                    self.planting = nil
                    sampSendClickTextdraw(0xFFFF)
                end
                imgui.CenterTextDisabledX(self.current and self.current.name or "No data")
            end

            for index, value in ipairs(self.data) do
                if self.current ~= value and skip ~= value then
                    imgui.Spacing()
                    imgui.BeginGroup()
                        if imgui.Button((u8"%s Посадить##%s"):format(faicons("SHOVEL"), index), imgui.ImVec2(150, 20)) then
                            if not self.current then
                                self.current = value
                                sampSendChat("/invent")
                            end
                        end
                        imgui.CenterTextX(value.name)
                    imgui.EndGroup()
                end
            end
        imgui.End()
    end

    this.samp.onShowTextDraw = sampEventHandler("onShowTextDraw", function(id, data)
        if this.current then
            if data.text == "ld_spac:white" then
                if not (this.use and this.planting) and this.current.model == tostring(data.modelId) and this.current.zoom == ("%.6f"):format(data.zoom / 100) then
                    this.use = true
                    lua_thread.create(await, this.sleep, sampSendClickTextdraw, id)
                end
            elseif data.text == "USE" then
                if this.use then
                    this.use = nil
                    this.planting = true
                    lua_thread.create(await, this.sleep, sampSendClickTextdraw, id)
                end
            end
            return false
        end
    end)

    this.samp.onToggleSelectTextDraw = sampEventHandler("onToggleSelectTextDraw", function(state, color)
        if state and this.current then
            return false
        end
    end)

    this.samp.onShowDialog = sampEventHandler("onShowDialog", function(id, style, title, button1, button2, text)
        if this.mode ~= 0 then
            if style == 5 and title:find("{BE433D}Садовый участок") then
                local index = 0
                for name, status, time in text:gmatch("\n(.-)\t(.-)\t(%d+) минут%(ы%)") do
                    if this.mode == 4 and status == "Мертвое" then
                        lua_thread.create(await, this.sleep, sampSendDialogResponse, id, 1, index, "")
                        return false
                    elseif this.mode == 3 and status == "Урожай" then
                        lua_thread.create(await, this.sleep, sampSendDialogResponse, id, 1, index, "")
                        return false
                    elseif (this.mode == 1 or this.mode == 2) and index > this.last then
                        if status ~= "Мертвое" and status ~= "Урожай" then
                            this.last = index
                            lua_thread.create(await, this.sleep, sampSendDialogResponse, id, 1, index, "")
                            return false
                        end
                    end
                    index = index + 1
                end

                this.last = -1
                this.mode = 0
                lua_thread.create(await, this.sleep, sampSendDialogResponse, id, 0, 0xFFFF, 0xFFFF)
                return false
            elseif style == 4 then
                local fertilizers, chemicals = text:match("Требует удобрений:\t{97FC9A}(%d+) кг%.\nТребует химикатов:\t{97FC9A}(%d+) г%.")
                fertilizers, chemicals = tonumber(fertilizers), tonumber(chemicals)

                if this.mode == 1 then
                    if fertilizers and fertilizers > 0 then
                        return pairsClick(id, text, "^Удобрить")
                    else
                        lua_thread.create(await, this.sleep, sampSendDialogResponse, id, 0, 0xFFFF, 0xFFFF)
                    end
                elseif this.mode == 2 then
                    if chemicals and chemicals > 0 then
                        return pairsClick(id, text, "^Обработать")
                    else
                        lua_thread.create(await, this.sleep, sampSendDialogResponse, id, 0, 0xFFFF, 0xFFFF)
                    end
                elseif this.mode == 3 then
                    return pairsClick(id, text, "^Собрать")
                elseif this.mode == 4 then
                    return pairsClick(id, text, "^Сорвать")
                end

                lua_thread.create(await, this.sleep, sampSendDialogResponse, id, 0, 0xFFFF, 0xFFFF)
                return false
            end
        end
    end)
    
    function cef:click()
        cef:send("@24, pressKey")
        wait(50)
        cef:send("@24, releaseKey")
    end

    function cef:onDisplay(str)
        if this.current then
            if str:find("cef.modals.showModal.*progressBar") then
                click = true
            elseif str:find('cef.modals.closeModal') then
                click = false
                if this.auto[0] then
                    this.planting = nil
                    this.use = nil
                    lua_thread.create(function()
                        wait(100)
                        sampSendChat("/invent")
                    end)
                else
                    this.current = nil
                    this.planting = nil
                    this.use = nil
                end
            elseif str:find("На этой грядке нет места") then
                this.current = nil
                this.planting = nil
                this.use = nil
                sampSendClickTextdraw(0xFFFF)
            end
        elseif this.mode ~= 0 then
            if str:find("На этой грядке ничего не растет") then
                this.mode = 0
            end
        end
    end

    return this
end)()

local market = (function()
    local this = mimguiFrame {
        state = {
            sell = imgui.new.bool(false),
            buy = imgui.new.bool(false)
        },
        auto = imgui.new.bool(true),
        data = {},
        samp = {}
    }

    function this:update()
        self.data = {}

        for index, block in ipairs({ "buy", "sell" }) do
            self.data[block] = {}
            for model, data in pairs(settings[block]) do
                for zoom, name in pairs(data) do
                    table.insert(self.data[block], {
                        name = name,
                        model = model,
                        zoom = zoom,
                        money = imgui.new.int(0)
                    })
                end
            end
        end
    end

    this:update()

    function this:draw()
        imgui.Begin("##market", self.status, imgui.WindowFlags.AlwaysAutoResize + imgui.WindowFlags.NoCollapse + imgui.WindowFlags.NoTitleBar)
            imgui.PushFont(fontSize[18])
            imgui.CenterTextX(u8"Фермерский магазин")
            imgui.PopFont()

            if imgui.Button(u8"Обновить список", imgui.ImVec2(190, 20)) then
                this:update()
            end

            imgui.Spacing()
            imgui.PushItemWidth(40)

            if #self.data.sell > 0 then
                imgui.Checkbox(u8"Продажа", self.state.sell)
                if self.state.sell[0] then
                    imgui.Checkbox(u8"Авторасчет количества", self.auto)
                    for index, value in ipairs(self.data.sell) do
                        if self.auto[0] then
                            imgui.Button("auto")
                            imgui.SameLine(nil, 5)
                            imgui.Text(value.name)
                        else
                            imgui.InputInt(("%s##%s"):format(value.name, index), value.money, 0, 500)
                        end
                    end
                    imgui.Spacing()
                end
            end

            if #self.data.buy > 0 then
                imgui.Checkbox(u8"Покупка", self.state.buy)
                if self.state.buy[0] then
                    for index, value in ipairs(self.data.buy) do
                        imgui.InputInt(("%s##%s"):format(value.name, index), value.money, 0, 500)
                    end
                end
            end
        imgui.End()
    end

    this.samp.onShowTextDraw = sampEventHandler("onShowTextDraw", function(id, data)
        if data.text == "Ma™aџњ® Јpoљae¦" or data.text == "Ma™aџњ® ckyЈae¦" then
            this.td = {
                base = id,
                id = id + 5,
                ptr = data.text == "Ma™aџњ® Јpoљae¦" and this.data.buy or this.data.sell,
                status = data.text == "Ma™aџњ® Јpoљae¦"
            }
        elseif this.td then
            if id >= this.td.id and data.text == "ld_spac:white" then
                local model, zoom = tostring(data.modelId), ("%.6f"):format(data.zoom / 100)
                if this.td.status then
                    if this.state.buy[0] then
                        for index, value in ipairs(this.td.ptr or {}) do
                            if value.model == model and value.zoom == zoom then
                                if value.money[0] ~= 0 then
                                    sampSendClickTextdraw(id)
                                    sampSendDialogResponse(1076, 1, 0xFFFF, value.money[0])
                                    this.dialog = true
                                    value.money[0] = 0
                                end
                            end
                        end
                    end
                else
                    if this.state.sell[0] then
                        for index, value in ipairs(this.td.ptr or {}) do
                            if value.model == model and value.zoom == zoom then
                                if value.money[0] ~= 0 then
                                    sampSendClickTextdraw(id)
                                    sampSendDialogResponse(1077, 1, 0xFFFF, value.money[0])
                                    this.dialog = true
                                    value.money[0] = 0
                                elseif this.auto[0] then
                                    sampSendClickTextdraw(id)
                                    this.dialog = 1
                                end
                            end
                        end
                    end
                end
            end
        end
    end)

    this.samp.onTextDrawHide = sampEventHandler("onTextDrawHide", function(id)
        if this.td and this.td.base == id then
            this.td = false
        end
    end)

    this.samp.onShowDialog = sampEventHandler("onShowDialog", function(id, style, title, button1, button2, text)
        if this.td and this.dialog then
            if title:find("{BE433D}Фермерский магазин") then
                if style == 1 then
                    if this.dialog == 1 then
                        local available, current = text:match("Доступное количество: {%x+}(%d+) шт.{%x+}\n\nУ Вас в инвентаре: {%x+}(%d+) шт.")
                        available, current = tonumber(available), tonumber(current)
                        if available and current then
                            local value = available >= current and current or available
                            sampSendDialogResponse(id, 1, 0xFFFF, value)
                            return false
                        end
                    end
                end

                sampSendDialogResponse(id, 0, 0xFFFF, 0xFFFF)
                this.dialog = false
                return false
            end
        end
    end)

    return this
end)()

local interface = (function()
    local this = {
        samp = {}
    }

    this.samp.onSendCommand = sampEventHandler("onSendCommand", function(arguments)
        local command, parameters = arguments:match("/(%S+) ?(.*)")
        if command == "ginvent" or command == "gin" then
            inventoryFrame:switch()
            return false
        elseif command == "gplant" or command == "gpl" then
            planting:switch()
            return false
        elseif command == "gmarket" or command == "gma" then
            market:switch()
            return false
        end
    end)

    return this
end)()

function imgui.CenterTextX(text)
  local textSize = imgui.CalcTextSize(text)
  imgui.SetCursorPosX(imgui.GetWindowWidth() / 2 - textSize.x / 2)
  imgui.Text(text)
end

function imgui.CenterTextDisabledX(text)
  local textSize = imgui.CalcTextSize(text)
  imgui.SetCursorPosX(imgui.GetWindowWidth() / 2 - textSize.x / 2)
  imgui.TextDisabled(text)
end

function imgui.CenterText(text)
  local textSize = imgui.CalcTextSize(text)
  local windowSize = imgui.GetWindowSize()
  imgui.SetCursorPos(imgui.ImVec2(windowSize.x / 2 - textSize.x / 2, windowSize.y / 2 - textSize.y / 2))
  imgui.Text(text)
end

function imgui.CustomButton(str_id, size, color)
  imgui.PushStyleColor(imgui.Col.Button, color or imgui.ImVec4(0, 0, 0, 0))
  local result = imgui.Button(str_id, size)
  imgui.PopStyleColor(1)
  return result
end

local lower, sub, char, upper = string.lower, string.sub, string.char, string.upper
local concat = table.concat

-- initialization table
local lu_rus, ul_rus = {}, {}
for i = 192, 223 do
  local A, a = char(i), char(i + 32)
  ul_rus[A] = a
  lu_rus[a] = A
end
local E, e = char(168), char(184)
ul_rus[E] = e
lu_rus[e] = E

function string.nlower(s)
  s = lower(s)
  local len, res = #s, {}
  for i = 1, len do
    local ch = sub(s, i, i)
    res[i] = ul_rus[ch] or ch
  end
  return concat(res)
end

function string.nupper(s)
  s = upper(s)
  local len, res = #s, {}
  for i = 1, len do
    local ch = sub(s, i, i)
    res[i] = lu_rus[ch] or ch
  end
  return concat(res)
end