Kasumi Hub for PVSB CRACKED OPEN SOURCE
Key Required

Kasumi Hub for PVSB CRACKED OPEN SOURCE

[🐰] Plants & Brainrots 🌻ScriptCracksScriptCracks 4.5K views 909 executesUpdated Oct 6, 2025
5.0/ 5(1 votes)
10100% Positive
Lua Script•1504 lines (52013 chars)
-- Kasumi Hub for Plants vs. Brainrot - FINAL FIX
-- Game validation first
local PLANTS_VS_BRAINROT_ID = 127742093697776

if game.PlaceId ~= PLANTS_VS_BRAINROT_ID then
    game:GetService("StarterGui"):SetCore("SendNotification", {
        Title = "Wrong Game!";
        Text = "This script only works in Plants vs. Brainrot";
        Duration = 5;
    })
    return
end

local WindUI = loadstring(game:HttpGet("https://github.com/Footagesus/WindUI/releases/latest/download/main.lua"))()
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local TeleportService = game:GetService("TeleportService")
local NetworkClient = game:GetService("NetworkClient")

local localPlayer = Players.LocalPlayer

-- Script Version
local SCRIPT_VERSION = "v1.1"

-- Configuration system - SEPARATE FOLDER: KasumiHubPVB
local CONFIG_FILE = "KasumiHubPVB/PlantsBrainrot_Config.json"
local SETTINGS_FILE = "KasumiHubPVB/PlantsBrainrot_Settings.json"
local defaultConfig = {
    selectedSeeds = {},
    selectedGears = {},
    equipInterval = 20,
    theme = "Dark",
    autoBuySeedsEnabled = false,
    autoBuyGearsEnabled = false,
    autoEquipEnabled = false,
    autoBuyAllSeedsEnabled = false,
    autoBuyAllGearsEnabled = false,
    antiAFKEnabled = false,
    autoReconnectEnabled = false
}

local defaultSettings = {
    autoLoadConfig = "None",
    autoLoadEnabled = false
}

local currentConfig = {}
local currentSettings = {}

-- Variables
local autoBuySeedsEnabled = false
local autoBuyGearsEnabled = false
local autoEquipEnabled = false
local autoBuyAllSeedsEnabled = false
local autoBuyAllGearsEnabled = false
local antiAFKEnabled = false
local autoReconnectEnabled = false
local selectedSeeds = {}
local selectedGears = {}
local equipInterval = 20
local Window = nil

-- UNIFIED TIMING SYSTEM
local ITEMS_PER_ROUND = 10
local PURCHASE_DELAY = 200
local CYCLE_INTERVAL = 3000
local currentItemIndex = 1
local purchaseCount = 0
local isAlternatingSeed = true

-- EXACT GAME REMOTES FROM SIGMA SPY
local BuyItemRemote = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("BuyItem")
local BuyGearRemote = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("BuyGear")
local OpenUIRemote = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("OpenUI")
local EquipBestRemote = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("EquipBest")
local ItemSellRemote = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("ItemSell")

-- UI References
local seedShopUI = localPlayer.PlayerGui.Main.Seeds
local gearShopUI = localPlayer.PlayerGui.Main.Gears

-- Available items lists
local availableSeeds = {
    "Cactus Seed", "Strawberry Seed", "Pumpkin Seed", "Sunflower Seed",
    "Dragon Fruit Seed", "Eggplant Seed", "Watermelon Seed", "Grape Seed",
    "Cocotank Seed", "Carnivorous Plant Seed", "Mr Carrot Seed", 
    "Tomatrio Seed", "Shroombino Seed", "Mango Seed"
}

local availableGears = {
    "Water Bucket", "Frost Grenade", "Banana Gun", "Frost Blower", "Carrot Launcher"
}

-- UI References for keybinds and dropdowns
local autoBuyToggleSeeds = nil
local autoBuyToggleGears = nil
local autoEquipToggle = nil
local autoBuyAllSeedsToggle = nil
local autoBuyAllGearsToggle = nil
local equipIntervalInput = nil
local seedDropdown = nil
local gearDropdown = nil
local themeDropdown = nil
local autoLoadDropdown = nil
local autoLoadToggle = nil
local ConfigNameInput = nil
local antiAFKToggle = nil
local autoReconnectToggle = nil

-- Control flags
local canChangeTheme = true
local canChangeDropdown = true

-- EXTENDED AUTO-RECONNECT SYSTEM (3 MINUTES DURATION)
local reconnectAttempts = 0
local MAX_RECONNECT_ATTEMPTS = 30  -- ✅ Increased to 30 attempts
local RECONNECT_INTERVAL = 6       -- ✅ 6 seconds between attempts (30 × 6 = 180 seconds = 3 minutes)
local lastDisconnectTime = 0
local lastPingTime = tick()
local consecutiveTimeouts = 0

local function setupAutoReconnect()
    print("🌐 Initializing 3-minute auto-reconnect with network monitoring...")
    
    -- Method 1: Detect Roblox error prompts
    pcall(function()
        local RobloxPromptGui = game:GetService("CoreGui"):FindFirstChild("RobloxPromptGui")
        if RobloxPromptGui then
            local promptOverlay = RobloxPromptGui:FindFirstChild("promptOverlay")
            if promptOverlay then
                promptOverlay.ChildAdded:Connect(function(child)
                    if child.Name == "ErrorPrompt" and autoReconnectEnabled then
                        task.wait(0.5)
                        
                        local errorMessage = child:FindFirstChild("MessageArea")
                        if errorMessage then
                            local messageText = errorMessage:FindFirstChild("ErrorMessage")
                            if messageText then
                                local text = messageText.Text:lower()
                                
                                if string.find(text, "disconnect") or
                                   string.find(text, "connection") or
                                   string.find(text, "lost") or
                                   string.find(text, "error") or
                                   string.find(text, "kicked") or
                                   string.find(text, "unable") or
                                   string.find(text, "timeout") or
                                   string.find(text, "network") or
                                   string.find(text, "failed") or
                                   string.find(text, "id=17") then
                                    
                                    reconnectAttempts = reconnectAttempts + 1
                                    
                                    if reconnectAttempts <= MAX_RECONNECT_ATTEMPTS then
                                        local timeRemaining = (MAX_RECONNECT_ATTEMPTS - reconnectAttempts) * RECONNECT_INTERVAL
                                        print(string.format("🔄 Network issue - reconnecting (attempt %d/%d, %ds remaining)", 
                                            reconnectAttempts, MAX_RECONNECT_ATTEMPTS, timeRemaining))
                                        
                                        task.wait(RECONNECT_INTERVAL)
                                        
                                        local success = pcall(function()
                                            TeleportService:TeleportToPlaceInstance(game.PlaceId, game.JobId, localPlayer)
                                        end)
                                        
                                        if not success then
                                            pcall(function()
                                                TeleportService:Teleport(game.PlaceId, localPlayer)
                                            end)
                                        end
                                    else
                                        print("❌ Max reconnect attempts reached (3 minutes elapsed)")
                                        WindUI:Notify({
                                            Title = "Auto-Reconnect Failed",
                                            Content = string.format("Could not reconnect after 3 minutes (%d attempts)", MAX_RECONNECT_ATTEMPTS),
                                            Duration = 5
                                        })
                                    end
                                end
                            end
                        end
                    end
                end)
            end
        end
    end)
    
    -- Method 2: NetworkClient disconnection detection
    pcall(function()
        NetworkClient.ChildRemoved:Connect(function()
            if autoReconnectEnabled then
                local currentTime = tick()
                
                if currentTime - lastDisconnectTime > 5 then
                    lastDisconnectTime = currentTime
                    reconnectAttempts = reconnectAttempts + 1
                    
                    if reconnectAttempts <= MAX_RECONNECT_ATTEMPTS then
                        local timeRemaining = (MAX_RECONNECT_ATTEMPTS - reconnectAttempts) * RECONNECT_INTERVAL
                        print(string.format("🔄 NetworkClient disconnect (attempt %d/%d, %ds left)", 
                            reconnectAttempts, MAX_RECONNECT_ATTEMPTS, timeRemaining))
                        
                        task.wait(RECONNECT_INTERVAL)
                        
                        pcall(function()
                            TeleportService:Teleport(game.PlaceId, localPlayer)
                        end)
                    end
                end
            end
        end)
    end)
    
    -- Method 3: Active ping monitoring (checks every 8 seconds)
    task.spawn(function()
        while true do
            task.wait(8)
            
            if autoReconnectEnabled then
                local pingStart = tick()
                local pingSuccess = pcall(function()
                    local test = game:GetService("Players"):GetPlayers()
                    return test ~= nil
                end)
                local pingTime = tick() - pingStart
                
                if not pingSuccess or pingTime > 4 then
                    consecutiveTimeouts = consecutiveTimeouts + 1
                    print(string.format("⚠️ Poor connection detected (timeout %d/4)", consecutiveTimeouts))
                    
                    if consecutiveTimeouts >= 4 then
                        print("🔴 Connection lost - poor network quality")
                        reconnectAttempts = reconnectAttempts + 1
                        
                        if reconnectAttempts <= MAX_RECONNECT_ATTEMPTS then
                            local timeRemaining = (MAX_RECONNECT_ATTEMPTS - reconnectAttempts) * RECONNECT_INTERVAL
                            print(string.format("🔄 Reconnecting (attempt %d/%d, %ds remaining)", 
                                reconnectAttempts, MAX_RECONNECT_ATTEMPTS, timeRemaining))
                            
                            task.wait(RECONNECT_INTERVAL)
                            
                            pcall(function()
                                TeleportService:Teleport(game.PlaceId, localPlayer)
                            end)
                        end
                        
                        consecutiveTimeouts = 0
                    end
                else
                    consecutiveTimeouts = 0
                end
                
                lastPingTime = tick()
            end
        end
    end)
    
    -- Method 4: Monitor ReplicatedFirst
    pcall(function()
        game:GetService("ReplicatedFirst"):GetPropertyChangedSignal("FinishedReplicating"):Connect(function()
            if not game:GetService("ReplicatedFirst").FinishedReplicating and autoReconnectEnabled then
                print("🔄 Replication interrupted")
                reconnectAttempts = reconnectAttempts + 1
                
                if reconnectAttempts <= MAX_RECONNECT_ATTEMPTS then
                    task.wait(RECONNECT_INTERVAL)
                    pcall(function()
                        TeleportService:Teleport(game.PlaceId, localPlayer)
                    end)
                end
            end
        end)
    end)
    
    -- Method 5: GuiService error monitoring
    pcall(function()
        local GuiService = game:GetService("GuiService")
        GuiService.ErrorMessageChanged:Connect(function()
            if autoReconnectEnabled then
                local errorMessage = GuiService:GetErrorMessage()
                if errorMessage and errorMessage ~= "" then
                    print("🔴 GuiService error:", errorMessage)
                    reconnectAttempts = reconnectAttempts + 1
                    
                    if reconnectAttempts <= MAX_RECONNECT_ATTEMPTS then
                        task.wait(RECONNECT_INTERVAL)
                        pcall(function()
                            TeleportService:Teleport(game.PlaceId, localPlayer)
                        end)
                    end
                end
            end
        end)
    end)
    
    -- Method 6: Heartbeat freeze detection (15 seconds threshold)
    local lastHeartbeat = tick()
    pcall(function()
        game:GetService("RunService").Heartbeat:Connect(function()
            if autoReconnectEnabled then
                local currentTime = tick()
                local timeSinceLastBeat = currentTime - lastHeartbeat
                
                if timeSinceLastBeat > 15 then
                    print("🔴 Connection frozen - no heartbeat for " .. math.floor(timeSinceLastBeat) .. " seconds")
                    reconnectAttempts = reconnectAttempts + 1
                    
                    if reconnectAttempts <= MAX_RECONNECT_ATTEMPTS then
                        task.wait(RECONNECT_INTERVAL)
                        pcall(function()
                            TeleportService:Teleport(game.PlaceId, localPlayer)
                        end)
                    end
                end
                
                lastHeartbeat = currentTime
            end
        end)
    end)
    
    print("✅ 3-minute auto-reconnect initialized (30 attempts × 6 seconds)")
end



-- ANTI-AFK SYSTEM
local idledConnection = nil
local antiAFKLoop = nil

local function startAntiAFK()
    local VirtualUser = game:GetService("VirtualUser")
    
    if not idledConnection then
        idledConnection = localPlayer.Idled:Connect(function()
            if antiAFKEnabled then
                VirtualUser:CaptureController()
                VirtualUser:ClickButton2(Vector2.new())
                print("🛡️ Anti-AFK: Prevented idle kick")
            end
        end)
    end
    
    if not antiAFKLoop then
        antiAFKLoop = task.spawn(function()
            while true do
                if antiAFKEnabled then
                    VirtualUser:CaptureController()
                    VirtualUser:Button1Down(Vector2.new(0, 0))
                    task.wait(0.1)
                    VirtualUser:Button1Up(Vector2.new(0, 0))
                    print("🛡️ Anti-AFK: Activity simulated")
                end
                task.wait(600)
            end
        end)
    end
end

-- KEYBIND SYSTEM
local function setupKeybinds()
    UserInputService.InputBegan:Connect(function(input, gameProcessed)
        if gameProcessed then return end
        
        local isCtrlPressed = UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) or UserInputService:IsKeyDown(Enum.KeyCode.RightControl)
        
        if isCtrlPressed then
            if input.KeyCode == Enum.KeyCode.B then
                autoBuySeedsEnabled = not autoBuySeedsEnabled
                
                if autoBuyToggleSeeds then
                    pcall(function()
                        autoBuyToggleSeeds.Value = autoBuySeedsEnabled
                        if autoBuyToggleSeeds.Callback then
                            autoBuyToggleSeeds.Callback(autoBuySeedsEnabled)
                        end
                    end)
                end
                
                WindUI:Notify({
                    Title = "Auto Buy Seeds " .. (autoBuySeedsEnabled and "Started" or "Stopped") .. " [Ctrl+B]",
                    Content = autoBuySeedsEnabled and string.format("Now purchasing %d selected seeds automatically", #selectedSeeds) or "Seed purchasing stopped",
                    Duration = 3
                })
            
            elseif input.KeyCode == Enum.KeyCode.N then
                autoBuyGearsEnabled = not autoBuyGearsEnabled
                
                if autoBuyToggleGears then
                    pcall(function()
                        autoBuyToggleGears.Value = autoBuyGearsEnabled
                        if autoBuyToggleGears.Callback then
                            autoBuyToggleGears.Callback(autoBuyGearsEnabled)
                        end
                    end)
                end
                
                WindUI:Notify({
                    Title = "Auto Buy Gears " .. (autoBuyGearsEnabled and "Started" or "Stopped") .. " [Ctrl+N]",
                    Content = autoBuyGearsEnabled and string.format("Now purchasing %d selected gears automatically", #selectedGears) or "Gear purchasing stopped",
                    Duration = 3
                })
            
            elseif input.KeyCode == Enum.KeyCode.E then
                autoEquipEnabled = not autoEquipEnabled
                
                if autoEquipToggle then
                    pcall(function()
                        autoEquipToggle.Value = autoEquipEnabled
                        if autoEquipToggle.Callback then
                            autoEquipToggle.Callback(autoEquipEnabled)
                        end
                    end)
                end
                
                if autoEquipEnabled then
                    startEquipLoop()
                end
                
                WindUI:Notify({
                    Title = "Auto Equip " .. (autoEquipEnabled and "Started" or "Stopped") .. " [Ctrl+E]",
                    Content = autoEquipEnabled and string.format("Now auto-equipping best brainrot every %d seconds", equipInterval) or "Auto equip stopped",
                    Duration = 3
                })
            end
        end
    end)
end

-- BUYING FUNCTIONS
local function buySeed(seedName)
    if not BuyItemRemote then return false end
    return pcall(function() BuyItemRemote:FireServer(seedName) end)
end

local function buyGear(gearName)
    if not BuyGearRemote then return false end
    return pcall(function() BuyGearRemote:FireServer(gearName) end)
end

-- SELLING FUNCTIONS
local function sellAllBrainrots()
    if not ItemSellRemote then return false end
    return pcall(function() ItemSellRemote:FireServer() end)
end

local function sellAllPlants()
    if not ItemSellRemote then return false end
    return pcall(function() 
        ItemSellRemote:FireServer(unpack({[2] = true}, 1, table.maxn({[2] = true}))) 
    end)
end

-- SHOP OPENING FUNCTIONS
local function openSeedShop()
    if not OpenUIRemote or not seedShopUI then return false end
    return pcall(function() OpenUIRemote:Fire(seedShopUI, true) end)
end

local function openGearShop()
    if not OpenUIRemote or not gearShopUI then return false end
    return pcall(function() OpenUIRemote:Fire(gearShopUI, true) end)
end

-- EQUIP BEST FUNCTION
local function equipBest()
    if not EquipBestRemote then return false end
    return pcall(function() EquipBestRemote:Fire() end)
end

local function startEquipLoop()
    task.spawn(function()
        while autoEquipEnabled do
            equipBest()
            task.wait(equipInterval)
        end
    end)
end

-- Apply equip interval function
local function applyEquipInterval()
    if equipIntervalInput then
        local value = ""
        if equipIntervalInput.Get then
            value = equipIntervalInput:Get()
        elseif equipIntervalInput.Value then
            value = equipIntervalInput.Value
        else
            value = "20"
        end
        
        local num = tonumber(value)
        if num and num > 0 then
            equipInterval = num
            WindUI:Notify({
                Title = "Equip Interval Updated",
                Content = "Auto-equip now runs every " .. num .. " seconds",
                Duration = 2
            })
            return true
        else
            WindUI:Notify({
                Title = "Invalid Number",
                Content = "Please enter a valid positive number for the interval",
                Duration = 3
            })
        end
    end
    return false
end

-- SETTINGS MANAGEMENT
local function saveSettings()
    local settings = {
        autoLoadConfig = currentSettings.autoLoadConfig or defaultSettings.autoLoadConfig,
        autoLoadEnabled = currentSettings.autoLoadEnabled or defaultSettings.autoLoadEnabled
    }
    
    if not isfolder("KasumiHubPVB") then
        makefolder("KasumiHubPVB")
    end
    
    local success = pcall(function()
        writefile(SETTINGS_FILE, game:GetService("HttpService"):JSONEncode(settings))
    end)
    
    return success
end

local function loadSettings()
    local success, result = pcall(function()
        if isfile(SETTINGS_FILE) then
            local data = readfile(SETTINGS_FILE)
            return game:GetService("HttpService"):JSONDecode(data)
        end
        return nil
    end)
    
    if success and result then
        currentSettings = result
    else
        currentSettings = table.clone(defaultSettings)
        saveSettings()
    end
    
    return currentSettings
end

-- Get available config files
local function getAvailableConfigs()
    local configs = {"None"}
    
    pcall(function()
        if isfolder("KasumiHubPVB") then
            local files = listfiles("KasumiHubPVB")
            for _, file in pairs(files) do
                if file:match("%.json$") and not file:match("Settings") then
                    local fileName = file:match("KasumiHubPVB/(.+)%.json$") or file:match("([^/\\]+)%.json$")
                    if fileName and fileName ~= "PlantsBrainrot_Settings" then
                        if not table.find(configs, fileName) then
                            table.insert(configs, fileName)
                        end
                    end
                end
            end
        end
    end)
    
    return configs
end

-- Save config function (saves all toggles)
local function saveConfig(customName)
    if not isfolder("KasumiHubPVB") then
        makefolder("KasumiHubPVB")
    end
    
    local fileName = customName and ("KasumiHubPVB/" .. customName .. ".json") or CONFIG_FILE
    
    local equipInt = equipInterval
    if equipIntervalInput then
        local equipValue = ""
        if equipIntervalInput.Get then
            equipValue = equipIntervalInput:Get()
        elseif equipIntervalInput.Value then
            equipValue = equipIntervalInput.Value
        end
        equipInt = tonumber(equipValue) or 20
    end
    
    currentConfig = {
        selectedSeeds = selectedSeeds,
        selectedGears = selectedGears,
        equipInterval = equipInt,
        theme = WindUI:GetCurrentTheme() or "Dark",
        autoBuySeedsEnabled = autoBuySeedsEnabled,
        autoBuyGearsEnabled = autoBuyGearsEnabled,
        autoEquipEnabled = autoEquipEnabled,
        autoBuyAllSeedsEnabled = autoBuyAllSeedsEnabled,
        autoBuyAllGearsEnabled = autoBuyAllGearsEnabled,
        antiAFKEnabled = antiAFKEnabled,
        autoReconnectEnabled = autoReconnectEnabled
    }
    
    local success = pcall(function()
        writefile(fileName, game:GetService("HttpService"):JSONEncode(currentConfig))
    end)
    
    if success then
        print("✅ Config saved to:", fileName)
        WindUI:Notify({
            Title = "Settings Saved",
            Content = "Your preferences have been saved as: " .. (customName or "PlantsBrainrot_Config"),
            Duration = 3
        })
    else
        WindUI:Notify({
            Title = "Save Failed",
            Content = "Could not save your settings. Please try again.",
            Duration = 3
        })
    end
    
    return success
end

-- Dropdown update functions
local function updateSeedDropdownProperly()
    if seedDropdown then
        task.spawn(function()
            task.wait(0.3)
            pcall(function()
                if seedDropdown.Set then
                    seedDropdown:Set(selectedSeeds)
                end
                if seedDropdown.Value ~= nil then
                    seedDropdown.Value = selectedSeeds
                end
                if Window and Window.Flags and Window.Flags.SeedSelection ~= nil then
                    Window.Flags.SeedSelection = selectedSeeds
                end
                task.wait(0.1)
                if seedDropdown.Refresh then
                    seedDropdown:Refresh(availableSeeds, selectedSeeds)
                end
            end)
        end)
    end
end

local function updateGearDropdownProperly()
    if gearDropdown then
        task.spawn(function()
            task.wait(0.3)
            pcall(function()
                if gearDropdown.Set then
                    gearDropdown:Set(selectedGears)
                end
                if gearDropdown.Value ~= nil then
                    gearDropdown.Value = selectedGears
                end
                if Window and Window.Flags and Window.Flags.GearSelection ~= nil then
                    Window.Flags.GearSelection = selectedGears
                end
                task.wait(0.1)
                if gearDropdown.Refresh then
                    gearDropdown:Refresh(availableGears, selectedGears)
                end
            end)
        end)
    end
end

local function updateAllUI()
    task.spawn(function()
        task.wait(0.3)
        
        updateSeedDropdownProperly()
        updateGearDropdownProperly()
        
        if equipIntervalInput and equipIntervalInput.Set then
            pcall(function()
                equipIntervalInput:Set(tostring(equipInterval))
            end)
        end
        
        if currentConfig.theme then
            pcall(function()
                WindUI:SetTheme(currentConfig.theme)
                if themeDropdown and themeDropdown.Set then
                    themeDropdown:Set(currentConfig.theme)
                end
            end)
        end
    end)
end

local function resetToDefault()
    selectedSeeds = {}
    selectedGears = {}
    equipInterval = defaultConfig.equipInterval
    
    task.spawn(function()
        task.wait(0.2)
        updateSeedDropdownProperly()
        updateGearDropdownProperly()
        updateAllUI()
    end)
    
    WindUI:Notify({
        Title = "Settings Reset",
        Content = "All preferences have been reset to their default values",
        Duration = 3
    })
end

-- ✅ CRITICAL FIX: Load settings AND config BEFORE creating UI
loadSettings()

-- If autoload is enabled, load the config NOW (before UI creation)
if currentSettings.autoLoadEnabled and currentSettings.autoLoadConfig ~= "None" then
    local configName = currentSettings.autoLoadConfig
    local filesToTry = {
        "KasumiHubPVB/" .. configName .. ".json",
        configName .. ".json"
    }
    
    for _, file in pairs(filesToTry) do
        if file and isfile(file) then
            local success, result = pcall(function()
                local data = readfile(file)
                return game:GetService("HttpService"):JSONDecode(data)
            end)
            if success and result then
                currentConfig = result
                selectedSeeds = currentConfig.selectedSeeds or {}
                selectedGears = currentConfig.selectedGears or {}
                equipInterval = currentConfig.equipInterval or 20
                
                autoBuySeedsEnabled = currentConfig.autoBuySeedsEnabled or false
                autoBuyGearsEnabled = currentConfig.autoBuyGearsEnabled or false
                autoEquipEnabled = currentConfig.autoEquipEnabled or false
                autoBuyAllSeedsEnabled = currentConfig.autoBuyAllSeedsEnabled or false
                autoBuyAllGearsEnabled = currentConfig.autoBuyAllGearsEnabled or false
                antiAFKEnabled = currentConfig.antiAFKEnabled or false
                autoReconnectEnabled = currentConfig.autoReconnectEnabled or false
                
                -- Start systems immediately
                if antiAFKEnabled then startAntiAFK() end
                if autoReconnectEnabled then setupAutoReconnect() end
                if autoEquipEnabled then startEquipLoop() end
                
                print("✅ Pre-loaded config before UI:", configName)
                break
            end
        end
    end
end

-- Set theme to default
WindUI:SetTheme(currentConfig.theme or defaultConfig.theme)

Window = WindUI:CreateWindow({
    Title = "Kasumi Hub",
    Icon = "zap",
    Author = "@kasumichwan",
    Folder = "KasumiHubPVB",
    Size = UDim2.fromOffset(580, 490),
    Theme = currentConfig.theme or defaultConfig.theme,
    SideBarWidth = 180,
})

Window:SetToggleKey(Enum.KeyCode.G)

Window:Tag({
    Title = SCRIPT_VERSION .. " ",
    Color = Color3.fromHex("#00ff88")
})

Window:CreateTopbarButton("theme-switcher", "moon", function()
    local newTheme = WindUI:GetCurrentTheme() == "Dark" and "Light" or "Dark"
    WindUI:SetTheme(newTheme)
    if canChangeTheme and themeDropdown then
        themeDropdown:Set(newTheme)
    end
    WindUI:Notify({
        Title = "Theme Changed",
        Content = "Interface theme changed to: " .. newTheme,
        Duration = 2
    })
end, 990)

Window:EditOpenButton({
    Title = "Open Kasumi Hub",
    Icon = "zap",
    CornerRadius = UDim.new(0,12),
    StrokeThickness = 2,
    Color = ColorSequence.new(
        Color3.fromHex("FF0F7B"), 
        Color3.fromHex("F89B29")
    ),
    OnlyMobile = true,
    Enabled = true,
    Draggable = true,
})

-- Create tabs
local InformationTab = Window:Tab({
    Title = "Information",
    Icon = "info",
    Locked = false
})

local AutoBuyTab = Window:Tab({
    Title = "Auto Buy",
    Icon = "shopping-cart",
    Locked = false
})

local SellTab = Window:Tab({
    Title = "Sell",
    Icon = "dollar-sign",
    Locked = false
})

local AutoEquipTab = Window:Tab({
    Title = "Auto Equip",
    Icon = "zap",
    Locked = false
})

local SettingsTab = Window:Tab({
    Title = "Settings",
    Icon = "settings",
    Locked = false
})

Window:SelectTab(1)

-- Information Tab
InformationTab:Paragraph({
    Title = "Kasumi Hub for Plants vs. Brainrot",
    Desc = [[Key will auto load and work forever until I changed it so if it ask for key it means I changed it but you can get the key always.

FEATURES:

• Auto buy seeds
• Auto buy gears
• Sell all brainrot and Plants (Favorite not included)
• Auto equip your best brainrot and auto collects all money
• Anti-AFK protection (NEW)
• Auto-reconnect on disconnect (NEW)
• Save and load config
• Auto-load your preferred setup on startup
• Multiple theme options for personalization

CONTROLS:
• G = Show/Hide Interface
• Ctrl + B = Toggle Seed Auto-Buy
• Ctrl + N = Toggle Gear Auto-Buy  
• Ctrl + E = Toggle Auto Equipment + Auto Collect Money

CHANGELOGS:

v1.1
• Added Anti-AFK system
• Added Auto-reconnect feature
• Improved config save/load

Made for you with love <3]]
})

-- Auto Buy Tab
AutoBuyTab:Button({
    Title = "Open Seed Shop",
    Icon = "plant",
    Variant = "Primary",
    Callback = function()
        local success = openSeedShop()
        WindUI:Notify({
            Title = success and "Seed Shop Opened" or "Shop Access Failed",
            Content = success and "You can now browse and purchase seeds" or "Unable to access the seed shop",
            Duration = 3
        })
    end
})

AutoBuyTab:Button({
    Title = "Open Gear Shop",
    Icon = "wrench",
    Variant = "Secondary", 
    Callback = function()
        local success = openGearShop()
        WindUI:Notify({
            Title = success and "Gear Shop Opened" or "Shop Access Failed",
            Content = success and "You can now browse and purchase gears" or "Unable to access the gear shop",
            Duration = 3
        })
    end
})

AutoBuyTab:Divider()

seedDropdown = AutoBuyTab:Dropdown({
    Title = "Select Seeds",
    Values = availableSeeds,
    Value = selectedSeeds,
    SearchBarEnabled = true,
    MenuWidth = 400,
    Multi = true,
    Flag = "SeedSelection",
    Callback = function(value)
        selectedSeeds = value
    end
})

-- ✅ FIXED: Use loaded config value as initial state
autoBuyToggleSeeds = AutoBuyTab:Toggle({
    Title = "Enable Auto Buy Seeds",
    Desc = "Automatically buy your selected seeds (Use Ctrl+B to toggle quickly)",
    Value = autoBuySeedsEnabled,  -- ← FIXED: Now uses loaded value
    Callback = function(enabled)
        autoBuySeedsEnabled = enabled
        if enabled then
            WindUI:Notify({
                Title = "Seed Auto Buy Started",
                Content = string.format("Now automatically purchasing %d seed types", #selectedSeeds),
                Duration = 3
            })
        else
            WindUI:Notify({
                Title = "Seed Auto Buy Stopped",
                Content = "Automatic seed purchasing has been disabled",
                Duration = 2
            })
        end
    end
})

autoBuyAllSeedsToggle = AutoBuyTab:Toggle({
    Title = "Auto Buy All Seeds",
    Desc = "Purchase all 14 available seed types automatically",
    Value = autoBuyAllSeedsEnabled,  -- ← FIXED
    Callback = function(enabled)
        autoBuyAllSeedsEnabled = enabled
        if enabled then
            WindUI:Notify({
                Title = "Buying All Seeds",
                Content = string.format("Now purchasing all %d seed types automatically", #availableSeeds),
                Duration = 3
            })
        else
            WindUI:Notify({
                Title = "Stopped Buying All Seeds", 
                Content = "Switched back to your selected seeds only",
                Duration = 2
            })
        end
    end
})

AutoBuyTab:Divider()

gearDropdown = AutoBuyTab:Dropdown({
    Title = "Select Gears",
    Values = availableGears,
    Value = selectedGears,
    SearchBarEnabled = true,
    MenuWidth = 400,
    Multi = true,
    Flag = "GearSelection",
    Callback = function(value)
        selectedGears = value
    end
})

autoBuyToggleGears = AutoBuyTab:Toggle({
    Title = "Enable Auto Buy Gears",
    Desc = "Automatically buy your selected gears (Use Ctrl+N to toggle quickly)",
    Value = autoBuyGearsEnabled,  -- ← FIXED
    Callback = function(enabled)
        autoBuyGearsEnabled = enabled
        if enabled then
            WindUI:Notify({
                Title = "Gear Auto Buy Started",
                Content = string.format("Now automatically purchasing %d gear types", #selectedGears),
                Duration = 3
            })
        else
            WindUI:Notify({
                Title = "Gear Auto Buy Stopped",
                Content = "Automatic gear purchasing has been disabled",
                Duration = 2
            })
        end
    end
})

autoBuyAllGearsToggle = AutoBuyTab:Toggle({
    Title = "Auto Buy All Gears",
    Desc = "Purchase all 5 available gear types automatically",
    Value = autoBuyAllGearsEnabled,  -- ← FIXED
    Callback = function(enabled)
        autoBuyAllGearsEnabled = enabled
        if enabled then
            WindUI:Notify({
                Title = "Buying ALL Gears",
                Content = string.format("Now purchasing all %d gear types automatically", #availableGears),
                Duration = 3
            })
        else
            WindUI:Notify({
                Title = "Stopped Buying All Gears",
                Content = "Switched back to your selected gears only",
                Duration = 2
            })
        end
    end
})

-- Sell Tab
SellTab:Paragraph({
    Title = "Note:",
    Desc = "Your favorited brainrots are not included when you sell all.",
    Image = "dollar-sign",
    ImageSize = 20,
    Color = "White"
})

SellTab:Button({
    Title = "Sell All Brainrots",
    Icon = "brain-circuit",
    Variant = "Primary",
    Callback = function()
        local success = sellAllBrainrots()
        WindUI:Notify({
            Title = success and "Brainrots Sold" or "Sale Failed",
            Content = success and "All brainrots in your inventory have been sold" or "Unable to sell brainrots right now",
            Duration = 3
        })
    end
})

SellTab:Button({
    Title = "Sell All Plants",
    Icon = "leaf",
    Variant = "Secondary",
    Callback = function()
        local success = sellAllPlants()
        WindUI:Notify({
            Title = success and "Plants Sold" or "Sale Failed",
            Content = success and "All plants in your inventory have been sold" or "Unable to sell plants right now",
            Duration = 3
        })
    end
})

SellTab:Button({
    Title = "Sell Everything",
    Icon = "trash-2",
    Variant = "Destructive",
    Callback = function()
        local brainrotSuccess = sellAllBrainrots()
        task.wait(0.5)
        local plantSuccess = sellAllPlants()
        
        local bothSuccess = brainrotSuccess and plantSuccess
        WindUI:Notify({
            Title = bothSuccess and "Everything Sold" or "Partial Sale",
            Content = bothSuccess and "Your entire inventory has been cleared" or "Some items couldn't be sold",
            Duration = 3
        })
    end
})

-- Auto Equip Tab
AutoEquipTab:Paragraph({
    Title = "Auto Equip Brainrots + Collect All Money",
    Desc = "Useful when you need to collect all money from your brainrots",
    Image = "zap",
    ImageSize = 20,
    Color = "White"
})

equipIntervalInput = AutoEquipTab:Input({
    Title = "Auto Equip Interval (seconds)",
    Desc = "How often to automatically equip your best brainrots (default: 20 seconds)",
    Value = tostring(equipInterval)
})

AutoEquipTab:Button({
    Title = "Apply Interval",
    Icon = "clock",
    Variant = "Secondary",
    Callback = function()
        applyEquipInterval()
    end
})

autoEquipToggle = AutoEquipTab:Toggle({
    Title = "Enable Auto Equip + Collect All Money",
    Desc = "Automatically equip your best brainrot and collects all money from your brainrots (Use Ctrl+E to toggle quickly)",
    Value = autoEquipEnabled,  -- ← FIXED
    Callback = function(enabled)
        autoEquipEnabled = enabled
        if enabled then
            startEquipLoop()
            WindUI:Notify({
                Title = "Auto Equipment Started",
                Content = string.format("Now auto-equipping best brainrot every %d seconds", equipInterval),
                Duration = 3
            })
        else
            WindUI:Notify({
                Title = "Auto Equipment Stopped",
                Content = "Automatic equipment has been disabled",
                Duration = 2
            })
        end
    end
})

AutoEquipTab:Button({
    Title = "Equip Best Brainrots",
    Icon = "zap",
    Variant = "Primary",
    Callback = function()
        local success = equipBest()
        WindUI:Notify({
            Title = success and "Equip Success" or "Equip Failed",
            Content = success and "Your best brainrots has been equipped" or "Unable to update equipment right now",
            Duration = 3
        })
    end
})

-- Settings Tab
SettingsTab:Paragraph({
    Title = "Personalization & Data Management",
    Desc = "Customize your interface and manage your saved configurations for quick setup across sessions.",
    Image = "palette",
    ImageSize = 20,
    Color = "White"
})

local themes = {}
for themeName, _ in pairs(WindUI:GetThemes()) do
    table.insert(themes, themeName)
end
table.sort(themes)

themeDropdown = SettingsTab:Dropdown({
    Title = "Interface Theme",
    Values = themes,
    SearchBarEnabled = true,
    MenuWidth = 280,
    Value = WindUI:GetCurrentTheme(),
    Callback = function(theme)
        canChangeDropdown = false
        WindUI:SetTheme(theme)
        WindUI:Notify({
            Title = "Theme Applied",
            Content = "Interface changed to " .. theme .. " theme",
            Icon = "palette",
            Duration = 2
        })
        canChangeDropdown = true
    end
})

SettingsTab:Divider()

SettingsTab:Paragraph({
    Title = "Connection & Activity",
    Desc = "Manage anti-AFK and auto-reconnect features for uninterrupted gameplay.",
    Image = "wifi",
    ImageSize = 20,
    Color = "White"
})

autoReconnectToggle = SettingsTab:Toggle({
    Title = "Enable Auto-Reconnect",
    Desc = "Automatically rejoin the game if disconnected due to network issues",
    Value = autoReconnectEnabled,  -- ← FIXED
    Callback = function(enabled)
        autoReconnectEnabled = enabled
        reconnectAttempts = 0
        if enabled then
            setupAutoReconnect()
            WindUI:Notify({
                Title = "Auto-Reconnect Enabled",
                Content = string.format("Will attempt to reconnect up to %d times if disconnected", MAX_RECONNECT_ATTEMPTS),
                Duration = 3
            })
        else
            WindUI:Notify({
                Title = "Auto-Reconnect Disabled",
                Content = "Manual reconnection required if disconnected",
                Duration = 2
            })
        end
    end
})

antiAFKToggle = SettingsTab:Toggle({
    Title = "Enable Anti-AFK Protection",
    Desc = "Prevents Roblox from kicking you for inactivity",
    Value = antiAFKEnabled,  -- ← FIXED
    Callback = function(enabled)
        antiAFKEnabled = enabled
        if enabled then
            startAntiAFK()
            WindUI:Notify({
                Title = "Anti-AFK Enabled",
                Content = "You will no longer be kicked for inactivity",
                Duration = 3
            })
        else
            WindUI:Notify({
                Title = "Anti-AFK Disabled",
                Content = "Inactivity kick protection has been turned off",
                Duration = 2
            })
        end
    end
})

SettingsTab:Divider()

SettingsTab:Paragraph({
    Title = "Startup Configuration",
    Desc = "Choose a saved configuration to automatically load when you start the script.",
    Image = "settings-2",
    ImageSize = 20,
    Color = "White"
})

autoLoadToggle = SettingsTab:Toggle({
    Title = "Enable Auto-Load",
    Desc = "Automatically load your chosen configuration when the script starts",
    Value = currentSettings.autoLoadEnabled or false,
    Callback = function(state)
        currentSettings.autoLoadEnabled = state
        saveSettings()
        WindUI:Notify({
            Title = "Auto-Load " .. (state and "Enabled" or "Disabled"),
            Content = "Startup configuration " .. (state and "will now load automatically" or "disabled"),
            Duration = 2
        })
    end
})

local availableConfigs = getAvailableConfigs()
autoLoadDropdown = SettingsTab:Dropdown({
    Title = "Startup Configuration",
    Values = availableConfigs,
    Value = currentSettings.autoLoadConfig or "None",
    SearchBarEnabled = true,
    MenuWidth = 280,
    Callback = function(config)
        currentSettings.autoLoadConfig = config
        saveSettings()
        WindUI:Notify({
            Title = "Startup Config Set",
            Content = "Will auto-load: " .. config,
            Duration = 2
        })
    end
})

SettingsTab:Button({
    Title = "Refresh Configuration List",
    Icon = "refresh-cw",
    Callback = function()
        local newConfigs = getAvailableConfigs()
        if autoLoadDropdown and autoLoadDropdown.Refresh then
            autoLoadDropdown:Refresh(newConfigs)
        end
        WindUI:Notify({
            Title = "List Updated",
            Content = "Found " .. math.max(0, #newConfigs - 1) .. " saved configurations",
            Duration = 2
        })
    end
})

SettingsTab:Divider()

SettingsTab:Paragraph({
    Title = "Save & Load Configurations",
    Desc = "Save your current preferences or load previously saved configurations for quick setup.",
    Image = "save",
    ImageSize = 20,
    Color = "White"
})

local configName = "kasumi-hub-pvb"

ConfigNameInput = SettingsTab:Input({
    Title = "Configuration Name",
    Value = configName,
    Callback = function(value)
        configName = value or "kasumi-hub-pvb"
    end
})

SettingsTab:Button({
    Title = "Save Current Settings",
    Icon = "save",
    Variant = "Primary",
    Callback = function()
        local name = configName
        if ConfigNameInput and ConfigNameInput.Get then
            name = ConfigNameInput:Get() or configName
        end
        saveConfig(name)
    end
})

SettingsTab:Button({
    Title = "Load Saved Settings",
    Icon = "folder-open",
    Variant = "Secondary",
    Callback = function()
        local name = configName
        if ConfigNameInput and ConfigNameInput.Get then
            name = ConfigNameInput:Get() or configName
        end
        
        local filesToTry = {"KasumiHubPVB/" .. name .. ".json", name .. ".json"}
        local loaded = false
        for _, file in pairs(filesToTry) do
            if file and isfile(file) then
                local success, result = pcall(function()
                    return game:GetService("HttpService"):JSONDecode(readfile(file))
                end)
                if success and result then
                    currentConfig = result
                    selectedSeeds = result.selectedSeeds or {}
                    selectedGears = result.selectedGears or {}
                    equipInterval = result.equipInterval or 20
                    
                    autoBuySeedsEnabled = result.autoBuySeedsEnabled or false
                    autoBuyGearsEnabled = result.autoBuyGearsEnabled or false
                    autoEquipEnabled = result.autoEquipEnabled or false
                    autoBuyAllSeedsEnabled = result.autoBuyAllSeedsEnabled or false
                    autoBuyAllGearsEnabled = result.autoBuyAllGearsEnabled or false
                    antiAFKEnabled = result.antiAFKEnabled or false
                    autoReconnectEnabled = result.autoReconnectEnabled or false
                    
                    -- Start systems
                    if antiAFKEnabled then startAntiAFK() end
                    if autoReconnectEnabled then setupAutoReconnect() end
                    if autoEquipEnabled then startEquipLoop() end
                    
                    -- Update toggles
                    if autoBuyToggleSeeds then autoBuyToggleSeeds.Value = autoBuySeedsEnabled end
                    if autoBuyToggleGears then autoBuyToggleGears.Value = autoBuyGearsEnabled end
                    if autoEquipToggle then autoEquipToggle.Value = autoEquipEnabled end
                    if autoBuyAllSeedsToggle then autoBuyAllSeedsToggle.Value = autoBuyAllSeedsEnabled end
                    if autoBuyAllGearsToggle then autoBuyAllGearsToggle.Value = autoBuyAllGearsEnabled end
                    if antiAFKToggle then antiAFKToggle.Value = antiAFKEnabled end
                    if autoReconnectToggle then autoReconnectToggle.Value = autoReconnectEnabled end
                    
                    updateAllUI()
                    loaded = true
                    WindUI:Notify({
                        Title = "Settings Loaded",
                        Content = name .. " loaded - Seeds: " .. #selectedSeeds .. ", Gears: " .. #selectedGears,
                        Duration = 3
                    })
                    break
                end
            end
        end
        
        if not loaded then
            WindUI:Notify({
                Title = "Load Failed", 
                Content = "Could not find: " .. name,
                Duration = 3
            })
        end
    end
})

SettingsTab:Button({
    Title = "Reset All Settings",
    Icon = "rotate-ccw",
    Variant = "Destructive",
    Callback = function()
        resetToDefault()
    end
})

-- Initialize keybinds
setupKeybinds()

-- UNIFIED AUTO-BUY LOOP
task.spawn(function()
    while true do
        local shouldBuySeeds = (autoBuySeedsEnabled and #selectedSeeds > 0) or autoBuyAllSeedsEnabled
        local shouldBuyGears = (autoBuyGearsEnabled and #selectedGears > 0) or autoBuyAllGearsEnabled
        
        if shouldBuySeeds or shouldBuyGears then
            local purchased = false
            
            if shouldBuySeeds and shouldBuyGears then
                if isAlternatingSeed then
                    local seedList = autoBuyAllSeedsEnabled and availableSeeds or selectedSeeds
                    if #seedList > 0 then
                        local seedIndex = ((currentItemIndex - 1) % #seedList) + 1
                        local seedName = seedList[seedIndex]
                        local success = buySeed(seedName)
                        if success then
                            purchased = true
                        end
                    end
                    isAlternatingSeed = false
                else
                    local gearList = autoBuyAllGearsEnabled and availableGears or selectedGears
                    if #gearList > 0 then
                        local gearIndex = ((currentItemIndex - 1) % #gearList) + 1
                        local gearName = gearList[gearIndex]
                        local success = buyGear(gearName)
                        if success then
                            purchased = true
                        end
                    end
                    isAlternatingSeed = true
                end
            elseif shouldBuySeeds then
                local seedList = autoBuyAllSeedsEnabled and availableSeeds or selectedSeeds
                if #seedList > 0 then
                    local seedIndex = ((currentItemIndex - 1) % #seedList) + 1
                    local seedName = seedList[seedIndex]
                    local success = buySeed(seedName)
                    if success then
                        purchased = true
                    end
                end
            elseif shouldBuyGears then
                local gearList = autoBuyAllGearsEnabled and availableGears or selectedGears
                if #gearList > 0 then
                    local gearIndex = ((currentItemIndex - 1) % #gearList) + 1
                    local gearName = gearList[gearIndex]
                    local success = buyGear(gearName)
                    if success then
                        purchased = true
                    end
                end
            end
            
            if purchased then
                purchaseCount = purchaseCount + 1
                
                if purchaseCount >= ITEMS_PER_ROUND then
                    purchaseCount = 0
                    currentItemIndex = currentItemIndex + 1
                    
                    local totalItems = 0
                    if shouldBuySeeds and shouldBuyGears then
                        totalItems = math.max(
                            autoBuyAllSeedsEnabled and #availableSeeds or #selectedSeeds,
                            autoBuyAllGearsEnabled and #availableGears or #selectedGears
                        )
                    elseif shouldBuySeeds then
                        totalItems = autoBuyAllSeedsEnabled and #availableSeeds or #selectedSeeds
                    elseif shouldBuyGears then
                        totalItems = autoBuyAllGearsEnabled and #availableGears or #selectedGears
                    end
                    
                    if currentItemIndex > totalItems then
                        currentItemIndex = 1
                        task.wait(CYCLE_INTERVAL / 1000)
                    end
                end
            end
            
            task.wait(PURCHASE_DELAY / 1000)
        else
            task.wait(2)
        end
    end
end)

-- Startup notification
task.spawn(function()
    task.wait(1)
    WindUI:Notify({
        Title = "Kasumi Hub " .. SCRIPT_VERSION .. " Ready",
        Content = string.format("Welcome! %s\nSeeds: %d | Gears: %d | Interval: %ds\n\nPress G to toggle interface", 
            currentSettings.autoLoadEnabled and currentSettings.autoLoadConfig ~= "None" and ("Auto-loaded: " .. currentSettings.autoLoadConfig) or "Enjoy using my scripts!",
            #selectedSeeds,
            #selectedGears,
            equipInterval),
        Duration = 8
    })
end)

task.spawn(function()
    while task.wait(0.1) do
    end
end)

Features & Information

Kasumi Hub for Plants vs. Brainrot CRACKED OPEN SOURCE

Community Comments (3)

watafacwatafacOct 6, 2025

bro cracked alots :))

watafacwatafacOct 6, 2025

w script

UnsingsUnsingsNov 18, 2025

W script but slightly out dated and doesnt have star fruit or king limone auto buy

More Scripts for [🐰] Plants & Brainrots 🌻