PoopyFart
Key RequiredUniversal

PoopyFart

Universal Scriptm4arm4ar 916 views 323 executesUpdated Jan 9, 2026
Unrated
00
Lua Script•2804 lines (102902 chars)
local HttpService = game:GetService("HttpService")
local CoreGui = game:GetService("CoreGui")
local Players = game:GetService("Players")
local RbxAnalyticsService = game:GetService("RbxAnalyticsService")

local cloneref = cloneref or function(o) return o end
local gethui = gethui or function() return CoreGui end

local CoreGui = cloneref(game:GetService("CoreGui"))
local Players = cloneref(game:GetService("Players"))
local VirtualInputManager = cloneref(game:GetService("VirtualInputManager"))
local UserInputService = cloneref(game:GetService("UserInputService"))
local RunService = cloneref(game:GetService("RunService"))
local TweenService = cloneref(game:GetService("TweenService"))
local LogService = cloneref(game:GetService("LogService"))
local GuiService = cloneref(game:GetService("GuiService"))

local request = (syn and syn.request) or (http and http.request) or http_request or (fluxus and fluxus.request) or request

local TOGGLE_KEY = Enum.KeyCode.RightControl
local MIN_CPM = 50
local MAX_CPM_LEGIT = 1500
local MAX_CPM_BLATANT = 3000

math.randomseed(os.time())

local THEME = {
    Background = Color3.fromRGB(20, 20, 24),
    ItemBG = Color3.fromRGB(32, 32, 38),
    Accent = Color3.fromRGB(114, 100, 255),
    Text = Color3.fromRGB(240, 240, 240),
    SubText = Color3.fromRGB(150, 150, 160),
    Success = Color3.fromRGB(100, 255, 140),
    Warning = Color3.fromRGB(255, 200, 80),
    Slider = Color3.fromRGB(60, 60, 70)
}

local function ColorToRGB(c)
    return string.format("%d,%d,%d", math.floor(c.R * 255), math.floor(c.G * 255), math.floor(c.B * 255))
end

local ConfigFile = "WordHelper_Longest_Config.json" -- Changed filename to not overwrite original
local Config = {
    CPM = 450, -- Lowered slightly as longer words take longer
    Blatant = false,
    Humanize = true,
    FingerModel = true,
    SortMode = "Longest", -- CHANGED TO LONGEST DEFAULT
    SuffixMode = "",
    LengthMode = 0,
    AutoPlay = false,
    AutoJoin = false,
    AutoJoinSettings = {
        _1v1 = true,
        _4p = true,
        _8p = true
    },
    PanicMode = true,
    ShowKeyboard = false,
    ErrorRate = 5,
    ThinkDelay = 0.8,
    RiskyMistakes = false,
    CustomWords = {},
    MinTypeSpeed = 50,
    MaxTypeSpeed = 3000,
    KeyboardLayout = "QWERTY"
}

local function SaveConfig()
    if writefile then
        writefile(ConfigFile, HttpService:JSONEncode(Config))
    end
end

local function LoadConfig()
    if isfile and isfile(ConfigFile) then
        local success, decoded = pcall(function() return HttpService:JSONDecode(readfile(ConfigFile)) end)
        if success and decoded then
            for k, v in pairs(decoded) do Config[k] = v end
        end
    end
end
LoadConfig()

-- Ensure Longest on start
Config.SortMode = "Longest" 

local currentCPM = Config.CPM
local isBlatant = Config.Blatant
local useHumanization = Config.Humanize
local useFingerModel = Config.FingerModel
local sortMode = Config.SortMode
local suffixMode = Config.SuffixMode or ""
local lengthMode = Config.LengthMode or 0
local autoPlay = Config.AutoPlay
local autoJoin = Config.AutoJoin
local panicMode = Config.PanicMode
local showKeyboard = Config.ShowKeyboard
local errorRate = Config.ErrorRate
local thinkDelayCurrent = Config.ThinkDelay
local riskyMistakes = Config.RiskyMistakes
local keyboardLayout = Config.KeyboardLayout or "QWERTY"

local isTyping = false
local isAutoPlayScheduled = false
local lastTypingStart = 0
local runConn = nil
local inputConn = nil
local logConn = nil
local unloaded = false
local isMyTurnLogDetected = false
local logRequiredLetters = ""
local turnExpiryTime = 0
local Blacklist = {}
local UsedWords = {}
local RandomOrderCache = {}
local RandomPriority = {}
local lastDetected = "---"
local lastLogicUpdate = 0
local lastAutoJoinCheck = 0
local lastWordCheck = 0
local cachedDetected = ""
local cachedCensored = false
local LOGIC_RATE = 0.1
local AUTO_JOIN_RATE = 0.5
local UpdateList
local ButtonCache = {}
local ButtonData = {}
local JoinDebounce = {}
local thinkDelayMin = 0.4
local thinkDelayMax = 1.2

local listUpdatePending = false
local forceUpdateList = false
local lastInputTime = 0
local LIST_DEBOUNCE = 0.05
local currentBestMatch = nil

if logConn then logConn:Disconnect() end
logConn = LogService.MessageOut:Connect(function(message, type)
    local wordPart, timePart = message:match("Word:%s*([A-Za-z]+)%s+Time to respond:%s*(%d+)")
    if wordPart and timePart then
        isMyTurnLogDetected = true
        logRequiredLetters = wordPart
        turnExpiryTime = tick() + tonumber(timePart)
    end
end)

local url = "https://raw.githubusercontent.com/skrylor/english-words/refs/heads/main/merged_english.txt"
local fileName = "ultimate_words_v4.txt"

-- Temporary Loading UI
local LoadingGui = Instance.new("ScreenGui")
LoadingGui.Name = "WordHelperLoading"
local success, parent = pcall(function() return gethui() end)
if not success or not parent then parent = game:GetService("CoreGui") end
LoadingGui.Parent = parent
LoadingGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling

local LoadingFrame = Instance.new("Frame", LoadingGui)
LoadingFrame.Size = UDim2.new(0, 300, 0, 100)
LoadingFrame.Position = UDim2.new(0.5, -150, 0.4, 0)
LoadingFrame.BackgroundColor3 = THEME.Background
LoadingFrame.BorderSizePixel = 0
Instance.new("UICorner", LoadingFrame).CornerRadius = UDim.new(0, 10)
local LStroke = Instance.new("UIStroke", LoadingFrame)
LStroke.Color = THEME.Accent
LStroke.Transparency = 0.5
LStroke.Thickness = 2

local LoadingTitle = Instance.new("TextLabel", LoadingFrame)
LoadingTitle.Size = UDim2.new(1, 0, 0, 40)
LoadingTitle.BackgroundTransparency = 1
LoadingTitle.Text = "WordHelper Longest V4"
LoadingTitle.TextColor3 = THEME.Accent
LoadingTitle.Font = Enum.Font.GothamBold
LoadingTitle.TextSize = 18

local LoadingStatus = Instance.new("TextLabel", LoadingFrame)
LoadingStatus.Size = UDim2.new(1, -20, 0, 30)
LoadingStatus.Position = UDim2.new(0, 10, 0, 50)
LoadingStatus.BackgroundTransparency = 1
LoadingStatus.Text = "Initializing..."
LoadingStatus.TextColor3 = THEME.Text
LoadingStatus.Font = Enum.Font.Gotham
LoadingStatus.TextSize = 14

local function UpdateStatus(text, color)
    LoadingStatus.Text = text
    if color then LoadingStatus.TextColor3 = color end
    game:GetService("RunService").RenderStepped:Wait()
end

-- Startup: Always fetch fresh word list
local function FetchWords()
    UpdateStatus("Fetching latest word list...", THEME.Warning)
    local success, res = pcall(function()
        return request({Url = url, Method = "GET"})
    end)
    
    if success and res and res.Body then
        writefile(fileName, res.Body)
        UpdateStatus("Fetched successfully!", THEME.Success)
    else
        UpdateStatus("Fetch failed! Using cached.", Color3.fromRGB(255, 80, 80))
    end
    task.wait(0.5)
end

FetchWords()

local Words = {}
local SeenWords = {}

local function LoadList(fname)
    UpdateStatus("Parsing word list...", THEME.Warning)
    if isfile(fname) then
        local content = readfile(fname)
        for w in content:gmatch("[^\r\n]+") do
            local clean = w:gsub("[%s%c]+", ""):lower()
            if #clean > 0 and not SeenWords[clean] then
                SeenWords[clean] = true
                table.insert(Words, clean)
            end
        end
        UpdateStatus("Loaded " .. #Words .. " words!", THEME.Success)
    else
         UpdateStatus("No word list found!", Color3.fromRGB(255, 80, 80))
    end
    task.wait(1)
end

LoadList(fileName)

if LoadingGui then LoadingGui:Destroy() end

table.sort(Words)
Buckets = {}
for _, w in ipairs(Words) do
    local c = w:sub(1,1) or ""
    if c == "" then c = "#" end
    Buckets[c] = Buckets[c] or {}
    table.insert(Buckets[c], w)
end

if Config.CustomWords then
    for _, w in ipairs(Config.CustomWords) do
        local clean = w:gsub("[%s%c]+", ""):lower()
        if #clean > 0 and not SeenWords[clean] then
            SeenWords[clean] = true
            table.insert(Words, clean)
            local c = clean:sub(1,1) or ""
            if c == "" then c = "#" end
            Buckets[c] = Buckets[c] or {}
            table.insert(Buckets[c], clean)
        end
    end
end

-- Clear memory
SeenWords = nil

local function shuffleTable(t)
    local n = #t
    for i = n, 2, -1 do
        local j = math.random(i)
        t[i], t[j] = t[j], t[i]
    end
    return t
end

local HardLetterScores = {
    x = 10, z = 9, q = 9, j = 8, v = 6, k = 5, b = 4, f = 3, w = 3,
    y = 2, g = 2, p = 2
}

local function GetKillerScore(word)
    local lastChar = word:sub(-1)
    return HardLetterScores[lastChar] or 0
end

local function Tween(obj, props, time)
    TweenService:Create(obj, TweenInfo.new(time or 0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), props):Play()
end

local function GetCurrentGameWord(providedFrame)
    local frame = providedFrame
    if not frame then
        local player = Players.LocalPlayer
        local gui = player and player:FindFirstChild("PlayerGui")
        local inGame = gui and gui:FindFirstChild("InGame")
        frame = inGame and inGame:FindFirstChild("Frame")
    end

    local container = frame and frame:FindFirstChild("CurrentWord")
    if not container then return "", false end
    
    local detected = ""
    local censored = false
    
    local children = container:GetChildren()
    local letterData = {}
    
    for _, c in ipairs(children) do
        if c:IsA("GuiObject") and c.Visible then
            local txt = c:FindFirstChild("Letter")
            if txt and txt:IsA("TextLabel") and txt.TextTransparency < 1 then
                table.insert(letterData, {
                    Obj = c,
                    Txt = txt,
                    X = c.AbsolutePosition.X,
                    Id = tonumber(c.Name) or 0
                })
            end
        end
    end
    
    table.sort(letterData, function(a,b)
        if math.abs(a.X - b.X) > 2 then
            return a.X < b.X
        end
        return a.Id < b.Id
    end)

    for _, data in ipairs(letterData) do
        local t = tostring(data.Txt.Text)
        if t:find("#") or t:find("%*") then censored = true end
        detected = detected .. t
    end
    
    return detected:lower():gsub(" ", ""), censored
end

local function GetTurnInfo(providedFrame)
    if isMyTurnLogDetected then
        if tick() < turnExpiryTime then
            return true, logRequiredLetters
        else
            isMyTurnLogDetected = false
        end
    end

    local frame = providedFrame
    if not frame then
        local player = Players.LocalPlayer
        local gui = player and player:FindFirstChild("PlayerGui")
        local inGame = gui and gui:FindFirstChild("InGame")
        frame = inGame and inGame:FindFirstChild("Frame")
    end

    local typeLbl = frame and frame:FindFirstChild("Type")
    
    if typeLbl and typeLbl:IsA("TextLabel") then
        local text = typeLbl.Text
        local player = Players.LocalPlayer
        if text:sub(1, #player.Name) == player.Name or text:sub(1, #player.DisplayName) == player.DisplayName then
            local char = text:match("starting with:%s*([A-Za-z])")
            return true, char
        end
    end
    return false, nil
end

local function GetSecureParent()
    local success, result = pcall(function()
        return gethui()
    end)
    if success and result then return result end
    
    success, result = pcall(function()
        return CoreGui
    end)
    if success and result then return result end
    
    return Players.LocalPlayer.PlayerGui
end

local ParentTarget = GetSecureParent()
local GuiName = tostring(math.random(1000000, 9999999))

local env = (getgenv and getgenv()) or _G

if env.WordHelperInstance and env.WordHelperInstance.Parent then
    env.WordHelperInstance:Destroy()
end

local ScreenGui = Instance.new("ScreenGui")
ScreenGui.Name = GuiName
ScreenGui.Parent = ParentTarget
ScreenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling

env.WordHelperInstance = ScreenGui

local ToastContainer = Instance.new("Frame", ScreenGui)
ToastContainer.Name = "ToastContainer"
ToastContainer.Size = UDim2.new(0, 300, 1, 0)
ToastContainer.Position = UDim2.new(1, -320, 0, 20)
ToastContainer.BackgroundTransparency = 1
ToastContainer.ZIndex = 100

local function ShowToast(message, type)
    local toast = Instance.new("Frame")
    toast.Size = UDim2.new(1, 0, 0, 40)
    toast.BackgroundColor3 = THEME.ItemBG
    toast.BorderSizePixel = 0
    toast.BackgroundTransparency = 1
    toast.Parent = ToastContainer
    
    local stroke = Instance.new("UIStroke", toast)
    stroke.Thickness = 1.5
    stroke.Transparency = 1
    
    local color = THEME.Text
    if type == "success" then color = THEME.Success
    elseif type == "warning" then color = THEME.Warning
    elseif type == "error" then color = Color3.fromRGB(255, 80, 80)
    end
    stroke.Color = color
    
    Instance.new("UICorner", toast).CornerRadius = UDim.new(0, 6)
    
    local lbl = Instance.new("TextLabel", toast)
    lbl.Size = UDim2.new(1, -20, 1, 0)
    lbl.Position = UDim2.new(0, 10, 0, 0)
    lbl.BackgroundTransparency = 1
    lbl.Text = message
    lbl.TextColor3 = color
    lbl.Font = Enum.Font.GothamMedium
    lbl.TextSize = 14
    lbl.TextWrapped = true
    lbl.TextTransparency = 1
    
    Tween(toast, {BackgroundTransparency = 0.1}, 0.3)
    Tween(lbl, {TextTransparency = 0}, 0.3)
    Tween(stroke, {Transparency = 0.2}, 0.3)
    
    task.delay(3, function()
        if toast and toast.Parent then
            Tween(toast, {BackgroundTransparency = 1}, 0.5)
            Tween(lbl, {TextTransparency = 1}, 0.5)
            Tween(stroke, {Transparency = 1}, 0.5)
            task.wait(0.5)
            toast:Destroy()
        end
    end)
end

local MainFrame = Instance.new("Frame")
MainFrame.Name = "MainFrame"
MainFrame.Size = UDim2.new(0, 300, 0, 500)
MainFrame.Position = UDim2.new(0.8, -50, 0.4, 0)
MainFrame.BackgroundColor3 = THEME.Background
MainFrame.BorderSizePixel = 0
MainFrame.Active = true
MainFrame.ClipsDescendants = true
MainFrame.Parent = ScreenGui

local function EnableDragging(frame)
    local dragging, dragInput, dragStart, startPos
    local function Update(input)
        local delta = input.Position - dragStart
        frame.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y)
    end
    
    frame.InputBegan:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
            dragging = true
            dragStart = input.Position
            startPos = frame.Position
            
            input.Changed:Connect(function()
                if input.UserInputState == Enum.UserInputState.End then
                    dragging = false
                end
            end)
        end
    end)
    
    frame.InputChanged:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then
            dragInput = input
        end
    end)
    
    UserInputService.InputChanged:Connect(function(input)
        if input == dragInput and dragging then
            Update(input)
        end
    end)
end

EnableDragging(MainFrame)

Instance.new("UICorner", MainFrame).CornerRadius = UDim.new(0, 10)
local Stroke = Instance.new("UIStroke", MainFrame)
Stroke.Color = THEME.Accent
Stroke.Transparency = 0.5
Stroke.Thickness = 2

local Header = Instance.new("Frame", MainFrame)
Header.Size = UDim2.new(1, 0, 0, 45)
Header.BackgroundColor3 = THEME.ItemBG
Header.BorderSizePixel = 0

local Title = Instance.new("TextLabel", Header)
Title.Text = "Word<font color=\"rgb(114,100,255)\">Longest</font> V4"
Title.RichText = true
Title.Font = Enum.Font.GothamBold
Title.TextSize = 18
Title.TextColor3 = THEME.Text
Title.Size = UDim2.new(1, -50, 1, 0)
Title.Position = UDim2.new(0, 15, 0, 0)
Title.BackgroundTransparency = 1
Title.TextXAlignment = Enum.TextXAlignment.Left

local MinBtn = Instance.new("TextButton", Header)
MinBtn.Text = "-"
MinBtn.Font = Enum.Font.GothamBold
MinBtn.TextSize = 24
MinBtn.TextColor3 = THEME.SubText
MinBtn.Size = UDim2.new(0, 45, 1, 0)
MinBtn.Position = UDim2.new(1, -90, 0, 0)
MinBtn.BackgroundTransparency = 1

local CloseBtn = Instance.new("TextButton", Header)
CloseBtn.Text = "X"
CloseBtn.Font = Enum.Font.GothamBold
CloseBtn.TextSize = 18
CloseBtn.TextColor3 = Color3.fromRGB(255, 80, 80)
CloseBtn.Size = UDim2.new(0, 45, 1, 0)
CloseBtn.Position = UDim2.new(1, -45, 0, 0)
CloseBtn.BackgroundTransparency = 1

CloseBtn.MouseButton1Click:Connect(function()
    unloaded = true
    if runConn then runConn:Disconnect() runConn = nil end
    if inputConn then inputConn:Disconnect() inputConn = nil end
    if logConn then logConn:Disconnect() logConn = nil end
    
    for _, btn in ipairs(ButtonCache) do btn:Destroy() end
    table.clear(ButtonCache)

    if ScreenGui and ScreenGui.Parent then ScreenGui:Destroy() end
end)

local StatusFrame = Instance.new("Frame", MainFrame)
StatusFrame.Size = UDim2.new(1, -30, 0, 24)
StatusFrame.Position = UDim2.new(0, 15, 0, 55)
StatusFrame.BackgroundTransparency = 1

local StatusDot = Instance.new("Frame", StatusFrame)
StatusDot.Size = UDim2.new(0, 8, 0, 8)
StatusDot.Position = UDim2.new(0, 0, 0.5, -4)
StatusDot.BackgroundColor3 = THEME.SubText
Instance.new("UICorner", StatusDot).CornerRadius = UDim.new(1, 0)

local StatusText = Instance.new("TextLabel", StatusFrame)
StatusText.Text = "Idle..."
StatusText.RichText = true
StatusText.Font = Enum.Font.Gotham
StatusText.TextSize = 12
StatusText.TextColor3 = THEME.SubText
StatusText.Size = UDim2.new(1, -15, 1, 0)
StatusText.Position = UDim2.new(0, 15, 0, 0)
StatusText.BackgroundTransparency = 1
StatusText.TextXAlignment = Enum.TextXAlignment.Left

local SearchFrame = Instance.new("Frame", MainFrame)
SearchFrame.Size = UDim2.new(1, -10, 0, 26)
SearchFrame.Position = UDim2.new(0, 5, 0, 82)
SearchFrame.BackgroundColor3 = THEME.ItemBG
Instance.new("UICorner", SearchFrame).CornerRadius = UDim.new(0, 6)

local SearchBox = Instance.new("TextBox", SearchFrame)
SearchBox.Size = UDim2.new(1, -20, 1, 0)
SearchBox.Position = UDim2.new(0, 10, 0, 0)
SearchBox.BackgroundTransparency = 1
SearchBox.Font = Enum.Font.Gotham
SearchBox.TextSize = 14
SearchBox.TextColor3 = THEME.Text
SearchBox.PlaceholderText = "Search words..."
SearchBox.PlaceholderColor3 = THEME.SubText
SearchBox.Text = ""
SearchBox.TextXAlignment = Enum.TextXAlignment.Left

SearchBox:GetPropertyChangedSignal("Text"):Connect(function()
    if UpdateList then
        UpdateList(lastDetected, lastRequiredLetter)
    end
end)

local ScrollList = Instance.new("ScrollingFrame", MainFrame)
ScrollList.Size = UDim2.new(1, -10, 1, -220)
ScrollList.Position = UDim2.new(0, 5, 0, 115)
ScrollList.BackgroundTransparency = 1
ScrollList.ScrollBarThickness = 3
ScrollList.ScrollBarImageColor3 = THEME.Accent
ScrollList.CanvasSize = UDim2.new(0,0,0,0)

local UIListLayout = Instance.new("UIListLayout", ScrollList)
UIListLayout.SortOrder = Enum.SortOrder.LayoutOrder
UIListLayout.Padding = UDim.new(0, 4)

local SettingsFrame = Instance.new("Frame", MainFrame)
SettingsFrame.BackgroundColor3 = THEME.ItemBG
SettingsFrame.BorderSizePixel = 0
SettingsFrame.ClipsDescendants = true

local SlidersFrame = Instance.new("Frame", SettingsFrame)
SlidersFrame.Size = UDim2.new(1, 0, 0, 125)
SlidersFrame.BackgroundTransparency = 1

local TogglesFrame = Instance.new("Frame", SettingsFrame)
TogglesFrame.Size = UDim2.new(1, 0, 0, 310)
TogglesFrame.Position = UDim2.new(0, 0, 0, 125)
TogglesFrame.BackgroundTransparency = 1
TogglesFrame.Visible = false

local sep = Instance.new("Frame", SettingsFrame)
sep.Size = UDim2.new(1, 0, 0, 1)
sep.BackgroundColor3 = Color3.fromRGB(45, 45, 50)

local settingsCollapsed = true
local function UpdateLayout()
    if settingsCollapsed then
        Tween(SettingsFrame, {Size = UDim2.new(1, 0, 0, 125), Position = UDim2.new(0, 0, 1, -125)})
        Tween(ScrollList, {Size = UDim2.new(1, -10, 1, -245)})
        TogglesFrame.Visible = false
    else
        Tween(SettingsFrame, {Size = UDim2.new(1, 0, 0, 435), Position = UDim2.new(0, 0, 1, -435)})
        Tween(ScrollList, {Size = UDim2.new(1, -10, 1, -555)})
        TogglesFrame.Visible = true
    end
end
UpdateLayout()

local ExpandBtn = Instance.new("TextButton", SlidersFrame)
ExpandBtn.Text = "v Show Settings v"
ExpandBtn.Font = Enum.Font.GothamBold
ExpandBtn.TextSize = 14
ExpandBtn.TextColor3 = THEME.Accent
ExpandBtn.BackgroundColor3 = Color3.fromRGB(40, 40, 45)
ExpandBtn.BackgroundTransparency = 0.5
ExpandBtn.Size = UDim2.new(1, -10, 0, 30)
ExpandBtn.Position = UDim2.new(0, 5, 1, -35)
Instance.new("UICorner", ExpandBtn).CornerRadius = UDim.new(0, 6)

ExpandBtn.MouseButton1Click:Connect(function()
    settingsCollapsed = not settingsCollapsed
    ExpandBtn.Text = settingsCollapsed and "v Show Settings v" or "^ Hide Settings ^"
    UpdateLayout()
end)

local function SetupSlider(btn, bg, fill, callback)
    btn.MouseButton1Down:Connect(function()
        local move, rel
        local function Update()
            local mousePos = UserInputService:GetMouseLocation()
            local relX = math.clamp(mousePos.X - bg.AbsolutePosition.X, 0, bg.AbsoluteSize.X)
            local pct = relX / bg.AbsoluteSize.X
            callback(pct)
            Config.CPM = currentCPM
            Config.ErrorRate = errorRate
            Config.ThinkDelay = thinkDelayCurrent
        end
        Update()
        move = RunService.RenderStepped:Connect(Update)
        rel = UserInputService.InputEnded:Connect(function(inp)
            if inp.UserInputType == Enum.UserInputType.MouseButton1 or inp.UserInputType == Enum.UserInputType.Touch then
                if move then move:Disconnect() move = nil end
                if rel then rel:Disconnect() rel = nil end
                SaveConfig()
            end
        end)
    end)
end

local KeyboardFrame = Instance.new("Frame", ScreenGui)
KeyboardFrame.Name = "KeyboardFrame"
KeyboardFrame.Size = UDim2.new(0, 400, 0, 160)
KeyboardFrame.Position = UDim2.new(0.1, 0, 0.5, -80)
KeyboardFrame.BackgroundColor3 = THEME.Background
KeyboardFrame.Visible = showKeyboard
EnableDragging(KeyboardFrame)
Instance.new("UICorner", KeyboardFrame).CornerRadius = UDim.new(0, 8)
local KStroke = Instance.new("UIStroke", KeyboardFrame)
KStroke.Color = THEME.Accent
KStroke.Transparency = 0.6
KStroke.Thickness = 2

local Keys = {}
local function CreateKey(char, pos, size)
    local k = Instance.new("Frame", KeyboardFrame)
    k.Size = size or UDim2.new(0, 30, 0, 30)
    k.Position = pos
    k.BackgroundColor3 = THEME.ItemBG
    Instance.new("UICorner", k).CornerRadius = UDim.new(0, 4)
    
    local l = Instance.new("TextLabel", k)
    l.Size = UDim2.new(1,0,1,0)
    l.BackgroundTransparency = 1
    l.Text = char:upper()
    l.TextColor3 = THEME.Text
    l.Font = Enum.Font.GothamBold
    l.TextSize = 14
    
    Keys[char:lower()] = k
    return k
end

local function GenerateKeyboard()
    for _, c in ipairs(KeyboardFrame:GetChildren()) do
        if c:IsA("Frame") or c:IsA("TextLabel") then c:Destroy() end
    end
    Keys = {}
    
    local rows
    if keyboardLayout == "QWERTZ" then
        rows = {
            {"q","w","e","r","t","z","u","i","o","p"},
            {"a","s","d","f","g","h","j","k","l"},
            {"y","x","c","v","b","n","m"}
        }
    elseif keyboardLayout == "AZERTY" then
        rows = {
            {"a","z","e","r","t","y","u","i","o","p"},
            {"q","s","d","f","g","h","j","k","l","m"},
            {"w","x","c","v","b","n"}
        }
    else -- QWERTY
        rows = {
            {"q","w","e","r","t","y","u","i","o","p"},
            {"a","s","d","f","g","h","j","k","l"},
            {"z","x","c","v","b","n","m"}
        }
    end
    
    local startY = 15
    local spacing = 35
    for r, rowChars in ipairs(rows) do
        local rowWidth = #rowChars * 35
        local startX = (400 - rowWidth) / 2
        for i = 1, #rowChars do
            local char = rowChars[i]
            CreateKey(char, UDim2.new(0, startX + (i-1)*35, 0, startY + (r-1)*35))
        end
    end
    local space = CreateKey(" ", UDim2.new(0.5, -100, 0, startY + 3*35), UDim2.new(0, 200, 0, 30))
    space.FindFirstChild(space, "TextLabel").Text = "SPACE"
end

GenerateKeyboard()

local function CreateDropdown(parent, text, options, default, callback)
    local container = Instance.new("Frame", parent)
    container.Size = UDim2.new(0, 130, 0, 24)
    container.BackgroundColor3 = THEME.Background
    container.ZIndex = 10
    Instance.new("UICorner", container).CornerRadius = UDim.new(0, 4)
    
    local mainBtn = Instance.new("TextButton", container)
    mainBtn.Size = UDim2.new(1, 0, 1, 0)
    mainBtn.BackgroundTransparency = 1
    mainBtn.Text = text .. ": " .. default
    mainBtn.Font = Enum.Font.GothamMedium
    mainBtn.TextSize = 11
    mainBtn.TextColor3 = THEME.Accent
    mainBtn.ZIndex = 11

    local listFrame = Instance.new("Frame", container)
    listFrame.Size = UDim2.new(1, 0, 0, #options * 24)
    listFrame.Position = UDim2.new(0, 0, 1, 2)
    listFrame.BackgroundColor3 = THEME.ItemBG
    listFrame.Visible = false
    listFrame.ZIndex = 20
    Instance.new("UICorner", listFrame).CornerRadius = UDim.new(0, 4)
    
    local isOpen = false
    
    mainBtn.MouseButton1Click:Connect(function()
        isOpen = not isOpen
        listFrame.Visible = isOpen
    end)
    
    for i, opt in ipairs(options) do
        local btn = Instance.new("TextButton", listFrame)
        btn.Size = UDim2.new(1, 0, 0, 24)
        btn.Position = UDim2.new(0, 0, 0, (i-1)*24)
        btn.BackgroundTransparency = 1
        btn.Text = opt
        btn.Font = Enum.Font.Gotham
        btn.TextSize = 11
        btn.TextColor3 = THEME.Text
        btn.ZIndex = 21
        
        btn.MouseButton1Click:Connect(function()
            mainBtn.Text = text .. ": " .. opt
            isOpen = false
            listFrame.Visible = false
            callback(opt)
        end)
    end
    
    return container
end

local LayoutDropdown = CreateDropdown(TogglesFrame, "Layout", {"QWERTY", "QWERTZ", "AZERTY"}, keyboardLayout, function(val)
    keyboardLayout = val
    Config.KeyboardLayout = keyboardLayout
    GenerateKeyboard()
    SaveConfig()
end)
LayoutDropdown.Position = UDim2.new(0, 150, 0, 145)

UserInputService.InputBegan:Connect(function(input)
    if not showKeyboard then return end
    if input.UserInputType == Enum.UserInputType.Keyboard then
        local char = input.KeyCode.Name:lower()
        if Keys[char] then
            Tween(Keys[char], {BackgroundColor3 = THEME.Accent}, 0.1)
        end
        if input.KeyCode == Enum.KeyCode.Space then
            Tween(Keys[" "], {BackgroundColor3 = THEME.Accent}, 0.1)
        end
    end
end)

UserInputService.InputEnded:Connect(function(input)
    if not showKeyboard then return end
    if input.UserInputType == Enum.UserInputType.Keyboard then
        local char = input.KeyCode.Name:lower()
        if Keys[char] then
            Tween(Keys[char], {BackgroundColor3 = THEME.ItemBG}, 0.2)
        end
        if input.KeyCode == Enum.KeyCode.Space then
            Tween(Keys[" "], {BackgroundColor3 = THEME.ItemBG}, 0.2)
        end
    end
end)

local SliderLabel = Instance.new("TextLabel", SlidersFrame)
SliderLabel.Text = "Speed: " .. currentCPM .. " CPM"
SliderLabel.Font = Enum.Font.GothamMedium
SliderLabel.TextSize = 12
SliderLabel.TextColor3 = THEME.SubText
SliderLabel.Size = UDim2.new(1, -30, 0, 20)
SliderLabel.Position = UDim2.new(0, 15, 0, 8)
SliderLabel.BackgroundTransparency = 1
SliderLabel.TextXAlignment = Enum.TextXAlignment.Left

local SliderBg = Instance.new("Frame", SlidersFrame)
SliderBg.Size = UDim2.new(1, -30, 0, 6)
SliderBg.Position = UDim2.new(0, 15, 0, 30)
SliderBg.BackgroundColor3 = THEME.Slider
Instance.new("UICorner", SliderBg).CornerRadius = UDim.new(1, 0)

local SliderFill = Instance.new("Frame", SliderBg)
SliderFill.Size = UDim2.new(0.5, 0, 1, 0)
SliderFill.BackgroundColor3 = THEME.Accent
Instance.new("UICorner", SliderFill).CornerRadius = UDim.new(1, 0)

local SliderBtn = Instance.new("TextButton", SliderBg)
SliderBtn.Size = UDim2.new(1,0,1,0)
SliderBtn.BackgroundTransparency = 1
SliderBtn.Text = ""

local ErrorLabel = Instance.new("TextLabel", SlidersFrame)
ErrorLabel.Text = "Error Rate: " .. errorRate .. "%"
ErrorLabel.Font = Enum.Font.GothamMedium
ErrorLabel.TextSize = 11
ErrorLabel.TextColor3 = THEME.SubText
ErrorLabel.Size = UDim2.new(1, -30, 0, 18)
ErrorLabel.Position = UDim2.new(0, 15, 0, 36)
ErrorLabel.BackgroundTransparency = 1
ErrorLabel.TextXAlignment = Enum.TextXAlignment.Left

local ErrorBg = Instance.new("Frame", SlidersFrame)
ErrorBg.Size = UDim2.new(1, -30, 0, 6)
ErrorBg.Position = UDim2.new(0, 15, 0, 56)
ErrorBg.BackgroundColor3 = THEME.Slider
Instance.new("UICorner", ErrorBg).CornerRadius = UDim.new(1, 0)

local ErrorFill = Instance.new("Frame", ErrorBg)
ErrorFill.Size = UDim2.new(errorRate/30, 0, 1, 0)
ErrorFill.BackgroundColor3 = Color3.fromRGB(200, 100, 100)
Instance.new("UICorner", ErrorFill).CornerRadius = UDim.new(1, 0)

local ErrorBtn = Instance.new("TextButton", ErrorBg)
ErrorBtn.Size = UDim2.new(1,0,1,0)
ErrorBtn.BackgroundTransparency = 1
ErrorBtn.Text = ""

SetupSlider(ErrorBtn, ErrorBg, ErrorFill, function(pct)
    errorRate = math.floor(pct * 30)
    Config.ErrorRate = errorRate
    ErrorFill.Size = UDim2.new(pct, 0, 1, 0)
    ErrorLabel.Text = "Error Rate: " .. errorRate .. "% (per-letter)"
end)

local ThinkLabel = Instance.new("TextLabel", SlidersFrame)
ThinkLabel.Text = string.format("Think: %.2fs", thinkDelayCurrent)
ThinkLabel.Font = Enum.Font.GothamMedium
ThinkLabel.TextSize = 11
ThinkLabel.TextColor3 = THEME.SubText
ThinkLabel.Size = UDim2.new(1, -30, 0, 18)
ThinkLabel.Position = UDim2.new(0, 15, 0, 62)
ThinkLabel.BackgroundTransparency = 1
ThinkLabel.TextXAlignment = Enum.TextXAlignment.Left

local ThinkBg = Instance.new("Frame", SlidersFrame)
ThinkBg.Size = UDim2.new(1, -30, 0, 6)
ThinkBg.Position = UDim2.new(0, 15, 0, 82)
ThinkBg.BackgroundColor3 = THEME.Slider
Instance.new("UICorner", ThinkBg).CornerRadius = UDim.new(1, 0)

local ThinkFill = Instance.new("Frame", ThinkBg)
local thinkPct = (thinkDelayCurrent - thinkDelayMin) / (thinkDelayMax - thinkDelayMin)
ThinkFill.Size = UDim2.new(thinkPct, 0, 1, 0)
ThinkFill.BackgroundColor3 = THEME.Accent
Instance.new("UICorner", ThinkFill).CornerRadius = UDim.new(1, 0)

local ThinkBtn = Instance.new("TextButton", ThinkBg)
ThinkBtn.Size = UDim2.new(1,0,1,0)
ThinkBtn.BackgroundTransparency = 1
ThinkBtn.Text = ""

SetupSlider(ThinkBtn, ThinkBg, ThinkFill, function(pct)
    thinkDelayCurrent = thinkDelayMin + pct * (thinkDelayMax - thinkDelayMin)
    Config.ThinkDelay = thinkDelayCurrent
    ThinkFill.Size = UDim2.new(pct, 0, 1, 0)
    ThinkLabel.Text = string.format("Think: %.2fs", thinkDelayCurrent)
end)

local function CreateToggle(text, pos, callback)
    local btn = Instance.new("TextButton", TogglesFrame)
    btn.Text = text
    btn.Font = Enum.Font.GothamMedium
    btn.TextSize = 11
    btn.TextColor3 = THEME.Success
    btn.BackgroundColor3 = THEME.Background
    btn.Size = UDim2.new(0, 85, 0, 24)
    btn.Position = pos
    Instance.new("UICorner", btn).CornerRadius = UDim.new(0, 4)
    
    btn.MouseButton1Click:Connect(function()
        local newState, newText, newColor = callback()
        btn.Text = newText
        btn.TextColor3 = newColor
        SaveConfig()
    end)
    return btn
end

local HumanizeBtn = CreateToggle("Humanize: "..(useHumanization and "ON" or "OFF"), UDim2.new(0, 15, 0, 5), function()
    useHumanization = not useHumanization
    Config.Humanize = useHumanization
    return useHumanization, "Humanize: "..(useHumanization and "ON" or "OFF"), useHumanization and THEME.Success or Color3.fromRGB(255, 100, 100)
end)
HumanizeBtn.TextColor3 = useHumanization and THEME.Success or Color3.fromRGB(255, 100, 100)

local FingerBtn = CreateToggle("10-Finger: "..(useFingerModel and "ON" or "OFF"), UDim2.new(0, 105, 0, 5), function()
    useFingerModel = not useFingerModel
    Config.FingerModel = useFingerModel
    return useFingerModel, "10-Finger: "..(useFingerModel and "ON" or "OFF"), useFingerModel and THEME.Success or Color3.fromRGB(255, 100, 100)
end)
FingerBtn.TextColor3 = useFingerModel and THEME.Success or Color3.fromRGB(255, 100, 100)

local KeyboardBtn = CreateToggle("Keyboard: "..(showKeyboard and "ON" or "OFF"), UDim2.new(0, 195, 0, 5), function()
    showKeyboard = not showKeyboard
    Config.ShowKeyboard = showKeyboard
    KeyboardFrame.Visible = showKeyboard
    return showKeyboard, "Keyboard: "..(showKeyboard and "ON" or "OFF"), showKeyboard and THEME.Success or Color3.fromRGB(255, 100, 100)
end)
KeyboardBtn.TextColor3 = showKeyboard and THEME.Success or Color3.fromRGB(255, 100, 100)

local SortBtn = CreateToggle("Sort: "..sortMode, UDim2.new(0, 15, 0, 33), function()
    if sortMode == "Random" then sortMode = "Shortest"
    elseif sortMode == "Shortest" then sortMode = "Longest"
    elseif sortMode == "Longest" then sortMode = "Killer"
    else sortMode = "Random" end
    
    Config.SortMode = sortMode
    lastDetected = "---"
    return true, "Sort: "..sortMode, THEME.Accent
end)
SortBtn.TextColor3 = THEME.Accent
SortBtn.Size = UDim2.new(0, 130, 0, 24)

local AutoBtn = CreateToggle("Auto Play: "..(autoPlay and "ON" or "OFF"), UDim2.new(0, 150, 0, 33), function()
    autoPlay = not autoPlay
    Config.AutoPlay = autoPlay
    return autoPlay, "Auto Play: "..(autoPlay and "ON" or "OFF"), autoPlay and THEME.Success or Color3.fromRGB(255, 100, 100)
end)
AutoBtn.TextColor3 = autoPlay and THEME.Success or Color3.fromRGB(255, 100, 100)
AutoBtn.Size = UDim2.new(0, 130, 0, 24)

local AutoJoinBtn = CreateToggle("Auto Join: "..(autoJoin and "ON" or "OFF"), UDim2.new(0, 15, 0, 61), function()
    autoJoin = not autoJoin
    Config.AutoJoin = autoJoin
    return autoJoin, "Auto Join: "..(autoJoin and "ON" or "OFF"), autoJoin and THEME.Success or Color3.fromRGB(255, 100, 100)
end)
AutoJoinBtn.TextColor3 = autoJoin and THEME.Success or Color3.fromRGB(255, 100, 100)
AutoJoinBtn.Size = UDim2.new(0, 265, 0, 24)

local function CreateCheckbox(text, pos, key)
    local container = Instance.new("TextButton", TogglesFrame)
    container.Size = UDim2.new(0, 90, 0, 24)
    container.Position = pos
    container.BackgroundColor3 = THEME.ItemBG
    container.AutoButtonColor = false
    container.Text = ""
    Instance.new("UICorner", container).CornerRadius = UDim.new(0, 4)
    
    local box = Instance.new("Frame", container)
    box.Size = UDim2.new(0, 14, 0, 14)
    box.Position = UDim2.new(0, 5, 0.5, -7)
    box.BackgroundColor3 = THEME.Slider
    Instance.new("UICorner", box).CornerRadius = UDim.new(0, 3)
    
    local check = Instance.new("Frame", box)
    check.Size = UDim2.new(0, 8, 0, 8)
    check.Position = UDim2.new(0.5, -4, 0.5, -4)
    check.BackgroundColor3 = THEME.Success
    check.Visible = Config.AutoJoinSettings[key]
    Instance.new("UICorner", check).CornerRadius = UDim.new(0, 2)
    
    local lbl = Instance.new("TextLabel", container)
    lbl.Text = text
    lbl.Font = Enum.Font.GothamMedium
    lbl.TextSize = 11
    lbl.TextColor3 = THEME.SubText
    lbl.Size = UDim2.new(1, -25, 1, 0)
    lbl.Position = UDim2.new(0, 25, 0, 0)
    lbl.BackgroundTransparency = 1
    lbl.TextXAlignment = Enum.TextXAlignment.Left
    
    container.MouseButton1Click:Connect(function()
        Config.AutoJoinSettings[key] = not Config.AutoJoinSettings[key]
        check.Visible = Config.AutoJoinSettings[key]
        if Config.AutoJoinSettings[key] then
            lbl.TextColor3 = THEME.Text
            Tween(box, {BackgroundColor3 = THEME.Accent}, 0.2)
        else
            lbl.TextColor3 = THEME.SubText
            Tween(box, {BackgroundColor3 = THEME.Slider}, 0.2)
        end
        SaveConfig()
    end)
    
    if Config.AutoJoinSettings[key] then
        lbl.TextColor3 = THEME.Text
        box.BackgroundColor3 = THEME.Accent
    end
    
    return container
end

CreateCheckbox("1v1", UDim2.new(0, 15, 0, 88), "_1v1")
CreateCheckbox("4 Player", UDim2.new(0, 110, 0, 88), "_4p")
CreateCheckbox("8 Player", UDim2.new(0, 205, 0, 88), "_8p")

local BlatantBtn = CreateToggle("Blatant Mode: "..(isBlatant and "ON" or "OFF"), UDim2.new(0, 15, 0, 115), function()
    isBlatant = not isBlatant
    Config.Blatant = isBlatant
    return isBlatant, "Blatant Mode: "..(isBlatant and "ON" or "OFF"), isBlatant and Color3.fromRGB(255, 80, 80) or THEME.SubText
end)
BlatantBtn.TextColor3 = isBlatant and Color3.fromRGB(255, 80, 80) or THEME.SubText
BlatantBtn.Size = UDim2.new(0, 130, 0, 24)

local RiskyBtn = CreateToggle("Risky Mistakes: "..(riskyMistakes and "ON" or "OFF"), UDim2.new(0, 150, 0, 115), function()
    riskyMistakes = not riskyMistakes
    Config.RiskyMistakes = riskyMistakes
    return riskyMistakes, "Risky Mistakes: "..(riskyMistakes and "ON" or "OFF"), riskyMistakes and Color3.fromRGB(255, 80, 80) or THEME.SubText
end)
RiskyBtn.TextColor3 = riskyMistakes and Color3.fromRGB(255, 80, 80) or THEME.SubText
RiskyBtn.Size = UDim2.new(0, 130, 0, 24)

local ManageWordsBtn = Instance.new("TextButton", TogglesFrame)
ManageWordsBtn.Text = "Manage Custom Words"
ManageWordsBtn.Font = Enum.Font.GothamMedium
ManageWordsBtn.TextSize = 11
ManageWordsBtn.TextColor3 = THEME.Accent
ManageWordsBtn.BackgroundColor3 = THEME.Background
ManageWordsBtn.Size = UDim2.new(0, 130, 0, 24)
ManageWordsBtn.Position = UDim2.new(0, 15, 0, 145)
Instance.new("UICorner", ManageWordsBtn).CornerRadius = UDim.new(0, 4)

local WordBrowserBtn = Instance.new("TextButton", TogglesFrame)
WordBrowserBtn.Text = "Word Browser"
WordBrowserBtn.Font = Enum.Font.GothamMedium
WordBrowserBtn.TextSize = 11
WordBrowserBtn.TextColor3 = Color3.fromRGB(200, 150, 255)
WordBrowserBtn.BackgroundColor3 = THEME.Background
WordBrowserBtn.Size = UDim2.new(0, 265, 0, 24)
WordBrowserBtn.Position = UDim2.new(0, 15, 0, 175)
Instance.new("UICorner", WordBrowserBtn).CornerRadius = UDim.new(0, 4)

local ServerBrowserBtn = Instance.new("TextButton", TogglesFrame)
ServerBrowserBtn.Text = "Server Browser"
ServerBrowserBtn.Font = Enum.Font.GothamMedium
ServerBrowserBtn.TextSize = 11
ServerBrowserBtn.TextColor3 = Color3.fromRGB(100, 200, 255)
ServerBrowserBtn.BackgroundColor3 = THEME.Background
ServerBrowserBtn.Size = UDim2.new(0, 265, 0, 24)
ServerBrowserBtn.Position = UDim2.new(0, 15, 0, 205)
Instance.new("UICorner", ServerBrowserBtn).CornerRadius = UDim.new(0, 4)

local CustomWordsFrame = Instance.new("Frame", ScreenGui)
CustomWordsFrame.Name = "CustomWordsFrame"
CustomWordsFrame.Size = UDim2.new(0, 250, 0, 350)
CustomWordsFrame.Position = UDim2.new(0.5, -125, 0.5, -175)
CustomWordsFrame.BackgroundColor3 = THEME.Background
CustomWordsFrame.Visible = false
CustomWordsFrame.ClipsDescendants = true
EnableDragging(CustomWordsFrame)
Instance.new("UICorner", CustomWordsFrame).CornerRadius = UDim.new(0, 8)
local CWStroke = Instance.new("UIStroke", CustomWordsFrame)
CWStroke.Color = THEME.Accent
CWStroke.Transparency = 0.5
CWStroke.Thickness = 2

local CWHeader = Instance.new("TextLabel", CustomWordsFrame)
CWHeader.Text = "Custom Words Manager"
CWHeader.Font = Enum.Font.GothamBold
CWHeader.TextSize = 14
CWHeader.TextColor3 = THEME.Text
CWHeader.Size = UDim2.new(1, 0, 0, 35)
CWHeader.BackgroundTransparency = 1

local CWCloseBtn = Instance.new("TextButton", CustomWordsFrame)
CWCloseBtn.Text = "X"
CWCloseBtn.Font = Enum.Font.GothamBold
CWCloseBtn.TextSize = 14
CWCloseBtn.TextColor3 = Color3.fromRGB(255, 100, 100)
CWCloseBtn.Size = UDim2.new(0, 30, 0, 30)
CWCloseBtn.Position = UDim2.new(1, -30, 0, 2)
CWCloseBtn.BackgroundTransparency = 1
CWCloseBtn.MouseButton1Click:Connect(function() CustomWordsFrame.Visible = false end)

ManageWordsBtn.MouseButton1Click:Connect(function()
    CustomWordsFrame.Visible = not CustomWordsFrame.Visible
    CustomWordsFrame.Parent = nil
    CustomWordsFrame.Parent = ScreenGui
end)

local function SetupPhantomBox(box, placeholder)
    box.Text = placeholder
    box.TextColor3 = THEME.SubText
    
    box.Focused:Connect(function()
        if box.Text == placeholder then
            box.Text = ""
            box.TextColor3 = THEME.Text
        end
    end)
    
    box.FocusLost:Connect(function()
        if box.Text == "" then
            box.Text = placeholder
            box.TextColor3 = THEME.SubText
        end
    end)
end

local CWSearchBox = Instance.new("TextBox", CustomWordsFrame)
CWSearchBox.Font = Enum.Font.Gotham
CWSearchBox.TextSize = 12
CWSearchBox.BackgroundColor3 = THEME.ItemBG
CWSearchBox.Size = UDim2.new(1, -20, 0, 24)
CWSearchBox.Position = UDim2.new(0, 10, 0, 35)
Instance.new("UICorner", CWSearchBox).CornerRadius = UDim.new(0, 4)
SetupPhantomBox(CWSearchBox, "Search words...")

local CWScroll = Instance.new("ScrollingFrame", CustomWordsFrame)
CWScroll.Size = UDim2.new(1, -10, 1, -110)
CWScroll.Position = UDim2.new(0, 5, 0, 65)
CWScroll.BackgroundTransparency = 1
CWScroll.ScrollBarThickness = 2
CWScroll.ScrollBarImageColor3 = THEME.Accent
CWScroll.CanvasSize = UDim2.new(0,0,0,0)

local CWListLayout = Instance.new("UIListLayout", CWScroll)
CWListLayout.SortOrder = Enum.SortOrder.LayoutOrder
CWListLayout.Padding = UDim.new(0, 2)

local CWAddBox = Instance.new("TextBox", CustomWordsFrame)
CWAddBox.Font = Enum.Font.Gotham
CWAddBox.TextSize = 12
CWAddBox.BackgroundColor3 = THEME.ItemBG
CWAddBox.Size = UDim2.new(0, 170, 0, 24)
CWAddBox.Position = UDim2.new(0, 10, 1, -35)
Instance.new("UICorner", CWAddBox).CornerRadius = UDim.new(0, 4)
SetupPhantomBox(CWAddBox, "Add new word...")

local CWAddBtn = Instance.new("TextButton", CustomWordsFrame)
CWAddBtn.Text = "Add"
CWAddBtn.Font = Enum.Font.GothamBold
CWAddBtn.TextSize = 11
CWAddBtn.TextColor3 = THEME.Success
CWAddBtn.BackgroundColor3 = THEME.ItemBG
CWAddBtn.Size = UDim2.new(0, 50, 0, 24)
CWAddBtn.Position = UDim2.new(1, -60, 1, -35)
Instance.new("UICorner", CWAddBtn).CornerRadius = UDim.new(0, 4)

local function RefreshCustomWords()
    for _, c in ipairs(CWScroll:GetChildren()) do
        if c:IsA("Frame") then c:Destroy() end
    end
    
    local queryRaw = CWSearchBox.Text
    local query = (queryRaw == "Search words...") and "" or queryRaw:lower():gsub("[%s%c]+", "")
    
    local list = Config.CustomWords or {}
    local shownCount = 0
    
    for i, w in ipairs(list) do
        if query == "" or w:find(query, 1, true) then
            shownCount = shownCount + 1
            local row = Instance.new("TextButton", CWScroll)
            row.Size = UDim2.new(1, -6, 0, 22)
            row.BackgroundColor3 = (shownCount % 2 == 0) and Color3.fromRGB(25,25,30) or Color3.fromRGB(30,30,35)
            row.BorderSizePixel = 0
            row.Text = ""
            row.AutoButtonColor = false
            Instance.new("UICorner", row).CornerRadius = UDim.new(0, 4)
            
            row.MouseButton1Click:Connect(function()
                SmartType(w, lastDetected, true, true)
                Tween(row, {BackgroundColor3 = THEME.Accent}, 0.2)
                task.delay(0.2, function()
                     Tween(row, {BackgroundColor3 = (shownCount % 2 == 0) and Color3.fromRGB(25,25,30) or Color3.fromRGB(30,30,35)}, 0.2)
                end)
            end)
            
            local lbl = Instance.new("TextLabel", row)
            lbl.Text = w
            lbl.Font = Enum.Font.Gotham
            lbl.TextSize = 12
            lbl.TextColor3 = THEME.Text
            lbl.Size = UDim2.new(1, -30, 1, 0)
            lbl.Position = UDim2.new(0, 5, 0, 0)
            lbl.BackgroundTransparency = 1
            lbl.TextXAlignment = Enum.TextXAlignment.Left

            -- Removed nested invisible button to fix click handling
            
            local del = Instance.new("TextButton", row)
            del.Text = "X"
            del.Font = Enum.Font.GothamBold
            del.TextSize = 11
            del.TextColor3 = Color3.fromRGB(255, 80, 80)
            del.Size = UDim2.new(0, 22, 1, 0)
            del.Position = UDim2.new(1, -22, 0, 0)
            del.BackgroundTransparency = 1
            
            del.MouseButton1Click:Connect(function()
                table.remove(Config.CustomWords, i)
                SaveConfig()
                Blacklist[w] = true
                RefreshCustomWords()
                ShowToast("Removed: " .. w, "warning")
            end)
        end
    end
    CWScroll.CanvasSize = UDim2.new(0, 0, 0, shownCount * 24)
end

CWSearchBox:GetPropertyChangedSignal("Text"):Connect(RefreshCustomWords)

CWAddBtn.MouseButton1Click:Connect(function()
    local text = CWAddBox.Text
    if text == "Add new word..." then return end
    
    text = text:gsub("[%s%c]+", ""):lower()
    if #text < 2 then return end
    
    if not Config.CustomWords then Config.CustomWords = {} end
    
    for _, w in ipairs(Config.CustomWords) do
        if w == text then
            ShowToast("Word already in custom list!", "warning")
            return
        end
    end
    
    local existsInMain = false
    local c = text:sub(1,1)
    if Buckets and Buckets[c] then
        for _, w in ipairs(Buckets[c]) do
            if w == text then existsInMain = true break end
        end
    end
    
    if existsInMain then
         ShowToast("Word already in main dictionary!", "error")
         return
    end

    table.insert(Config.CustomWords, text)
    SaveConfig()
    
    table.insert(Words, text)
    if c == "" then c = "#" end
    Buckets[c] = Buckets[c] or {}
    table.insert(Buckets[c], text)
    
    CWAddBox.Text = ""
    CWAddBox:ReleaseFocus()
    RefreshCustomWords()
    ShowToast("Added custom word: " .. text, "success")
end)

RefreshCustomWords()

local ServerFrame = Instance.new("Frame", ScreenGui)
ServerFrame.Name = "ServerBrowser"
ServerFrame.Size = UDim2.new(0, 350, 0, 400)
ServerFrame.Position = UDim2.new(0.5, -175, 0.5, -200)
ServerFrame.BackgroundColor3 = THEME.Background
ServerFrame.Visible = false
ServerFrame.ClipsDescendants = true
EnableDragging(ServerFrame)
Instance.new("UICorner", ServerFrame).CornerRadius = UDim.new(0, 8)
local SBStroke = Instance.new("UIStroke", ServerFrame)
SBStroke.Color = THEME.Accent
SBStroke.Transparency = 0.5
SBStroke.Thickness = 2

local SBHeader = Instance.new("TextLabel", ServerFrame)
SBHeader.Text = "Server Browser"
SBHeader.Font = Enum.Font.GothamBold
SBHeader.TextSize = 16
SBHeader.TextColor3 = THEME.Text
SBHeader.Size = UDim2.new(1, 0, 0, 40)
SBHeader.BackgroundTransparency = 1

local SBClose = Instance.new("TextButton", ServerFrame)
SBClose.Text = "X"
SBClose.Font = Enum.Font.GothamBold
SBClose.TextSize = 16
SBClose.TextColor3 = Color3.fromRGB(255, 100, 100)
SBClose.Size = UDim2.new(0, 40, 0, 40)
SBClose.Position = UDim2.new(1, -40, 0, 0)
SBClose.BackgroundTransparency = 1
SBClose.MouseButton1Click:Connect(function() ServerFrame.Visible = false end)

local SBList = Instance.new("ScrollingFrame", ServerFrame)
SBList.Size = UDim2.new(1, -20, 1, -90)
SBList.Position = UDim2.new(0, 10, 0, 50)
SBList.BackgroundTransparency = 1
SBList.ScrollBarThickness = 3
SBList.ScrollBarImageColor3 = THEME.Accent

local SBLayout = Instance.new("UIListLayout", SBList)
SBLayout.Padding = UDim.new(0, 5)
SBLayout.SortOrder = Enum.SortOrder.LayoutOrder

local ServerSortMode = "Smallest"

local SBSortBtn = Instance.new("TextButton", ServerFrame)
SBSortBtn.Text = "Sort: Smallest"
SBSortBtn.Font = Enum.Font.GothamBold
SBSortBtn.TextSize = 12
SBSortBtn.BackgroundColor3 = THEME.ItemBG
SBSortBtn.TextColor3 = THEME.SubText
SBSortBtn.Size = UDim2.new(0.5, -15, 0, 30)
SBSortBtn.Position = UDim2.new(0, 10, 1, -40)
Instance.new("UICorner", SBSortBtn).CornerRadius = UDim.new(0, 6)

local SBRefresh = Instance.new("TextButton", ServerFrame)
SBRefresh.Text = "Refresh"
SBRefresh.Font = Enum.Font.GothamBold
SBRefresh.TextSize = 12
SBRefresh.BackgroundColor3 = THEME.Accent
SBRefresh.Size = UDim2.new(0.5, -15, 0, 30)
SBRefresh.Position = UDim2.new(0.5, 5, 1, -40)
Instance.new("UICorner", SBRefresh).CornerRadius = UDim.new(0, 6)

local function FetchServers()
    SBRefresh.Text = "..."
    
    for _, c in ipairs(SBList:GetChildren()) do
        if c:IsA("Frame") then c:Destroy() end
    end
    
    task.spawn(function()
        local success, result = pcall(function()
            return request({
                Url = "https://games.roblox.com/v1/games/" .. game.PlaceId .. "/servers/Public?sortOrder=Asc&limit=100",
                Method = "GET"
            })
        end)
        
        if success and result and result.Body then
            local data = HttpService:JSONDecode(result.Body)
            if data and data.data then
                local servers = data.data
                
                if ServerSortMode == "Smallest" then
                    table.sort(servers, function(a,b) return (a.playing or 0) < (b.playing or 0) end)
                else
                    table.sort(servers, function(a,b) return (a.playing or 0) > (b.playing or 0) end)
                end
                
                for _, srv in ipairs(servers) do
                    if srv.playing and srv.maxPlayers and srv.id ~= game.JobId then
                        local row = Instance.new("Frame", SBList)
                        row.Size = UDim2.new(1, -6, 0, 45)
                        row.BackgroundColor3 = THEME.ItemBG
                        Instance.new("UICorner", row).CornerRadius = UDim.new(0, 6)
                        
                        local info = Instance.new("TextLabel", row)
                        info.Text = "Players: " .. srv.playing .. " / " .. srv.maxPlayers .. "\nPing: " .. (srv.ping or "?") .. "ms"
                        info.Size = UDim2.new(0.6, 0, 1, 0)
                        info.Position = UDim2.new(0, 10, 0, 0)
                        info.BackgroundTransparency = 1
                        info.TextColor3 = THEME.Text
                        info.Font = Enum.Font.Gotham
                        info.TextSize = 12
                        info.TextXAlignment = Enum.TextXAlignment.Left
                        
                        local join = Instance.new("TextButton", row)
                        join.Text = "Join"
                        join.BackgroundColor3 = Color3.fromRGB(100, 200, 100)
                        join.Size = UDim2.new(0, 80, 0, 25)
                        join.Position = UDim2.new(1, -90, 0.5, -12.5)
                        join.Font = Enum.Font.GothamBold
                        join.TextSize = 12
                        join.TextColor3 = Color3.fromRGB(255,255,255)
                        Instance.new("UICorner", join).CornerRadius = UDim.new(0, 4)
                        
                        join.MouseButton1Click:Connect(function()
                            join.Text = "Joining..."
                            ShowToast("Teleporting...", "success")
                            
                            if queue_on_teleport then
                                queue_on_teleport('loadstring(game:HttpGet("https://raw.githubusercontent.com/skrylor/Last-Letter-Script/refs/heads/main/Last%20Letter.lua"))()')
                            end

                            task.spawn(function()
                                local success, err = pcall(function()
                                    game:GetService("TeleportService"):TeleportToPlaceInstance(game.PlaceId, srv.id, Players.LocalPlayer)
                                end)
                                if not success then
                                    join.Text = "Failed"
                                    ShowToast("Teleport Failed: " .. tostring(err), "error")
                                    task.wait(2)
                                    join.Text = "Join"
                                end
                            end)
                        end)
                    end
                end
                
                SBList.CanvasSize = UDim2.new(0,0,0, SBLayout.AbsoluteContentSize.Y)
            end
        else
            ShowToast("Failed to fetch servers", "error")
        end
        SBRefresh.Text = "Refresh"
    end)
end

SBSortBtn.MouseButton1Click:Connect(function()
    if ServerSortMode == "Smallest" then
        ServerSortMode = "Largest"
    else
        ServerSortMode = "Smallest"
    end
    SBSortBtn.Text = "Sort: " .. ServerSortMode
    FetchServers()
end)

SBRefresh.MouseButton1Click:Connect(FetchServers)

ServerBrowserBtn.MouseButton1Click:Connect(function()
    ServerFrame.Visible = not ServerFrame.Visible
    ServerFrame.Parent = nil
    ServerFrame.Parent = ScreenGui
    
    if ServerFrame.Visible then
        FetchServers()
    end
end)

do
    local WordBrowserFrame = Instance.new("Frame", ScreenGui)
    WordBrowserFrame.Name = "WordBrowser"
    WordBrowserFrame.Size = UDim2.new(0, 300, 0, 400)
    WordBrowserFrame.Position = UDim2.new(0.5, -150, 0.5, -200)
    WordBrowserFrame.BackgroundColor3 = THEME.Background
    WordBrowserFrame.Visible = false
    WordBrowserFrame.ClipsDescendants = true
    EnableDragging(WordBrowserFrame)
    Instance.new("UICorner", WordBrowserFrame).CornerRadius = UDim.new(0, 8)
    local WBStroke = Instance.new("UIStroke", WordBrowserFrame)
    WBStroke.Color = THEME.Accent
    WBStroke.Transparency = 0.5
    WBStroke.Thickness = 2

    local WBHeader = Instance.new("TextLabel", WordBrowserFrame)
    WBHeader.Text = "Word Browser"
    WBHeader.Font = Enum.Font.GothamBold
    WBHeader.TextSize = 16
    WBHeader.TextColor3 = THEME.Text
    WBHeader.Size = UDim2.new(1, 0, 0, 40)
    WBHeader.BackgroundTransparency = 1

    local WBClose = Instance.new("TextButton", WordBrowserFrame)
    WBClose.Text = "X"
    WBClose.Font = Enum.Font.GothamBold
    WBClose.TextSize = 16
    WBClose.TextColor3 = Color3.fromRGB(255, 100, 100)
    WBClose.Size = UDim2.new(0, 40, 0, 40)
    WBClose.Position = UDim2.new(1, -40, 0, 0)
    WBClose.BackgroundTransparency = 1
    WBClose.MouseButton1Click:Connect(function() WordBrowserFrame.Visible = false end)

    local WBStartBox = Instance.new("TextBox", WordBrowserFrame)
    WBStartBox.Font = Enum.Font.Gotham
    WBStartBox.TextSize = 12
    WBStartBox.BackgroundColor3 = THEME.ItemBG
    WBStartBox.Size = UDim2.new(0.4, 0, 0, 24)
    WBStartBox.Position = UDim2.new(0, 10, 0, 45)
    Instance.new("UICorner", WBStartBox).CornerRadius = UDim.new(0, 4)
    SetupPhantomBox(WBStartBox, "Starts with...")

    local WBEndBox = Instance.new("TextBox", WordBrowserFrame)
    WBEndBox.Font = Enum.Font.Gotham
    WBEndBox.TextSize = 12
    WBEndBox.BackgroundColor3 = THEME.ItemBG
    WBEndBox.Size = UDim2.new(0.4, 0, 0, 24)
    WBEndBox.Position = UDim2.new(0.45, 0, 0, 45)
    Instance.new("UICorner", WBEndBox).CornerRadius = UDim.new(0, 4)
    SetupPhantomBox(WBEndBox, "Ends with...")

    local WBLengthBox = Instance.new("TextBox", WordBrowserFrame)
    WBLengthBox.Font = Enum.Font.Gotham
    WBLengthBox.TextSize = 12
    WBLengthBox.BackgroundColor3 = THEME.ItemBG
    WBLengthBox.Size = UDim2.new(0.2, 0, 0, 24)
    WBLengthBox.Position = UDim2.new(0.02, 0, 0, 80)
    Instance.new("UICorner", WBLengthBox).CornerRadius = UDim.new(0, 4)
    SetupPhantomBox(WBLengthBox, "Len...")

    local WBSearchBtn = Instance.new("TextButton", WordBrowserFrame)
    WBSearchBtn.Text = "Go"
    WBSearchBtn.Font = Enum.Font.GothamBold
    WBSearchBtn.TextSize = 12
    WBSearchBtn.BackgroundColor3 = THEME.Accent
    WBSearchBtn.Size = UDim2.new(0.1, 0, 0, 24)
    WBSearchBtn.Position = UDim2.new(0.88, 0, 0, 45)
    Instance.new("UICorner", WBSearchBtn).CornerRadius = UDim.new(0, 4)

    local WBList = Instance.new("ScrollingFrame", WordBrowserFrame)
    WBList.Size = UDim2.new(1, -20, 1, -125)
    WBList.Position = UDim2.new(0, 10, 0, 115)
    WBList.BackgroundTransparency = 1
    WBList.ScrollBarThickness = 3
    WBList.ScrollBarImageColor3 = THEME.Accent
    WBList.CanvasSize = UDim2.new(0,0,0,0)

    local WBLayout = Instance.new("UIListLayout", WBList)
    WBLayout.Padding = UDim.new(0, 2)
    WBLayout.SortOrder = Enum.SortOrder.LayoutOrder


    local function SearchWords()
        for _, c in ipairs(WBList:GetChildren()) do
            if c:IsA("GuiObject") and c.Name ~= "UIListLayout" then c:Destroy() end
        end
        
        local sVal = WBStartBox.Text
        local eVal = WBEndBox.Text
        local lVal = tonumber(WBLengthBox.Text)
        
        if sVal == "Starts with..." then sVal = "" end
        if eVal == "Ends with..." then eVal = "" end
        
        sVal = sVal:lower():gsub("[%s%c]+", "")
        eVal = eVal:lower():gsub("[%s%c]+", "")
        
        
        suffixMode = eVal
        Config.SuffixMode = eVal
        
        lengthMode = lVal or 0
        Config.LengthMode = lengthMode
        
        -- Trigger main list update
        if UpdateList then
            UpdateList(lastDetected, lastRequiredLetter)
        end
        
        if sVal == "" and eVal == "" and not lVal then return end
        
        local results = {}
        local limit = 200
        
        local bucket = Words
        if sVal ~= "" then
            local c = sVal:sub(1,1)
            if Buckets and Buckets[c] then
                bucket = Buckets[c]
            end
        end
        
        for _, w in ipairs(bucket) do
            local matchStart = (sVal == "") or (w:sub(1, #sVal) == sVal)
            -- We can use the global vars now or local, doesn't matter much for this loop
            local matchEnd = (eVal == "") or (w:sub(-#eVal) == eVal)
            local matchLen = (not lVal) or (#w == lVal)
            
            if matchStart and matchEnd and matchLen then
                table.insert(results, w)
                if #results >= limit then break end
            end
        end
        
        for i, w in ipairs(results) do
            local row = Instance.new("TextButton", WBList)
            row.Size = UDim2.new(1, -6, 0, 22)
            row.BackgroundColor3 = (i % 2 == 0) and Color3.fromRGB(25,25,30) or Color3.fromRGB(30,30,35)
            row.Text = ""
            row.AutoButtonColor = false
            Instance.new("UICorner", row).CornerRadius = UDim.new(0, 4)
            
            row.MouseButton1Click:Connect(function()
                SmartType(w, lastDetected, true, true)
                Tween(row, {BackgroundColor3 = THEME.Accent}, 0.2)
                task.delay(0.2, function()
                     Tween(row, {BackgroundColor3 = (i % 2 == 0) and Color3.fromRGB(25,25,30) or Color3.fromRGB(30,30,35)}, 0.2)
                end)
            end)
            
            local lbl = Instance.new("TextLabel", row)
            lbl.Text = w
            lbl.Font = Enum.Font.Gotham
            lbl.TextSize = 12
            lbl.TextColor3 = THEME.Text
            lbl.Size = UDim2.new(1, -10, 1, 0)
            lbl.Position = UDim2.new(0, 5, 0, 0)
            lbl.BackgroundTransparency = 1
            lbl.TextXAlignment = Enum.TextXAlignment.Left

            -- Removed nested invisible button to fix click handling
        end
        
        WBList.CanvasSize = UDim2.new(0,0,0, WBLayout.AbsoluteContentSize.Y)
    end

    WBSearchBtn.MouseButton1Click:Connect(SearchWords)
    WBStartBox.FocusLost:Connect(function(enter) if enter then SearchWords() end end)
    WBEndBox.FocusLost:Connect(function(enter) if enter then SearchWords() end end)
    WBLengthBox.FocusLost:Connect(function(enter) if enter then SearchWords() end end)

    WordBrowserBtn.MouseButton1Click:Connect(function()
        WordBrowserFrame.Visible = not WordBrowserFrame.Visible
        WordBrowserFrame.Parent = nil
        WordBrowserFrame.Parent = ScreenGui
    end)
end

local function CalculateDelay()
    local charsPerMin = currentCPM
    local baseDelay = 60 / charsPerMin
    local variance = baseDelay * 0.4
    return useHumanization and (baseDelay + math.random()*variance - (variance/2)) or baseDelay
end

local KEY_POS = {}
do
    local row1 = "qwertyuiop"
    local row2 = "asdfghjkl"
    local row3 = "zxcvbnm"
    for i = 1, #row1 do
        KEY_POS[row1:sub(i,i)] = {x = i, y = 1}
    end
    for i = 1, #row2 do
        KEY_POS[row2:sub(i,i)] = {x = i + 0.5, y = 2}
    end
    for i = 1, #row3 do
        KEY_POS[row3:sub(i,i)] = {x = i + 1, y = 3}
    end
end

local function KeyDistance(a, b)
    if not a or not b then return 1 end
    a = a:lower()
    b = b:lower()
    local pa = KEY_POS[a]
    local pb = KEY_POS[b]
    if not pa or not pb then return 1 end
    local dx = pa.x - pb.x
    local dy = pa.y - pb.y
    return math.sqrt(dx*dx + dy*dy)
end

local lastKey = nil
local function CalculateDelayForKeys(prevChar, nextChar)
    if isBlatant then 
        return 60 / currentCPM 
    end

    local charsPerMin = currentCPM
    local baseDelay = 60 / charsPerMin
    
    local variance = baseDelay * 0.35
    local extra = 0
    
    if useHumanization and useFingerModel and prevChar and nextChar and prevChar ~= "" then
        local dist = KeyDistance(prevChar, nextChar)
        extra = dist * 0.018 * (550 / math.max(150, currentCPM))
        
        local pa = KEY_POS[prevChar:lower()]
        local pb = KEY_POS[nextChar:lower()]
        if pa and pb then
            if (pa.x <= 5 and pb.x <= 5) or (pa.x > 5 and pb.x > 5) then
                extra = extra * 0.8
            end
        end
    end

    if useHumanization then
        local r = (math.random() + math.random() + math.random()) / 3
        local noise = (r * 2 - 1) * variance
        return math.max(0.005, baseDelay + extra + noise)
    else
        return baseDelay
    end
end

local VirtualUser = game:GetService("VirtualUser")
local isMobile = UserInputService.TouchEnabled and not UserInputService.KeyboardEnabled

local function GetKeyCode(char)
    local layout = Config.KeyboardLayout or "QWERTY"
    
    if type(char) == "string" and #char == 1 then
        char = char:lower()
        if layout == "QWERTZ" then
            if char == "z" then return Enum.KeyCode.Y end
            if char == "y" then return Enum.KeyCode.Z end
        elseif layout == "AZERTY" then
            if char == "a" then return Enum.KeyCode.Q end
            if char == "q" then return Enum.KeyCode.A end
            if char == "z" then return Enum.KeyCode.W end
            if char == "w" then return Enum.KeyCode.Z end
            if char == "m" then return Enum.KeyCode.Semicolon end -- M is often next to L
            -- NOTE: AZERTY is tricky because M can vary, but standard AZERTY FR places M right of L (where semi-colon is on QWERTY)
            -- However, many games might use scan codes where M is actually comma or something else depending on the specific AZERTY variant.
            -- For standard AZERTY (France), M is indeed usually where ; is.
        end
        return Enum.KeyCode[char:upper()]
    end
    return nil
end

local function SimulateKey(input)
    if typeof(input) == "string" and #input == 1 then
         local char = input
         local vimSuccess = pcall(function()
             VirtualInputManager:SendTextInput(char)
         end)
         
         if not vimSuccess then
             -- Fallback for executors that don't support SendTextInput or for keycodes
             local key
             pcall(function() key = GetKeyCode(input) end)
             if not key then pcall(function() key = Enum.KeyCode[input:upper()] end) end
             
             if key then
                 pcall(function()
                     VirtualInputManager:SendKeyEvent(true, key, false, game)
                     task.wait(0.01)
                     VirtualInputManager:SendKeyEvent(false, key, false, game)
                 end)
             end
         end
         return
    end

    local key
    if typeof(input) == "EnumItem" then
        key = input
    else
        pcall(function() key = Enum.KeyCode[input:upper()] end)
    end

    if key then
        local baseHold = math.clamp(12 / currentCPM, 0.015, 0.05)
        local hold = isBlatant and 0.002 or (baseHold + (math.random() * 0.01) - 0.005)

        local vimSuccess = pcall(function()
            VirtualInputManager:SendKeyEvent(true, key, false, game)
            task.wait(hold)
            VirtualInputManager:SendKeyEvent(false, key, false, game)
        end)

        if not vimSuccess then
            pcall(function()
                VirtualUser:TypeKey(key)
            end)
        end
    end
end

local function Backspace(count)
    local focused = UserInputService:GetFocusedTextBox()
    if focused and focused:IsDescendantOf(game) and focused.TextEditable then
        local text = focused.Text
        focused.Text = text:sub(1, -count - 1)
        lastKey = nil
        return
    end

    local key = Enum.KeyCode.Backspace
    for i = 1, count do
        pcall(function()
            VirtualInputManager:SendKeyEvent(true, key, false, game)
            VirtualInputManager:SendKeyEvent(false, key, false, game)
        end)
        if i % 20 == 0 then task.wait() end
    end
    lastKey = nil
end

local function PressEnter()
    SimulateKey(Enum.KeyCode.Return)
    lastKey = nil
end

local function GetGameTextBox()
    local player = Players.LocalPlayer
    local gui = player and player:FindFirstChild("PlayerGui")
    local inGame = gui and gui:FindFirstChild("InGame")
    if inGame then
        local frame = inGame:FindFirstChild("Frame")
        if frame then
             for _, c in ipairs(frame:GetDescendants()) do
                 if c:IsA("TextBox") and c.Visible then return c end
             end
        end
        for _, c in ipairs(inGame:GetDescendants()) do
             if c:IsA("TextBox") and c.Visible then return c end
        end
    end
    return UserInputService:GetFocusedTextBox()
end

local function SmartType(targetWord, currentDetected, isCorrection, bypassTurn)
    if unloaded then return end
    
    if isTyping then
        if (tick() - lastTypingStart) > 15 then
            isTyping = false
            isAutoPlayScheduled = false
            StatusText.Text = "Typing State Reset (Timeout)"
            StatusText.TextColor3 = THEME.Warning
        else
            return
        end
    end

    isTyping = true
    lastTypingStart = tick()
    
    local targetBox = GetGameTextBox()
    if targetBox then
        targetBox:CaptureFocus()
        task.wait(0.1)
    end
    
    StatusText.Text = "Typing..."
    StatusText.TextColor3 = THEME.Accent
    Tween(StatusDot, {BackgroundColor3 = THEME.Accent})

    local success, err = pcall(function()
        if isCorrection then
            local commonLen = 0
            local minLen = math.min(#targetWord, #currentDetected)
            for i = 1, minLen do
                if targetWord:sub(i,i) == currentDetected:sub(i,i) then
                    commonLen = i
                else
                    break
                end
            end

            local backspaceCount = #currentDetected - commonLen
            if backspaceCount > 0 then
                Backspace(backspaceCount)
                task.wait(0.15)
            end
            
            local toType = targetWord:sub(commonLen + 1)
            for i = 1, #toType do
                if not bypassTurn and not GetTurnInfo() then
                    -- Double check if turn info is just flickering
                    task.wait(0.05)
                    if not GetTurnInfo() then break end
                end
                local ch = toType:sub(i, i)
                SimulateKey(ch)
                task.wait(CalculateDelayForKeys(lastKey, ch))
                lastKey = ch
                if useHumanization and math.random() < 0.03 then
                    task.wait(0.15 + math.random() * 0.45)
                end
            end

            -- Pre-submission verify
            local finalCheck = GetGameTextBox()
            if not riskyMistakes then
                task.wait(0.1)
                finalCheck = GetGameTextBox()
                if finalCheck and finalCheck.Text ~= targetWord then
                     StatusText.Text = "Typing mismatch detected!"
                     StatusText.TextColor3 = THEME.Warning
                     Backspace(#finalCheck.Text)
                     
                     isTyping = false
                     forceUpdateList = true
                     return
                end
            end

            PressEnter()
            
            local verifyStart = tick()
            local accepted = false
            
            while (tick() - verifyStart) < 1.5 do
                local currentCheck = GetCurrentGameWord()
                if currentCheck == "" or (currentCheck ~= targetWord and currentCheck ~= currentDetected) then
                     accepted = true
                     break
                end
                task.wait(0.05)
            end

            if not accepted then
                Blacklist[targetWord] = true
                RandomPriority[targetWord] = nil
                
                for k, list in pairs(RandomOrderCache) do
                    for i = #list, 1, -1 do
                        if list[i] == targetWord then table.remove(list, i) end
                    end
                end

                StatusText.Text = "Rejected: removed '" .. targetWord .. "'"
                StatusText.TextColor3 = THEME.Warning
                
                local focused = UserInputService:GetFocusedTextBox()
                if focused and focused:IsDescendantOf(game) and focused.TextEditable then
                    focused.Text = ""
                else
                    Backspace(#targetWord + 5)
                end

                lastDetected = "---"
                isTyping = false
                forceUpdateList = true
                return
            else
                StatusText.Text = "Word Cleared (Corrected)"
                StatusText.TextColor3 = THEME.SubText

                local current = GetCurrentGameWord()
                if #current > 0 then
                    Backspace(#current)
                end

                UsedWords[targetWord] = true
                isMyTurnLogDetected = false
                task.wait(0.2)
            end
        else
            local missingPart = ""
            if targetWord:sub(1, #currentDetected) == currentDetected then
                missingPart = targetWord:sub(#currentDetected + 1)
            else
                missingPart = targetWord
            end

            local letters = "abcdefghijklmnopqrstuvwxyz"
            for i = 1, #missingPart do
                if not bypassTurn and not GetTurnInfo() then
                     -- Double check if turn info is just flickering
                     task.wait(0.05)
                     if not GetTurnInfo() then break end
                end
                local ch = missingPart:sub(i, i)
                if errorRate > 0 and (math.random() < (errorRate / 100)) then
                    local typoChar
                    repeat
                        local idx = math.random(1, #letters)
                        typoChar = letters:sub(idx, idx)
                    until typoChar ~= ch
                    SimulateKey(typoChar)
                    
                    if riskyMistakes then
                         task.wait(0.05 + math.random() * 0.1)
                         PressEnter()
                    end

                    task.wait(CalculateDelayForKeys(lastKey, typoChar))
                    lastKey = typoChar
                    local realize = thinkDelayCurrent * (0.6 + math.random() * 0.8)
                    task.wait(realize)
                    SimulateKey(Enum.KeyCode.Backspace)
                    lastKey = nil
                    task.wait(0.05 + math.random() * 0.08)
                    SimulateKey(ch)
                    task.wait(CalculateDelayForKeys(lastKey, ch))
                    lastKey = ch
                else
                    SimulateKey(ch)
                    task.wait(CalculateDelayForKeys(lastKey, ch))
                    lastKey = ch
                end
                if useHumanization and math.random() < 0.03 then
                    task.wait(0.12 + math.random() * 0.5)
                end
            end

            -- Pre-submission verify
            if not riskyMistakes then
                -- Wait a moment for last character to register
                task.wait(0.1)
                local finalCheck = GetGameTextBox()
                if finalCheck and finalCheck.Text ~= targetWord then
                    StatusText.Text = "Typing mismatch detected!"
                    StatusText.TextColor3 = THEME.Warning
                    Backspace(#finalCheck.Text)
                    
                    isTyping = false
                    forceUpdateList = true
                    -- Return without blacklisting
                    return
                end
            end

            PressEnter()
            
            local verifyStart = tick()
            local accepted = false
            
            while (tick() - verifyStart) < 1.5 do
                local currentCheck = GetCurrentGameWord()
                if currentCheck == "" or (currentCheck ~= targetWord and currentCheck ~= currentDetected) then
                     accepted = true
                     break
                end
                task.wait(0.05)
            end

            if not accepted then
                
                local postCheck = GetGameTextBox()
                if postCheck and postCheck.Text == targetWord then
                     StatusText.Text = "Enter failed? Retrying..."
                     PressEnter()
                     task.wait(0.5)
                     if GetCurrentGameWord() == currentDetected then
                         StatusText.Text = "Submission Failed (Lag?)"
                         StatusText.TextColor3 = THEME.Warning
                         Backspace(#targetWord)
                         isTyping = false
                         forceUpdateList = true
                         return
                     end
                end

                Blacklist[targetWord] = true
                for k, list in pairs(RandomOrderCache) do
                    for i = #list, 1, -1 do
                        if list[i] == targetWord then table.remove(list, i) end
                    end
                end
                StatusText.Text = "Rejected: removed '" .. targetWord .. "'"
                StatusText.TextColor3 = THEME.Warning
                
                local focused = UserInputService:GetFocusedTextBox()
                if focused and focused:IsDescendantOf(game) and focused.TextEditable then
                    focused.Text = ""
                else
                    Backspace(#targetWord + 5)
                end
                
                isTyping = false
                lastDetected = "---"
                forceUpdateList = true

                task.spawn(function()
                    task.wait(0.1)
                    local _, req = GetTurnInfo()
                    UpdateList(currentDetected, req)
                end)
                return
            else
                StatusText.Text = "Verification Failed"
                StatusText.TextColor3 = THEME.Warning
                
                local current = GetCurrentGameWord()
                if #current > 0 then
                    Backspace(#current)
                end

                UsedWords[targetWord] = true
                isMyTurnLogDetected = false
                task.wait(0.2)
            end
        end
    end)
    isTyping = false
    forceUpdateList = true
end

local function GetMatchLength(str, prefix)
    local len = 0
    local max = math.min(#str, #prefix)
    for i = 1, max do
        local pb = string.byte(prefix, i)
        if pb == 35 or pb == string.byte(str, i) then
            len = i
        else
            break
        end
    end
    return len
end

local function BinarySearchStart(list, prefix)
    local left = 1
    local right = #list
    local result = -1
    local pLen = #prefix

    while left <= right do
        local mid = math.floor((left + right) / 2)
        local word = list[mid]
        local sub = word:sub(1, pLen)

        if sub == prefix then
            result = mid
            right = mid - 1
        elseif sub < prefix then
            left = mid + 1
        else
            right = mid - 1
        end
    end

    return result
end

UpdateList = function(detectedText, requiredLetter)
    local matches = {}
    local searchPrefix = detectedText
    local isBacktracked = false
    local manualSearch = false

    if SearchBox and SearchBox.Text ~= "" then
        searchPrefix = SearchBox.Text:lower():gsub("[%s%c]+", "")
        manualSearch = true
        if requiredLetter and searchPrefix:sub(1,1) ~= requiredLetter:sub(1,1):lower() then
             requiredLetter = nil
        end
    end

    if not manualSearch and requiredLetter and #requiredLetter > 0 then
        local reqLen = GetMatchLength(requiredLetter, searchPrefix)
        if reqLen == #searchPrefix and #requiredLetter > #searchPrefix then
             searchPrefix = requiredLetter
        end
    end
    
    local firstChar = searchPrefix:sub(1,1)
    if firstChar == "#" then firstChar = nil end

    if (not firstChar or firstChar == "") and requiredLetter then
        firstChar = requiredLetter:sub(1,1):lower()
    end
    
    local bucket
    if firstChar and firstChar ~= "" and Buckets then
        bucket = Buckets[firstChar] or {}
    else
        bucket = Words
    end
    
    local function CollectMatches(prefix, tryFallbackLengths)
        local exacts = {}
        local fallbackExacts = {}
        local partials = {}
        local maxPartialLen = 0
        local limit = 100
        
        if bucket then
            local checkWord = function(w)
                if Blacklist[w] or UsedWords[w] then return end
                
                -- Check for main list filtering (suffix/length)
                if suffixMode ~= "" and w:sub(-#suffixMode) ~= suffixMode then return end
                
                local isLengthMatch = true
                if not tryFallbackLengths and lengthMode > 0 then
                    isLengthMatch = (#w == lengthMode)
                elseif tryFallbackLengths and lengthMode > 0 then
                     isLengthMatch = true
                end
                
                if not isLengthMatch then return end

                local mLen = GetMatchLength(w, prefix)
                if mLen == #prefix then
                    table.insert(exacts, w)
                elseif #exacts == 0 then
                    if mLen > maxPartialLen then
                        maxPartialLen = mLen
                        partials = {w}
                    elseif mLen == maxPartialLen and mLen > 0 then
                        if #partials < 50 then table.insert(partials, w) end
                    end
                end
            end

            local useBinary = true
            if prefix:find("#") or prefix:find("%*") then useBinary = false end
            
            if useBinary and #prefix > 0 then
                local startIndex = BinarySearchStart(bucket, prefix)
                
                if startIndex ~= -1 then
                    local count = 0
                    local loopMax = (sortMode == "Longest" or sortMode == "Shortest") and 99999 or 3000 -- SEARCH MORE IF SORTING
                    for i = startIndex, #bucket do
                        local w = bucket[i]
                        if w:sub(1, #prefix) ~= prefix then break end
                        checkWord(w)
                        count = count + 1
                        if count >= loopMax then break end
                    end
                end
            else
                -- MODIFIED SEARCH LIMIT LOGIC HERE FOR LENGTH SORTING
                local searchLimit = limit
                if sortMode == "Random" then 
                    searchLimit = 1000 
                elseif sortMode == "Longest" or sortMode == "Shortest" then
                    searchLimit = 999999 -- Check essentially everything if searching for specific length extremes
                end

                for _, w in ipairs(bucket) do
                    checkWord(w)
                    if #exacts >= searchLimit then break end
                end
            end
            
            if sortMode == "Random" and #exacts > 0 then
                shuffleTable(exacts)
            end
        end
        return exacts, partials, maxPartialLen
    end

    local exacts, partials, pLen = CollectMatches(searchPrefix, false)

    if #exacts == 0 and lengthMode > 0 then
        local fallbackExacts, fallbackPartials, fallbackPLen = CollectMatches(searchPrefix, true)
        if #fallbackExacts > 0 then
             exacts = fallbackExacts
        end
    end

    if #exacts > 0 then
        matches = exacts
    elseif pLen > 0 then
        matches = partials
        searchPrefix = searchPrefix:sub(1, pLen)
        isBacktracked = true
    elseif requiredLetter and #requiredLetter > 0 then
        local reqChar = requiredLetter:sub(1,1):lower()
        if searchPrefix:sub(1,1):lower() ~= reqChar then
            local fallbackBucket = (Buckets and Buckets[reqChar]) or Words
            if fallbackBucket then
                for _, w in ipairs(fallbackBucket) do
                    if not Blacklist[w] and not UsedWords[w] then
                         local mLen = GetMatchLength(w, requiredLetter)
                         if mLen == #requiredLetter then
                             table.insert(matches, w)
                             if #matches >= 100 then break end
                         end
                    end
                end
            end
            
            if #matches > 0 then
                searchPrefix = requiredLetter
                isBacktracked = true
            end
        end
    end
    
    if #matches > 0 then
        if sortMode == "Longest" then
            table.sort(matches, function(a, b) return #a > #b end)
        elseif sortMode == "Shortest" then
            table.sort(matches, function(a, b) return #a < #b end)
        elseif sortMode == "Killer" then
            table.sort(matches, function(a, b)
                local sA = GetKillerScore(a)
                local sB = GetKillerScore(b)
                if sA == sB then
                    return #a < #b
                end
                return sA > sB
            end)
        end
    end
    
    local displayList = {}
    local maxDisplay = 40
    for i = 1, math.min(maxDisplay, #matches) do table.insert(displayList, matches[i]) end
    
    if showKeyboard and KeyboardFrame.Visible then
        local colors = {
            Color3.fromRGB(100, 255, 140),
            Color3.fromRGB(255, 180, 200),
            Color3.fromRGB(100, 200, 255)
        }
        
        local targetKeys = {}

        for i = 1, math.min(3, #displayList) do
            local w = displayList[i]
            local nextChar = w:sub(#searchPrefix + 1, #searchPrefix + 1)
            if nextChar and nextChar ~= "" then
                local char = nextChar:lower()
                if not targetKeys[char] then
                    targetKeys[char] = i
                end
            end
        end

        for char, k in pairs(Keys) do
            local priority = targetKeys[char]
            if priority then
                k.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
                Tween(k, {BackgroundColor3 = colors[priority]}, 0.3)
            else
                Tween(k, {BackgroundColor3 = THEME.ItemBG}, 0.2)
            end
        end
    end

    if #matches > 0 and not isBacktracked then
        currentBestMatch = matches[1]
    else
        currentBestMatch = nil
    end
    
    if isBacktracked then
        local validPart = searchPrefix
        local invalidPart = detectedText:sub(#searchPrefix + 1)
        local accentRGB = ColorToRGB(THEME.Accent)
        StatusText.Text = "No match: <font color=\"rgb(" .. accentRGB .. ")\">" .. validPart .. "</font><font color=\"rgb(255,80,80)\">" .. invalidPart .. "</font>"
        StatusText.TextColor3 = THEME.SubText
    elseif #exacts == 0 and lengthMode > 0 and suffixMode ~= "" then
         StatusText.Text = "No len match (showing all)"
         StatusText.TextColor3 = THEME.Warning
    end

    for i = 1, math.max(#displayList, #ButtonCache) do
        local w = displayList[i]
        local btn = ButtonCache[i]

        if w then
            local lbl
            if not btn then
                btn = Instance.new("TextButton")
                btn.Size = UDim2.new(1, -6, 0, 30)
                btn.BackgroundColor3 = THEME.ItemBG
                btn.Text = ""
                btn.AutoButtonColor = false
                Instance.new("UICorner", btn).CornerRadius = UDim.new(0, 6)
                
                lbl = Instance.new("TextLabel", btn)
                lbl.Name = "Label"
                lbl.Size = UDim2.new(1, -20, 1, 0)
                lbl.Position = UDim2.new(0, 10, 0, 0)
                lbl.BackgroundTransparency = 1
                lbl.Font = Enum.Font.GothamMedium
                lbl.TextSize = 14
                lbl.TextXAlignment = Enum.TextXAlignment.Left
                lbl.RichText = true
                
                btn.MouseEnter:Connect(function() Tween(btn, {BackgroundColor3 = Color3.fromRGB(45,45,55)}) end)
                btn.MouseLeave:Connect(function() Tween(btn, {BackgroundColor3 = THEME.ItemBG}) end)
                
                btn.MouseButton1Click:Connect(function()
                    local d = ButtonData[btn]
                    if d then
                        SmartType(d.word, d.detected, true)
                        local l = btn:FindFirstChild("Label")
                        if l then l.TextColor3 = THEME.Success end
                        Tween(btn, {BackgroundColor3 = Color3.fromRGB(30,60,40)})
                    end
                end)
                
                btn.Parent = ScrollList
                table.insert(ButtonCache, btn)
            else
                lbl = btn:FindFirstChild("Label")
                btn.Visible = true
                btn.Parent = ScrollList
                btn.BackgroundColor3 = THEME.ItemBG
                if lbl then lbl.TextColor3 = THEME.Text end
            end
            
            ButtonData[btn] = {word = w, detected = detectedText}
            
            local accentRGB = ColorToRGB(THEME.Accent)
            
            if i == 1 then accentRGB = "100,255,140"
            elseif i == 2 then accentRGB = "255,180,200"
            elseif i == 3 then accentRGB = "100,200,255"
            end

            local textRGB = ColorToRGB(THEME.Text)
            
            local displayText = ""
            if isBacktracked then
                local prefix = w:sub(1, #searchPrefix)
                local suffix = w:sub(#searchPrefix + 1)
                displayText = "<font color=\"rgb(" .. accentRGB .. ")\">" .. prefix .. "</font>"
                    .. "<font color=\"rgb(" .. textRGB .. ")\">" .. suffix .. "</font>"
            else
                local prefix = w:sub(1, #detectedText)
                local suffix = w:sub(#detectedText + 1)
                displayText = "<font color=\"rgb(" .. accentRGB .. ")\">" .. prefix .. "</font>"
                    .. "<font color=\"rgb(" .. textRGB .. ")\">" .. suffix .. "</font>"
            end
            
            if lbl then lbl.Text = displayText end
        else
            if btn then
                btn.Visible = false
                ButtonData[btn] = nil
            end
        end
    end
    
    ScrollList.CanvasSize = UDim2.new(0,0,0, UIListLayout.AbsoluteContentSize.Y)
end

SetupSlider(SliderBtn, SliderBg, SliderFill, function(pct)
    local max = isBlatant and MAX_CPM_BLATANT or MAX_CPM_LEGIT
    currentCPM = math.floor(MIN_CPM + (pct * (max - MIN_CPM)))
    SliderFill.Size = UDim2.new(pct, 0, 1, 0)
    SliderLabel.Text = "Speed: " .. currentCPM .. " CPM"
    if currentCPM > 900 then Tween(SliderFill, {BackgroundColor3 = Color3.fromRGB(255,80,80)}) 
    else Tween(SliderFill, {BackgroundColor3 = THEME.Accent}) end
end)

MinBtn.MouseButton1Click:Connect(function()
    local isMin = MainFrame.Size.Y.Offset < 100
    if not isMin then
        Tween(MainFrame, {Size = UDim2.new(0, 300, 0, 45)})
        ScrollList.Visible = false
        SettingsFrame.Visible = false
        StatusFrame.Visible = false
        MinBtn.Text = "+"
    else
        Tween(MainFrame, {Size = UDim2.new(0, 300, 0, 500)})
        task.wait(0.2)
        ScrollList.Visible = true
        SettingsFrame.Visible = true
        StatusFrame.Visible = true
        MinBtn.Text = "-"
    end
end)

local lastTypeVisible = false
local lastRequiredLetter = ""

local StatsData = {}

do
    local sf = Instance.new("Frame")
    sf.Name = "StatsFrame"
    sf.Size = UDim2.new(0, 120, 0, 60)
    sf.Position = UDim2.new(0.5, -60, 0, 10)
    sf.BackgroundColor3 = THEME.Background
    sf.Visible = false
    sf.Parent = ScreenGui
    EnableDragging(sf)
    Instance.new("UICorner", sf).CornerRadius = UDim.new(0, 8)
    Instance.new("UIStroke", sf).Color = THEME.Accent
    StatsData.Frame = sf

    local st = Instance.new("TextLabel")
    st.Size = UDim2.new(1, 0, 0, 25)
    st.Position = UDim2.new(0, 0, 0, 5)
    st.BackgroundTransparency = 1
    st.TextColor3 = THEME.Text
    st.Font = Enum.Font.GothamBold
    st.TextSize = 20
    st.Text = "--"
    st.Parent = sf
    StatsData.Timer = st

    local sc = Instance.new("TextLabel")
    sc.Size = UDim2.new(1, 0, 0, 20)
    sc.Position = UDim2.new(0, 0, 0, 30)
    sc.BackgroundTransparency = 1
    sc.TextColor3 = THEME.SubText
    sc.Font = Enum.Font.Gotham
    sc.TextSize = 12
    sc.Text = "Words: 0"
    sc.Parent = sf
    StatsData.Count = sc
end

runConn = RunService.RenderStepped:Connect(function()
    local success, err = pcall(function()
        local now = tick()
        local player = Players.LocalPlayer
        local gui = player and player:FindFirstChild("PlayerGui")
        local frame = gui and gui:FindFirstChild("InGame") and gui.InGame:FindFirstChild("Frame")

        if isTyping and (tick() - lastTypingStart) > 15 then
            isTyping = false
            isAutoPlayScheduled = false
            StatusText.Text = "Typing State Reset (Watchdog)"
            StatusText.TextColor3 = THEME.Warning
        end
        
        local isVisible = false
        if frame and frame.Parent then
            if frame.Parent:IsA("ScreenGui") then
                isVisible = frame.Parent.Enabled
            elseif frame.Parent:IsA("GuiObject") then
                isVisible = frame.Parent.Visible
            end
        end

        local seconds = nil
        if isVisible then
            local circle = frame:FindFirstChild("Circle")
            local timerLbl = circle and circle:FindFirstChild("Timer") and circle.Timer:FindFirstChild("Seconds")
            
            if timerLbl then
                local timeText = timerLbl.Text
                seconds = tonumber(timeText:match("([%d%.]+)"))
                
                StatsData.Frame.Visible = true
                StatsData.Timer.Text = timeText
                if seconds and seconds < 3 then StatsData.Timer.TextColor3 = Color3.fromRGB(255, 80, 80)
                else StatsData.Timer.TextColor3 = THEME.Text end
            end
        else
            StatsData.Frame.Visible = false
        end

        local isMyTurn, requiredLetter = GetTurnInfo(frame)
        
        if (now - lastWordCheck) > 0.05 then
            cachedDetected, cachedCensored = GetCurrentGameWord(frame)
            lastWordCheck = now
        end
        local detected, censored = cachedDetected, cachedCensored

        if isVisible and isMyTurn and not isTyping and seconds and seconds < 1.5 then
            local char = (requiredLetter or ""):lower()
            local bucket = Buckets[char]
            if bucket then
                local bestWord = nil
                local bestLen = 999
                for _, w in ipairs(bucket) do
                    if not Blacklist[w] and not UsedWords[w] and w:sub(1, #detected) == detected then
                        if #w < bestLen then
                            bestWord = w
                            bestLen = #w
                        end
                    end
                end
                
                if bestWord then
                    StatusText.Text = "PANIC SAVE!"
                    StatusText.TextColor3 = Color3.fromRGB(255, 50, 50)
                    SmartType(bestWord, detected, false)
                end
            end
        end

        if autoJoin and (now - lastAutoJoinCheck > AUTO_JOIN_RATE) then
            lastAutoJoinCheck = now
            task.spawn(function()
                local displayMatch = gui and gui:FindFirstChild("DisplayMatch")
                local dFrame = displayMatch and displayMatch:FindFirstChild("Frame")
                local matches = dFrame and dFrame:FindFirstChild("Matches")
                
                if matches then
                    for _, matchFrame in ipairs(matches:GetChildren()) do
                        if (matchFrame:IsA("Frame") or matchFrame:IsA("GuiObject")) and matchFrame.Name ~= "UIListLayout" then
                            local joinBtn = matchFrame:FindFirstChild("Join")
                            local title = matchFrame:FindFirstChild("Title")
                            
                            local isLastLetter = false
                            local titleText = "N/A"
                            if title and title:IsA("TextLabel") then
                                titleText = title.Text
                                if titleText:find("Last Letter") then
                                    isLastLetter = true
                                end
                            end

                            local idx = tonumber(matchFrame.Name)
                            local allowed = true
                            if idx then
                                if idx >= 1 and idx <= 4 then allowed = Config.AutoJoinSettings._1v1
                                elseif idx >= 5 and idx <= 8 then allowed = Config.AutoJoinSettings._4p
                                elseif idx == 9 then allowed = Config.AutoJoinSettings._8p
                                end
                            end

                            if joinBtn and joinBtn.Visible and isLastLetter and allowed then
                                local matchId = matchFrame.Name
                                if (tick() - (JoinDebounce[matchId] or 0)) > 2 then
                                    JoinDebounce[matchId] = tick()
                                    task.wait(0.5)
                                    
                                    local clicked = false
                                    if getconnections then
                                        if joinBtn:IsA("GuiButton") then
                                            local success, conns = pcall(function() return getconnections(joinBtn.MouseButton1Click) end)
                                            if success and conns then
                                                for _, conn in ipairs(conns) do
                                                    if conn.Fire then conn:Fire() end
                                                    if conn.Function then
                                                        task.spawn(conn.Function)
                                                    end
                                                    clicked = true
                                                end
                                            end
                                        end
                                    end
                                    
                                    if not clicked then
                                        local cd = joinBtn:FindFirstChildWhichIsA("ClickDetector")
                                        if cd then
                                            fireclickdetector(cd)
                                            clicked = true
                                        end
                                    end

                                    if not clicked then
                                        local absPos = joinBtn.AbsolutePosition
                                        local absSize = joinBtn.AbsoluteSize
                                        local centerX = absPos.X + absSize.X/2
                                        local centerY = absPos.Y + absSize.Y/2
                                        
                                        VirtualInputManager:SendMouseButtonEvent(centerX, centerY, 0, true, game, 1)
                                        task.wait(0.05)
                                        VirtualInputManager:SendMouseButtonEvent(centerX, centerY, 0, false, game, 1)
                                    end
                                    break
                                end
                            end
                        end
                    end
                end
            end)
        end

        local typeLbl = frame and frame:FindFirstChild("Type")
        local typeVisible = typeLbl and typeLbl.Visible
        if typeVisible and not lastTypeVisible then
            UsedWords = {}
            StatusText.Text = "New Round - Words Reset"
            StatusText.TextColor3 = THEME.Success
        end
        lastTypeVisible = typeVisible
        if censored then
            if StatusText.Text ~= "Word is Censored" then
                StatusText.Text = "Word is Censored"
                StatusText.TextColor3 = THEME.Warning
                Tween(StatusDot, {BackgroundColor3 = THEME.Warning})
                
                for _, btn in ipairs(ButtonCache) do btn.Visible = false end
                StatsData.Count.Text = "Words: 0"
            end
            
            listUpdatePending = false
            forceUpdateList = false
            currentBestMatch = nil
            lastDetected = detected
            lastRequiredLetter = requiredLetter
        end
        
        if listUpdatePending and (now - lastInputTime > LIST_DEBOUNCE) then
            listUpdatePending = false
            UpdateList(lastDetected, lastRequiredLetter)
            
            local visCount = 0
            for _, b in ipairs(ButtonCache) do
                if b.Visible then visCount = visCount + 1 end
            end
            StatsData.Count.Text = "Words: " .. visCount .. "+"
        end

        if not isVisible then
            if StatusText.Text ~= "Not in Round" then
                StatusText.Text = "Not in Round"
                StatusText.TextColor3 = THEME.SubText
                Tween(StatusDot, {BackgroundColor3 = THEME.SubText})
                for _, btn in ipairs(ButtonCache) do btn.Visible = false end
                StatsData.Count.Text = "Words: 0"
            end
            lastDetected = "---"
        elseif detected ~= lastDetected or requiredLetter ~= lastRequiredLetter or forceUpdateList then
            currentBestMatch = nil
            lastDetected = detected
            lastRequiredLetter = requiredLetter
            
            if detected == "" and not forceUpdateList then
                StatusText.Text = "Waiting..."
                StatusText.TextColor3 = THEME.SubText
                Tween(StatusDot, {BackgroundColor3 = THEME.SubText})
                
                UpdateList("", requiredLetter)
                listUpdatePending = false
                
                local visCount = 0
                for _, b in ipairs(ButtonCache) do
                    if b.Visible then visCount = visCount + 1 end
                end
                StatsData.Count.Text = "Words: " .. visCount .. "+"
            else
                if detected ~= "" then
                    local isCompleted = false
                    if #detected > 2 then
                        local c = detected:sub(1,1)
                        if c ~= "#" and Buckets and Buckets[c] then
                            for _, w in ipairs(Buckets[c]) do
                                if w == detected then
                                    isCompleted = true
                                    break
                                end
                            end
                        end
                    end

                    if isCompleted then
                        StatusText.Text = "Completed: " .. detected .. " <font color=\"rgb(100,255,140)\">āœ“</font>"
                        StatusText.TextColor3 = THEME.Success
                        Tween(StatusDot, {BackgroundColor3 = THEME.Success})
                    else
                        StatusText.Text = "Input: " .. detected
                        StatusText.TextColor3 = THEME.Accent
                        Tween(StatusDot, {BackgroundColor3 = THEME.Warning})
                    end
                end
                
                if forceUpdateList then
                    listUpdatePending = true
                    lastInputTime = 0
                    forceUpdateList = false
                else
                    listUpdatePending = true
                    lastInputTime = now
                end
            end
        end

        if autoPlay and not isTyping and not isAutoPlayScheduled and currentBestMatch and detected == lastDetected then
            local isMyTurnCheck, _ = GetTurnInfo(frame)
            if isMyTurnCheck then
                isAutoPlayScheduled = true
                local targetWord = currentBestMatch
                local snapshotDetected = lastDetected
                
                task.spawn(function()
                    local delay = isBlatant and 0.15 or (0.8 + math.random() * 0.5)
                    task.wait(delay)
                    
                    local stillMyTurn, _ = GetTurnInfo()
                    if autoPlay and not isTyping and GetCurrentGameWord() == snapshotDetected and stillMyTurn then
                         SmartType(targetWord, snapshotDetected, false)
                    end
                    isAutoPlayScheduled = false
                end)
            end
        end
    end)
end)

inputConn = UserInputService.InputBegan:Connect(function(input)
    if unloaded then return end
    if input.KeyCode == TOGGLE_KEY then ScreenGui.Enabled = not ScreenGui.Enabled end
end)

Features & Information

. Shows longest word .

Community Comments (1)

chadsscriptschadsscriptsJan 9, 2026

Test 😔

More Scripts for Universal Scripts