-- [[ Rscripts Risk Notice ]] -- This script is not verified by rscripts.net. Deal with caution. -- -- Stay safe: -- • Never log in on unofficial Roblox sites or lookalike domains. -- • Real Roblox links use roblox.com (check the .com ending). -- • Treat fake Roblox login / "claim reward" pages as phishing. -- [[ End Rscripts Risk Notice ]] local playersService = game:GetService("Players") local runService = game:GetService("RunService") local userInputService = game:GetService("UserInputService") local httpService = game:GetService("HttpService") local teleportService = game:GetService("TeleportService") local STATE = _G.STATE if not STATE then STATE = { connect = function(signal, fn) return signal:Connect(fn) end, onCleanup = function() end, } end local MENU_TOGGLE_KEY = Enum.KeyCode.RightShift local PANIC_KEY = Enum.KeyCode.Delete local MIN_TAP_INTERVAL = 0.02 local MAX_TAP_INTERVAL = 0.2 local CONFIG_FOLDER = "ChainFightAutoQTE" local CONFIG_PATH = "ChainFightAutoQTE/config.json" local VALID_KEYS = {F = true, E = true, R = true, T = true, G = true} local CHAIN_COLOR = Color3.fromRGB(255, 60, 60) local HUMAN_COLOR = Color3.fromRGB(60, 200, 255) local MIN_ESP_DISTANCE = 100 local MAX_ESP_DISTANCE = 2000 local localPlayer = playersService.LocalPlayer local playerGui = localPlayer:WaitForChild("PlayerGui") local qteGui = playerGui:WaitForChild("QTERandGui") local qteFrame = qteGui:WaitForChild("Frame") local qteText = qteFrame:WaitForChild("TextLabel") local spamGui = playerGui:WaitForChild("QTESpam") local spamFrame = spamGui:WaitForChild("Frame") local spamText = spamFrame:WaitForChild("TextLabel") local Potent = { autoQteEnabled = false, highlighterEnabled = true, tapInterval = 0.05, lastTapAt = 0, espEnabled = true, espDistance = 500, } local fluent = nil local fluentWindow = nil local fluentOptions = nil local screenGui = nil local keyHighlighter = nil local activePrompt = "" local espHighlights = {} local espLabels = {} local diagnosticsParagraph = nil local diagnosticState = { lastPrompt = "-", lastInput = "-", tapCount = 0, invalidCount = 0, } local inputBeganConn = nil local heartbeatPromptConn = nil local heartbeatEspConn = nil local unloaded = false local function saveConfig() if not isfolder(CONFIG_FOLDER) then makefolder(CONFIG_FOLDER) end writefile(CONFIG_PATH, httpService:JSONEncode({ autoQteEnabled = Potent.autoQteEnabled, highlighterEnabled = Potent.highlighterEnabled, tapInterval = Potent.tapInterval, espEnabled = Potent.espEnabled, espDistance = Potent.espDistance, })) if fluent then fluent:Notify({Title = "Configuration saved", Content = "Chain Fight settings were saved.", Duration = 3}) end end local function loadConfig() if not isfile(CONFIG_PATH) then return end local ok, data = pcall(function() return httpService:JSONDecode(readfile(CONFIG_PATH)) end) if not ok or type(data) ~= "table" then return end if type(data.autoQteEnabled) == "boolean" then Potent.autoQteEnabled = data.autoQteEnabled end if type(data.highlighterEnabled) == "boolean" then Potent.highlighterEnabled = data.highlighterEnabled end if type(data.tapInterval) == "number" then Potent.tapInterval = math.clamp(data.tapInterval, MIN_TAP_INTERVAL, MAX_TAP_INTERVAL) end if type(data.espEnabled) == "boolean" then Potent.espEnabled = data.espEnabled end if type(data.espDistance) == "number" then Potent.espDistance = math.clamp(data.espDistance, MIN_ESP_DISTANCE, MAX_ESP_DISTANCE) end end local function disableAutomation() Potent.autoQteEnabled = false Potent.highlighterEnabled = false if fluentOptions then pcall(function() fluentOptions.AutoQTE:SetValue(false) end) pcall(function() fluentOptions.KeyHighlighter:SetValue(false) end) end if fluent then fluent:Notify({Title = "Automation paused", Content = "Auto QTE and the key highlighter are disabled.", Duration = 3}) end end local function isPlayerChain(player) local character = player.Character if not character then return nil end local cn = character:FindFirstChild("Cn") if not cn or not cn:IsA("BoolValue") then return nil end return cn.Value end local function clearEspTarget(player) local highlight = espHighlights[player] if highlight then highlight:Destroy() espHighlights[player] = nil end local label = espLabels[player] if label then label:Remove() espLabels[player] = nil end end local function updateEsp() if not Potent.espEnabled then for player in pairs(espHighlights) do clearEspTarget(player) end return end -- Chains see humans; humans see chains. local wantChain = not isPlayerChain(localPlayer) local camera = workspace.CurrentCamera for _, player in ipairs(playersService:GetPlayers()) do if player == localPlayer then continue end local character = player.Character local isChain = isPlayerChain(player) if not character or isChain == nil or isChain ~= wantChain then clearEspTarget(player) continue end local hrp = character:FindFirstChild("HumanoidRootPart") local humanoid = character:FindFirstChildWhichIsA("Humanoid") if not hrp or not humanoid or humanoid.Health <= 0 then clearEspTarget(player) continue end local color = isChain and CHAIN_COLOR or HUMAN_COLOR local highlight = espHighlights[player] if not highlight then highlight = Instance.new("Highlight") highlight.Name = "PotentEsp" highlight.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop highlight.FillTransparency = 0.6 highlight.OutlineTransparency = 0 highlight.Parent = character espHighlights[player] = highlight end highlight.FillColor = color highlight.OutlineColor = color local label = espLabels[player] if not label then label = Drawing.new("Text") label.Center = true label.Outline = true label.Font = Drawing.Fonts.Plex label.Size = 16 espLabels[player] = label end local distance = (hrp.Position - camera.CFrame.Position).Magnitude if distance <= Potent.espDistance then local screenPos, onScreen = camera:WorldToViewportPoint(hrp.Position + Vector3.new(0, 3, 0)) if onScreen then label.Position = Vector2.new(screenPos.X, screenPos.Y) label.Text = string.format("%s [%s] %dm", player.Name, isChain and "CHAIN" or "HUMAN", distance) label.Color = color label.Visible = true else label.Visible = false end else label.Visible = false end end for player in pairs(espHighlights) do if not player.Parent then clearEspTarget(player) end end end local function teardown() if unloaded then return end unloaded = true if inputBeganConn then pcall(function() inputBeganConn:Disconnect() end) end if heartbeatPromptConn then pcall(function() heartbeatPromptConn:Disconnect() end) end if heartbeatEspConn then pcall(function() heartbeatEspConn:Disconnect() end) end for player in pairs(espHighlights) do clearEspTarget(player) end if keyHighlighter then pcall(function() keyHighlighter:Remove() end) end end local function rejoinCurrentServer() local exactOk = pcall(function() teleportService:TeleportToPlaceInstance(game.PlaceId, game.JobId, localPlayer) end) if exactOk then return end local fallbackOk, fallbackErr = pcall(function() teleportService:Teleport(game.PlaceId, localPlayer) end) if not fallbackOk and fluent then fluent:Notify({Title = "Rejoin failed", Content = tostring(fallbackErr), Duration = 4}) end end local function updateDiagnostics() if not diagnosticsParagraph then return end local singleVisible = qteFrame.Visible and qteText.Visible local spamVisible = spamFrame.Visible and spamText.Visible local promptVisible = singleVisible or spamVisible local focused = isrbxactive() and "Yes" or "No" local content = string.format( "Prompt: %s\nPrompt visible: %s\nRoblox focused: %s\nAuto QTE: %s\nLast input: %s\nTaps sent: %d\nInvalid prompts: %d", diagnosticState.lastPrompt, promptVisible and "Yes" or "No", focused, Potent.autoQteEnabled and "On" or "Off", diagnosticState.lastInput, diagnosticState.tapCount, diagnosticState.invalidCount ) pcall(function() diagnosticsParagraph:SetDesc(content) end) end local function buildUi() local hiddenGui = gethui() local oldFluent = hiddenGui:FindFirstChild("ScreenGui") if oldFluent then oldFluent:Destroy() end local oldChainFight = hiddenGui:FindFirstChild("ChainFightAutoQTE") if oldChainFight then oldChainFight:Destroy() end fluent = loadstring(game:HttpGet("https://github.com/dawid-scripts/Fluent/releases/latest/download/main.lua"))() fluentWindow = fluent:CreateWindow({ Title = "Chain Fight / Auto QTE", SubTitle = "QTE assistance", TabWidth = 150, Size = UDim2.fromOffset(620, 500), Acrylic = true, Theme = "Dark", MinimizeKey = MENU_TOGGLE_KEY, }) local qteTab = fluentWindow:AddTab({Title = "Auto QTE", Icon = "zap"}) local espTab = fluentWindow:AddTab({Title = "ESP", Icon = "eye"}) local diagnosticsTab = fluentWindow:AddTab({Title = "Diagnostics", Icon = "activity"}) local settingsTab = fluentWindow:AddTab({Title = "Settings", Icon = "settings"}) fluentOptions = fluent.Options qteTab:AddParagraph({ Title = "Chain Fight QTE", Content = "This menu watches the game's own QTE prompt and presses the displayed PC key when it appears." }) diagnosticsParagraph = diagnosticsTab:AddParagraph({ Title = "Live input diagnostics", Content = "Waiting for the first prompt..." }) diagnosticsTab:AddButton({ Title = "Reset counters", Description = "Clear tap and invalid-prompt counts.", Callback = function() diagnosticState.tapCount = 0 diagnosticState.invalidCount = 0 diagnosticState.lastInput = "Reset" updateDiagnostics() end, }) qteTab:AddToggle("AutoQTE", { Title = "Auto QTE", Description = "Automatically press F, E, R, T, or G during active prompts.", Default = Potent.autoQteEnabled, Callback = function(value) Potent.autoQteEnabled = value end, }) qteTab:AddToggle("KeyHighlighter", { Title = "Key highlighter", Description = "Show the active prompt in the center of the screen.", Default = Potent.highlighterEnabled, Callback = function(value) Potent.highlighterEnabled = value end, }) qteTab:AddParagraph({ Title = "Prompt keys", Content = "Supports the single-key, random-sequence, and repeated-F QTE modes. Prompts use F, E, R, T, and G." }) espTab:AddParagraph({ Title = "Chain / Human ESP", Content = "Highlights the opposite role: chains see humans, humans see chains. Follows your current role automatically." }) espTab:AddToggle("EspEnabled", { Title = "ESP", Description = "Highlight nearby players of the opposite role.", Default = Potent.espEnabled, Callback = function(value) Potent.espEnabled = value end, }) espTab:AddSlider("EspDistance", { Title = "ESP distance", Description = "Only highlight players within this distance.", Min = MIN_ESP_DISTANCE, Max = MAX_ESP_DISTANCE, Default = Potent.espDistance, Rounding = 0, Callback = function(value) Potent.espDistance = value end, }) settingsTab:AddParagraph({ Title = "Input settings", Content = "Tune the delay between repeated key taps. Right Shift toggles the Fluent window. Delete pauses automation." }) settingsTab:AddSlider("TapInterval", { Title = "Tap interval", Description = "Delay between repeated taps while the prompt is visible.", Min = MIN_TAP_INTERVAL, Max = MAX_TAP_INTERVAL, Default = Potent.tapInterval, Rounding = 2, Callback = function(value) Potent.tapInterval = value end, }) settingsTab:AddButton({ Title = "Save configuration", Description = "Save Auto QTE settings to disk.", Callback = saveConfig, }) settingsTab:AddButton({ Title = "Load configuration", Description = "Load the saved Auto QTE settings.", Callback = function() loadConfig() if fluentOptions then pcall(function() fluentOptions.AutoQTE:SetValue(Potent.autoQteEnabled) end) pcall(function() fluentOptions.KeyHighlighter:SetValue(Potent.highlighterEnabled) end) pcall(function() fluentOptions.TapInterval:SetValue(Potent.tapInterval) end) pcall(function() fluentOptions.EspEnabled:SetValue(Potent.espEnabled) end) pcall(function() fluentOptions.EspDistance:SetValue(Potent.espDistance) end) end end, }) settingsTab:AddButton({ Title = "Pause automation", Description = "Disable Auto QTE and the key highlighter immediately.", Callback = disableAutomation, }) settingsTab:AddButton({ Title = "Rejoin current server", Description = "Reconnect to this exact server instance.", Callback = rejoinCurrentServer, }) -- The window's own X button asks to confirm, then calls fluent:Destroy(); piggyback on that to fully unload. local originalDestroy = fluent.Destroy fluent.Destroy = function(self, ...) teardown() return originalDestroy(self, ...) end fluentWindow:SelectTab(1) screenGui = fluent.GUI fluent:Notify({Title = "Chain Fight Auto QTE loaded", Content = "Ready to assist with the game's QTE prompts.", Duration = 4}) STATE.onCleanup(function() if fluent then pcall(function() fluent:Destroy() end) end end) end local function buildHighlighter() keyHighlighter = Drawing.new("Text") keyHighlighter.Visible = false keyHighlighter.Center = true keyHighlighter.Outline = true keyHighlighter.Font = Drawing.Fonts.Plex keyHighlighter.Size = 64 keyHighlighter.Color = Color3.fromRGB(255, 255, 255) STATE.onCleanup(function() keyHighlighter:Remove() end) STATE.onCleanup(function() for player in pairs(espHighlights) do clearEspTarget(player) end end) end local function updatePrompt() updateDiagnostics() local prompt = "" if qteFrame.Visible and qteText.Visible then prompt = string.upper(qteText.Text or "") elseif spamFrame.Visible and spamText.Visible then prompt = string.upper(spamText.Text or "") end if VALID_KEYS[prompt] then activePrompt = prompt diagnosticState.lastPrompt = prompt if Potent.highlighterEnabled then local camera = workspace.CurrentCamera keyHighlighter.Text = prompt keyHighlighter.Position = Vector2.new(camera.ViewportSize.X / 2, camera.ViewportSize.Y * 0.35) keyHighlighter.Visible = true else keyHighlighter.Visible = false end if Potent.autoQteEnabled then if not isrbxactive() then diagnosticState.lastInput = "Skipped: Roblox not focused" return end local now = os.clock() if now - Potent.lastTapAt >= Potent.tapInterval then Potent.lastTapAt = now keytap(string.byte(prompt)) diagnosticState.lastInput = "Sent " .. prompt diagnosticState.tapCount = diagnosticState.tapCount + 1 end end else activePrompt = "" diagnosticState.lastPrompt = prompt ~= "" and prompt or "-" if prompt ~= "" then diagnosticState.invalidCount = diagnosticState.invalidCount + 1 end keyHighlighter.Visible = false end end local function initialize() loadConfig() buildUi() buildHighlighter() inputBeganConn = STATE.connect(userInputService.InputBegan, function(input, processed) if processed then return end if input.KeyCode == PANIC_KEY then disableAutomation() end end) heartbeatPromptConn = STATE.connect(runService.Heartbeat, updatePrompt) heartbeatEspConn = STATE.connect(runService.Heartbeat, updateEsp) STATE.onCleanup(function() keyHighlighter.Visible = false end) end xpcall(initialize, function(err) warn("Chain Fight Auto QTE failed to initialize.") warn(err) warn(debug.traceback()) end)