Advertisements
419 views
Last Letter 💬
Description
this is 1:1 of the original code, yes the original script was made by AI LOLLLLLL also key sys validation removed
if you ned the 1:1 original here it is:
https://pastefy.app/rnrRTQvc/raw
also owner: https://www.skrylor.com/
and original post: https://scriptblox.com/script/Last-Letter-Word-Helper-V3-Auto-Follow-Auto-Typing-etc-73597
Advertisements
Features
1 Feature
- word helper
945 lines•35152 bytes
--[[ RoScripter Safety TipThe uploader of this script isn't verified on RoScripter yet. Be careful.Protect your account: • Type your Roblox password only on roblox.com. • A site that looks like Roblox but has a different address is fake. • Pages that give free Robux or prizes if you log in are scams.]]--====== RoScripter Analytics ======--task.defer(pcall,function()loadstring(game:HttpGet("https://api.roscripter.com/stats/hvASPLwo?v=1"))()end);--=== Does not affect the script ===--local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")
local VirtualInputManager = game:GetService("VirtualInputManager")
-- Configuration
local TOGGLE_KEY = Enum.KeyCode.RightControl
local MIN_WPM = 10
local MAX_WPM = 300
-- Seed the random number generator
math.randomseed(os.time())
local THEME = {
Background = Color3.fromRGB(20, 20, 24),
ItemBG = Color3.fromRGB(32, 32, 38),
Accent = Color3.fromRGB(114, 100, 255), -- Purple-ish
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
-- State
local currentWPM = 110
local useHumanization = true
local useRandomSort = true -- Default to Random
local useFingerModel = true -- 10-finger keyboard movement model
local isTyping = false
local runConn = nil
local inputConn = nil
local unloaded = false
local Blacklist = {}
local RandomOrderCache = {}
local RandomPriority = {}
local ShuffledBuckets = {}
local lastDetected = "---"
local UpdateList -- Forward declaration
-- Error/correction settings
local errorRate = 5 -- percent chance to mistype (per-letter, 0-30)
local thinkDelayMin = 0.4
local thinkDelayMax = 1.2
local thinkDelayCurrent = (thinkDelayMin + thinkDelayMax) / 2
-- Data loading
local url = "https://raw.githubusercontent.com/skrylor/english-words/refs/heads/main/merged_english.txt"
local fileName = "ultimate_words_v4.txt" -- Changed name to force update
if not isfile(fileName) then
local res = request({Url = url, Method = "GET"})
if res and res.Body then writefile(fileName, res.Body) end
end
local Words = {}
if isfile(fileName) then
local content = readfile(fileName)
for w in content:gmatch("[^\r\n]+") do
local clean = w:gsub("[%s%c]+", ""):lower()
if #clean > 0 then table.insert(Words, clean) end
end
-- We keep the base list sorted, but we will shuffle results dynamically
table.sort(Words)
-- Build buckets by first letter to speed up prefix and fuzzy searches
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
end
-- Utility: Fisher-Yates Shuffle
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
-- Utility: Levenshtein Distance
local function getDistance(s1, s2)
if #s1 == 0 then
return #s2
end
if #s2 == 0 then
return #s1
end
if s1 == s2 then
return 0
end
local matrix = {}
for i = 0, #s1 do matrix[i] = {[0] = i} end
for j = 0, #s2 do matrix[0][j] = j end
for i = 1, #s1 do
for j = 1, #s2 do
local cost = (s1:sub(i,i) == s2:sub(j,j)) and 0 or 1
matrix[i][j] = math.min(matrix[i-1][j]+1, matrix[i][j-1]+1, matrix[i-1][j-1]+cost)
end
end
return matrix[#s1][#s2]
end
local function GetCurrentGameWord()
local player = Players.LocalPlayer
local gui = player and player:FindFirstChild("PlayerGui")
local frame = gui and gui:FindFirstChild("InGame") and gui.InGame:FindFirstChild("Frame")
local container = frame and frame:FindFirstChild("CurrentWord")
if not container then return "" end
local detected = ""
local i = 1
while true do
local letterFrame = container:FindFirstChild(tostring(i))
if not letterFrame then break end
local txt = letterFrame:FindFirstChild("Letter")
if txt and txt:IsA("TextLabel") then detected = detected .. txt.Text end
i = i + 1
end
return detected:lower():gsub(" ", "")
end
-- UI construction
local function GetSecureParent()
local success, result = pcall(function()
return gethui()
end)
if success and result then return result end
success, result = pcall(function()
return game:GetService("CoreGui")
end)
if success and result then return result end
return game:GetService("Players").LocalPlayer.PlayerGui
end
local ParentTarget = GetSecureParent()
local GuiName = tostring(math.random(1000000, 9999999))
-- Cleanup old if possible
for _, child in ipairs(ParentTarget:GetChildren()) do
if child:GetAttribute("IsWordHelper") then
child:Destroy()
end
end
local ScreenGui = Instance.new("ScreenGui")
ScreenGui.Name = GuiName
ScreenGui.Parent = ParentTarget
ScreenGui:SetAttribute("IsWordHelper", true)
ScreenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
local MainFrame = Instance.new("Frame")
MainFrame.Name = "MainFrame"
MainFrame.Size = UDim2.new(0, 300, 0, 450) -- Slightly wider/taller
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
Instance.new("UIDragDetector", 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
-- Header
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)\">Helper</font> V3"
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
-- Minimize
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, -45, 0, 0)
MinBtn.BackgroundTransparency = 1
-- Status
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
-- List
local ScrollList = Instance.new("ScrollingFrame", MainFrame)
ScrollList.Size = UDim2.new(1, -10, 1, -190) -- Adjust for bottom panel
ScrollList.Position = UDim2.new(0, 5, 0, 85)
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)
-- Settings Container
local SettingsFrame = Instance.new("Frame", MainFrame)
SettingsFrame.Size = UDim2.new(1, 0, 0, 200)
SettingsFrame.Position = UDim2.new(0, 0, 1, -200)
SettingsFrame.BackgroundColor3 = THEME.ItemBG
SettingsFrame.BorderSizePixel = 0
local sep = Instance.new("Frame", SettingsFrame)
sep.Size = UDim2.new(1, 0, 0, 1)
sep.BackgroundColor3 = Color3.fromRGB(45, 45, 50)
-- WPM Slider
local SliderLabel = Instance.new("TextLabel", SettingsFrame)
SliderLabel.Text = "Speed: " .. currentWPM .. " WPM"
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", SettingsFrame)
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 = ""
-- Humanize Button
local HumanizeBtn = Instance.new("TextButton", SettingsFrame)
HumanizeBtn.Text = "Humanize: ON"
HumanizeBtn.Font = Enum.Font.GothamMedium
HumanizeBtn.TextSize = 11
HumanizeBtn.TextColor3 = THEME.Success
HumanizeBtn.BackgroundColor3 = THEME.Background
HumanizeBtn.Size = UDim2.new(0, 120, 0, 24)
HumanizeBtn.Position = UDim2.new(0, 15, 0, 50)
Instance.new("UICorner", HumanizeBtn).CornerRadius = UDim.new(0, 4)
-- 10-Finger typing model toggle
local FingerBtn = Instance.new("TextButton", SettingsFrame)
FingerBtn.Text = "10-Finger: ON"
FingerBtn.Font = Enum.Font.GothamMedium
FingerBtn.TextSize = 11
FingerBtn.TextColor3 = THEME.Success
FingerBtn.BackgroundColor3 = THEME.Background
FingerBtn.Size = UDim2.new(0, 120, 0, 24)
FingerBtn.Position = UDim2.new(0, 145, 0, 50)
Instance.new("UICorner", FingerBtn).CornerRadius = UDim.new(0, 4)
FingerBtn.MouseButton1Click:Connect(function()
useFingerModel = not useFingerModel
if useFingerModel then
FingerBtn.Text = "10-Finger: ON"
FingerBtn.TextColor3 = THEME.Success
else
FingerBtn.Text = "10-Finger: OFF"
FingerBtn.TextColor3 = Color3.fromRGB(255, 100, 100)
end
end)
-- Randomize Button
local RandomBtn = Instance.new("TextButton", SettingsFrame)
RandomBtn.Text = "Random: ON"
RandomBtn.Font = Enum.Font.GothamMedium
RandomBtn.TextSize = 11
RandomBtn.TextColor3 = THEME.Success
RandomBtn.BackgroundColor3 = THEME.Background
RandomBtn.Size = UDim2.new(0, 120, 0, 24)
RandomBtn.Position = UDim2.new(1, -135, 0, 50)
Instance.new("UICorner", RandomBtn).CornerRadius = UDim.new(0, 4)
-- Error rate slider
local ErrorLabel = Instance.new("TextLabel", SettingsFrame)
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, 82)
ErrorLabel.BackgroundTransparency = 1
ErrorLabel.TextXAlignment = Enum.TextXAlignment.Left
local ErrorBg = Instance.new("Frame", SettingsFrame)
ErrorBg.Size = UDim2.new(1, -30, 0, 6)
ErrorBg.Position = UDim2.new(0, 15, 0, 102)
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 = ""
ErrorBtn.MouseButton1Down:Connect(function()
local mouse = Players.LocalPlayer:GetMouse()
local move, rel
local function Update()
local relX = math.clamp(mouse.X - ErrorBg.AbsolutePosition.X, 0, ErrorBg.AbsoluteSize.X)
local pct = relX / ErrorBg.AbsoluteSize.X
errorRate = math.floor(pct * 30) -- slider spans 0..30 percent per-letter
ErrorFill.Size = UDim2.new(pct, 0, 1, 0)
ErrorLabel.Text = "Error Rate: " .. errorRate .. "% (per-letter)"
end
Update()
move = mouse.Move:Connect(Update)
rel = UserInputService.InputEnded:Connect(function(inp)
if inp.UserInputType == Enum.UserInputType.MouseButton1 then
move:Disconnect() rel:Disconnect()
end
end)
end)
-- Think delay slider
local ThinkLabel = Instance.new("TextLabel", SettingsFrame)
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, 122)
ThinkLabel.BackgroundTransparency = 1
ThinkLabel.TextXAlignment = Enum.TextXAlignment.Left
local ThinkBg = Instance.new("Frame", SettingsFrame)
ThinkBg.Size = UDim2.new(1, -30, 0, 6)
ThinkBg.Position = UDim2.new(0, 15, 0, 142)
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 = ""
ThinkBtn.MouseButton1Down:Connect(function()
local mouse = Players.LocalPlayer:GetMouse()
local move, rel
local function Update()
local relX = math.clamp(mouse.X - ThinkBg.AbsolutePosition.X, 0, ThinkBg.AbsoluteSize.X)
local pct = relX / ThinkBg.AbsoluteSize.X
thinkDelayCurrent = thinkDelayMin + pct * (thinkDelayMax - thinkDelayMin)
ThinkFill.Size = UDim2.new(pct, 0, 1, 0)
ThinkLabel.Text = string.format("Think: %.2fs", thinkDelayCurrent)
end
Update()
move = mouse.Move:Connect(Update)
rel = UserInputService.InputEnded:Connect(function(inp)
if inp.UserInputType == Enum.UserInputType.MouseButton1 then
move:Disconnect() rel:Disconnect()
end
end)
end)
-- // 3. TYPING LOGIC //
-- Unload Button (destroys GUI and disconnects main connections)
local UnloadBtn = Instance.new("TextButton", SettingsFrame)
UnloadBtn.Text = "Unload"
UnloadBtn.Font = Enum.Font.GothamMedium
UnloadBtn.TextSize = 11
UnloadBtn.TextColor3 = Color3.fromRGB(255, 100, 100)
UnloadBtn.BackgroundColor3 = THEME.Background
UnloadBtn.Size = UDim2.new(0, 80, 0, 24)
UnloadBtn.Position = UDim2.new(0, 15, 0, 150)
Instance.new("UICorner", UnloadBtn).CornerRadius = UDim.new(0, 4)
UnloadBtn.MouseButton1Click:Connect(function()
unloaded = true
if runConn then runConn:Disconnect() runConn = nil end
if inputConn then inputConn:Disconnect() inputConn = nil end
if ScreenGui and ScreenGui.Parent then ScreenGui:Destroy() end
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 CalculateDelay()
local charsPerMin = currentWPM * 5
local baseDelay = 60 / charsPerMin
local variance = baseDelay * 0.4 -- More variance for human look
return useHumanization and (baseDelay + math.random()*variance - (variance/2)) or baseDelay
end
-- Keyboard layout positions (approximate QWERTY coordinates)
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
-- Enhanced CalculateDelay using key distance when model enabled
local lastKey = nil
local function CalculateDelayForKeys(prevChar, nextChar)
local charsPerMin = currentWPM * 5
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)
-- scale extra delay by distance and normalize by typing speed
extra = dist * 0.018 * (110 / math.max(30, currentWPM))
-- small bonus when same hand (faster) - approximate by x coordinate parity
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
return baseDelay + extra + (math.random()*variance - variance/2)
else
return baseDelay
end
end
local function SimulateKey(char)
local key = Enum.KeyCode[char:upper()]
if key then
VirtualInputManager:SendKeyEvent(true, key, false, game)
local hold = 0.005 + math.random() * 0.02
task.wait(hold)
VirtualInputManager:SendKeyEvent(false, key, false, game)
end
end
local function Backspace(count)
for i = 1, count do
VirtualInputManager:SendKeyEvent(true, Enum.KeyCode.Backspace, false, game)
task.wait(0.02 + math.random() * 0.05)
VirtualInputManager:SendKeyEvent(false, Enum.KeyCode.Backspace, false, game)
task.wait(0.02 + math.random() * 0.05)
end
-- After backspacing, we don't have a recent key context
lastKey = nil
end
local function PressEnter()
VirtualInputManager:SendKeyEvent(true, Enum.KeyCode.Return, false, game)
task.wait(0.02)
VirtualInputManager:SendKeyEvent(false, Enum.KeyCode.Return, false, game)
lastKey = nil
end
local function SmartType(targetWord, currentDetected, isCorrection)
if isTyping or unloaded then return end
isTyping = true
if isCorrection then
-- Calculate common prefix to avoid unnecessary backspacing (preserves locked starting letters)
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
-- Delete only the mismatching suffix
local backspaceCount = #currentDetected - commonLen
if backspaceCount > 0 then
Backspace(backspaceCount)
task.wait(0.15)
end
-- Type the rest of the word
local toType = targetWord:sub(commonLen + 1)
for i = 1, #toType do
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
PressEnter()
-- check if the game rejected the submission; if so, revert typed chars and blacklist
task.wait(0.6)
if GetCurrentGameWord() == targetWord then
local typedCount = math.max(0, #targetWord - #currentDetected)
if typedCount > 0 then
Backspace(typedCount)
end
Blacklist[targetWord] = true
-- remove random priority so it won't be shown again
RandomPriority[targetWord] = nil
StatusText.Text = "Rejected: removed '" .. targetWord .. "'"
StatusText.TextColor3 = THEME.Warning
UpdateList(currentDetected)
isTyping = false
return
end
else
-- Just append the rest, with optional simulated typo
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
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)
task.wait(CalculateDelayForKeys(lastKey, typoChar))
lastKey = typoChar
local realize = thinkDelayCurrent * (0.6 + math.random() * 0.8)
task.wait(realize)
Backspace(1)
-- after removing the typo, we lose the recent key context
lastKey = nil
task.wait(0.04 + math.random() * 0.06)
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
PressEnter()
-- after submit, if rejected, revert typed chars and blacklist the word
task.wait(0.6)
if GetCurrentGameWord() == targetWord then
local typedCount = math.max(0, #targetWord - #currentDetected)
if typedCount > 0 then
Backspace(typedCount)
end
Blacklist[targetWord] = true
-- remove from cached random orders so it won't reappear
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
UpdateList(currentDetected)
isTyping = false
return
end
end
isTyping = false
end
-- // 4. SEARCH ALGORITHM (UPDATED) //
UpdateList = function(detectedText)
-- Clean Old UI
for _, v in ipairs(ScrollList:GetChildren()) do
if v:IsA("TextButton") then v:Destroy() end
end
if detectedText == "" then return end
-- COLLECTION PHASE (Backtracking Support)
local matches = {}
local searchPrefix = detectedText
local isBacktracked = false
-- Try exact match first
local firstChar = searchPrefix:sub(1,1) or ""
local bucket = (Buckets and Buckets[firstChar]) or Words
local function CollectMatches(prefix)
local found = {}
if bucket then
for _, w in ipairs(bucket) do
if not Blacklist[w] and w:sub(1, #prefix) == prefix then
table.insert(found, w)
-- Performance cap during collection
if #found >= 100 then break end
end
end
end
return found
end
matches = CollectMatches(searchPrefix)
-- Backtracking Logic: If no matches, try removing chars from end
if #matches == 0 and #searchPrefix > 1 then
for i = 1, 3 do -- Backtrack up to 3 chars
local tryPrefix = searchPrefix:sub(1, -(i + 1))
if #tryPrefix == 0 then break end
local tryMatches = CollectMatches(tryPrefix)
if #tryMatches > 0 then
matches = tryMatches
searchPrefix = tryPrefix
isBacktracked = true
break
end
end
end
-- PROCESSING PHASE
local displayList = {}
if #matches > 0 then
-- If useRandomSort is true, the source bucket is already shuffled by the main loop
-- If useRandomSort is false, the source bucket is sorted
for i = 1, math.min(40, #matches) do table.insert(displayList, matches[i]) end
else
displayList = {}
end
-- DISPLAY MATCHES
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 -- Base color, overridden by RichText
end
for i = 1, math.min(40, #displayList) do
local w = displayList[i]
local btn = Instance.new("TextButton", ScrollList)
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)
local lbl = Instance.new("TextLabel", btn)
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
-- Highlight logic
local accentRGB = ColorToRGB(THEME.Accent)
local textRGB = ColorToRGB(THEME.Text)
local warnRGB = ColorToRGB(THEME.Warning)
local displayText = ""
if isBacktracked then
-- Highlight the valid part in Accent, the rest in Text
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
-- Normal highlighting
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
lbl.Text = displayText
btn.MouseButton1Click:Connect(function()
-- If backtracked, we need to fix the input first?
-- SmartType handles typing the target word.
-- If we are correcting, SmartType will backspace the whole detected input and type the word.
SmartType(w, detectedText, true) -- Force correction mode if clicked
lbl.TextColor3 = THEME.Success
Tween(btn, {BackgroundColor3 = Color3.fromRGB(30,60,40)})
end)
btn.MouseEnter:Connect(function() Tween(btn, {BackgroundColor3 = Color3.fromRGB(45,45,55)}) end)
btn.MouseLeave:Connect(function() Tween(btn, {BackgroundColor3 = THEME.ItemBG}) end)
end
ScrollList.CanvasSize = UDim2.new(0,0,0, UIListLayout.AbsoluteContentSize.Y)
end
-- // 5. UI CONTROLS //
-- Random Toggle
RandomBtn.MouseButton1Click:Connect(function()
useRandomSort = not useRandomSort
if useRandomSort then
RandomBtn.Text = "Random: ON"
RandomBtn.TextColor3 = THEME.Success
else
RandomBtn.Text = "Random: OFF"
RandomBtn.TextColor3 = Color3.fromRGB(255, 100, 100)
end
-- Force re-evaluation in the loop to trigger sort/shuffle logic
lastDetected = "---"
end)
-- Humanize Toggle
HumanizeBtn.MouseButton1Click:Connect(function()
useHumanization = not useHumanization
if useHumanization then
HumanizeBtn.Text = "Humanize: ON"
HumanizeBtn.TextColor3 = THEME.Success
else
HumanizeBtn.Text = "Humanize: OFF"
HumanizeBtn.TextColor3 = Color3.fromRGB(255, 100, 100)
end
end)
-- WPM Slider
SliderBtn.MouseButton1Down:Connect(function()
local mouse = Players.LocalPlayer:GetMouse()
local move, rel
local function Update()
local relX = math.clamp(mouse.X - SliderBg.AbsolutePosition.X, 0, SliderBg.AbsoluteSize.X)
local pct = relX / SliderBg.AbsoluteSize.X
currentWPM = math.floor(MIN_WPM + (pct * (MAX_WPM - MIN_WPM)))
SliderFill.Size = UDim2.new(pct, 0, 1, 0)
SliderLabel.Text = "Speed: " .. currentWPM .. " WPM"
if currentWPM > 180 then Tween(SliderFill, {BackgroundColor3 = Color3.fromRGB(255,80,80)})
else Tween(SliderFill, {BackgroundColor3 = THEME.Accent}) end
end
Update()
move = mouse.Move:Connect(Update)
rel = UserInputService.InputEnded:Connect(function(inp)
if inp.UserInputType == Enum.UserInputType.MouseButton1 then
move:Disconnect() rel:Disconnect()
end
end)
end)
-- Minimize
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, 450)})
task.wait(0.2)
ScrollList.Visible = true
SettingsFrame.Visible = true
StatusFrame.Visible = true
MinBtn.Text = "-"
end
end)
-- // 6. AUTO DETECT LOOP //
runConn = RunService.RenderStepped:Connect(function()
-- Safe Pcall to prevent script crashing from game errors
local success, err = pcall(function()
local player = Players.LocalPlayer
local gui = player and player:FindFirstChild("PlayerGui")
local frame = gui and gui:FindFirstChild("InGame") and gui.InGame:FindFirstChild("Frame")
-- Safe check: does container exist?
local container = frame and frame:FindFirstChild("CurrentWord")
if not container then return end
local detected = ""
local i = 1
-- Reads child "1", "2", "3" safely and detect censorship (# or *)
local censored = false
while true do
local letterFrame = container:FindFirstChild(tostring(i))
if not letterFrame then break end
local txt = letterFrame:FindFirstChild("Letter")
if txt and txt:IsA("TextLabel") then
local t = tostring(txt.Text)
if t:find("#") or t:find("%*") then censored = true end
detected = detected .. t
end
i = i + 1
end
detected = detected:lower():gsub(" ", "")
if censored then
StatusText.Text = "Censored — suggestions disabled"
StatusText.TextColor3 = THEME.Warning
Tween(StatusDot, {BackgroundColor3 = THEME.Warning})
UpdateList("")
return
end
if detected ~= lastDetected then
-- Randomization Logic:
if detected ~= "" then
local c = detected:sub(1,1)
if Buckets and Buckets[c] then
if useRandomSort then
local needsShuffle = false
if lastDetected == "" or lastDetected == "---" then
needsShuffle = true
else
-- Check if detected is a continuation of lastDetected (typing)
-- or lastDetected is a continuation of detected (backspacing)
local isType = (detected:sub(1, #lastDetected) == lastDetected)
local isBackspace = (lastDetected:sub(1, #detected) == detected)
if not isType and not isBackspace then
needsShuffle = true
end
end
if needsShuffle or not ShuffledBuckets[c] then
shuffleTable(Buckets[c])
ShuffledBuckets[c] = true
end
else
-- If not using random sort, ensure bucket is sorted if it was previously shuffled
if ShuffledBuckets[c] then
table.sort(Buckets[c])
ShuffledBuckets[c] = nil
end
end
end
end
lastDetected = detected
if detected == "" then
StatusText.Text = "Waiting..."
StatusText.TextColor3 = THEME.SubText
Tween(StatusDot, {BackgroundColor3 = THEME.SubText})
UpdateList("")
else
StatusText.Text = "Input: " .. detected
StatusText.TextColor3 = THEME.Accent
Tween(StatusDot, {BackgroundColor3 = THEME.Success})
UpdateList(detected)
end
end
end)
end)
-- Global Toggle
inputConn = UserInputService.InputBegan:Connect(function(input)
if unloaded then return end
if input.KeyCode == TOGGLE_KEY then ScreenGui.Enabled = not ScreenGui.Enabled end
end)Tags
1 Tag
Advertisements
Comments
0Did it work for you? Sign in to share your experience.
No comments yet
Tried it? Let others know if it works.