Auto Horse Race
Keyless

Auto Horse Race

🎭 Arcane Lineagebradysacc999bradysacc999 1.8K views 27 executesUpdated Oct 19, 2026
Unrated
00
Lua Script•387 lines (13262 chars)
-- Arcane Lineage | Halloween Horse Race Farm (Potassium)
-- Auto Quest : clicks the Horse Race paper on the quest board when no quest is active
-- Auto Race  : teleports through every gate in order and fires the game's own gate remote
-- UI: draggable panel with two toggles + status. F6 toggles both together.

local Players = game:GetService("Players")
local RS = game:GetService("ReplicatedStorage")
local UIS = game:GetService("UserInputService")
local LocalPlayer = Players.LocalPlayer

-- stop any previous copy
if getgenv().HorseFarm and getgenv().HorseFarm.Stop then
    pcall(getgenv().HorseFarm.Stop)
end

--------------------------------------------------------------------
-- CONFIG
--------------------------------------------------------------------
local CFG = {
    AutoQuest = false,
    AutoRace = false,

    GateDelay = 0.15,      -- pause after each gate is accepted
    GateTimeout = 3,       -- seconds to wait for the server to accept a gate
    MaxRetries = 8,        -- retries on a single gate before the race bot gives up

    QuestRetry = 10,       -- min seconds between quest click attempts
    QuestTimeout = 300,    -- if no FinishQuest after this long, assume the quest is over
    BoardOffset = Vector3.new(7, 0, 0), -- where to stand next to the paper
}

local function log(...) print("[HorseFarm]", ...) end

--------------------------------------------------------------------
-- STATE
--------------------------------------------------------------------
local alive = true
local busy = false -- race runner busy
local state = {
    questActive = false,
    questSince = 0,
    finishedAt = 0,
    lastClick = 0,
    completed = 0,
    questText = "idle",
    raceText = "waiting",
}
local conns = {}

--------------------------------------------------------------------
-- GAME CONFIG (read from the game's own module)
--------------------------------------------------------------------
local okReq, HS = pcall(function()
    return require(RS:WaitForChild("Libraries"):WaitForChild("HalloweenStory"))
end)
if not okReq or type(HS) ~= "table" then
    warn("[HorseFarm] Could not load HalloweenStory module:", tostring(HS))
    return
end

local Gates = HS.Race.Gates
local Attr = HS.Race.Attributes
local GateRemote = RS:WaitForChild("Remotes"):WaitForChild("Information"):WaitForChild(HS.Remote)

--------------------------------------------------------------------
-- HELPERS
--------------------------------------------------------------------
local function getRoot()
    local char = LocalPlayer.Character
    return char and char:FindFirstChild("HumanoidRootPart")
end

local function raceActive()
    return type(LocalPlayer:GetAttribute(Attr.Gate)) == "number"
        and type(LocalPlayer:GetAttribute(Attr.Gates)) == "number"
        and type(LocalPlayer:GetAttribute(Attr.Place)) == "string"
end

local function coursesFolder()
    local f = workspace
    for _, name in ipairs(Gates.CoursePath) do
        f = f and f:FindFirstChild(name)
    end
    return f
end

local function findCourse(folder, place)
    if not folder or type(place) ~= "string" then return nil end
    for _, child in ipairs(folder:GetChildren()) do
        if child.Name == place or child:GetAttribute("Label") == place then
            return child
        end
    end
    return nil
end

--------------------------------------------------------------------
-- QUEST EVENTS (track StartQuest / FinishQuest from the server)
--------------------------------------------------------------------
for _, d in ipairs(RS:GetDescendants()) do
    if d:IsA("RemoteEvent") and d.Name == "Quest" then
        table.insert(conns, d.OnClientEvent:Connect(function(action, data)
            if action == "StartQuest" then
                state.questActive = true
                state.questSince = tick()
                log("Quest started")
            elseif action == "FinishQuest" then
                state.questActive = false
                state.finishedAt = tick()
                -- the game sends FinishQuest twice; only the one with a table counts
                if type(data) == "table" then
                    state.completed = state.completed + 1
                    log("Quest finished (total " .. state.completed .. ")")
                end
            end
        end))
    end
end

--------------------------------------------------------------------
-- AUTO QUEST
--------------------------------------------------------------------
local function acceptQuest()
    state.lastClick = tick()

    local board = workspace:FindFirstChild("QuestBoards")
    board = board and board:FindFirstChild("Halloween")
    board = board and board:FindFirstChild("Board")
    local papers = board and board:FindFirstChild("Papers")
    local paper = papers and papers:FindFirstChild("HW_HorseRace")
    local cd = paper and paper:FindFirstChildOfClass("ClickDetector")

    if not (paper and cd) then
        state.questText = "Horse Race paper not on board"
        return
    end

    local root = getRoot()
    if not root then
        state.questText = "no character"
        return
    end

    local original = root.CFrame
    state.questText = "clicking board..."

    root.CFrame = CFrame.new(paper.Position + CFG.BoardOffset)
    task.wait(0.5)
    local ok, err = pcall(fireclickdetector, cd)
    if not ok then warn("[HorseFarm] fireclickdetector error:", tostring(err)) end

    local t = tick()
    while alive and not state.questActive and (tick() - t) < 4 do
        task.wait(0.1)
    end

    if state.questActive then
        state.questText = "quest active"
    else
        state.questText = "no reply from board (retrying)"
        local r = getRoot()
        if r then r.CFrame = original end
    end
end

task.spawn(function()
    while alive do
        task.wait(1)
        if CFG.AutoQuest then
            if state.questActive and (tick() - state.questSince) > CFG.QuestTimeout then
                state.questActive = false
                state.questText = "quest timed out, retrying"
            end

            if not state.questActive and not raceActive() and not busy
                and (tick() - state.lastClick) >= CFG.QuestRetry
                and (tick() - state.finishedAt) >= 3 then
                local ok, err = pcall(acceptQuest)
                if not ok then warn("[HorseFarm] quest error:", tostring(err)) end
            end
        end
    end
end)

--------------------------------------------------------------------
-- AUTO RACE
--------------------------------------------------------------------
local function runRace()
    if busy then return end
    busy = true

    local place = LocalPlayer:GetAttribute(Attr.Place)
    local course = findCourse(coursesFolder(), place)
    if not course then
        warn("[HorseFarm] Course not found for:", tostring(place))
        state.raceText = "course not found"
        busy = false
        return
    end

    log("Race detected:", tostring(place))

    local retries, lastGate = 0, nil
    while alive and CFG.AutoRace and raceActive() do
        if LocalPlayer:GetAttribute(Attr.Place) ~= place then break end

        local gate = LocalPlayer:GetAttribute(Attr.Gate)
        local total = LocalPlayer:GetAttribute(Attr.Gates)
        state.raceText = place .. " " .. gate .. "/" .. total

        if gate == lastGate then
            retries = retries + 1
            if retries > CFG.MaxRetries then
                warn("[HorseFarm] Gate " .. gate .. " not accepted after " .. CFG.MaxRetries .. " tries - race bot stopped")
                state.raceText = "stuck on gate " .. gate
                CFG.AutoRace = false
                break
            end
        else
            retries, lastGate = 0, gate
        end

        local marker = course:FindFirstChild(tostring(gate))
        local root = getRoot()

        if marker and marker:IsA("BasePart") and root then
            root.CFrame = marker.CFrame * CFrame.new(0, (Gates.Height or 10) / 2, 0)
            pcall(function()
                root.AssemblyLinearVelocity = Vector3.zero
                root.AssemblyAngularVelocity = Vector3.zero
            end)

            task.wait(0.1)
            pcall(function() GateRemote:FireServer(Gates.Remote, gate) end)

            local t = tick()
            while alive and CFG.AutoRace and LocalPlayer:GetAttribute(Attr.Gate) == gate
                and raceActive() and (tick() - t) < CFG.GateTimeout do
                task.wait(0.05)
            end
        else
            warn("[HorseFarm] Missing gate marker or character for gate", tostring(gate))
            task.wait(0.5)
        end

        task.wait(CFG.GateDelay)
    end

    if state.raceText:find("stuck") == nil then state.raceText = "waiting" end
    log("Race loop finished")
    busy = false
end

task.spawn(function()
    while alive do
        if CFG.AutoRace and raceActive() and not busy then
            local ok, err = pcall(runRace)
            if not ok then
                busy = false
                warn("[HorseFarm] race error:", tostring(err))
            end
        end
        task.wait(0.5)
    end
end)

--------------------------------------------------------------------
-- UI
--------------------------------------------------------------------
local gui = Instance.new("ScreenGui")
gui.Name = "HorseFarmGui"
gui.ResetOnSpawn = false
pcall(function() gui.Parent = (gethui and gethui()) or game:GetService("CoreGui") end)

local frame = Instance.new("Frame")
frame.Size = UDim2.new(0, 230, 0, 170)
frame.Position = UDim2.new(0, 20, 0.4, 0)
frame.BackgroundColor3 = Color3.fromRGB(25, 25, 30)
frame.BorderSizePixel = 0
frame.Parent = gui

local title = Instance.new("TextLabel")
title.Size = UDim2.new(1, 0, 0, 26)
title.BackgroundColor3 = Color3.fromRGB(45, 30, 70)
title.BorderSizePixel = 0
title.TextColor3 = Color3.new(1, 1, 1)
title.Font = Enum.Font.GothamBold
title.TextSize = 13
title.Text = "Horse Race Farm (drag me)"
title.Parent = frame

local function makeButton(text, y)
    local b = Instance.new("TextButton")
    b.Size = UDim2.new(1, -16, 0, 28)
    b.Position = UDim2.new(0, 8, 0, y)
    b.BorderSizePixel = 0
    b.TextColor3 = Color3.new(1, 1, 1)
    b.Font = Enum.Font.GothamBold
    b.TextSize = 13
    b.Text = text
    b.Parent = frame
    return b
end

local btnQuest = makeButton("", 34)
local btnRace = makeButton("", 68)

local statusLabel = Instance.new("TextLabel")
statusLabel.Size = UDim2.new(1, -16, 0, 60)
statusLabel.Position = UDim2.new(0, 8, 0, 102)
statusLabel.BackgroundTransparency = 1
statusLabel.TextColor3 = Color3.fromRGB(210, 210, 220)
statusLabel.Font = Enum.Font.Gotham
statusLabel.TextSize = 12
statusLabel.TextXAlignment = Enum.TextXAlignment.Left
statusLabel.TextYAlignment = Enum.TextYAlignment.Top
statusLabel.TextWrapped = true
statusLabel.Text = ""
statusLabel.Parent = frame

local function refreshButtons()
    btnQuest.Text = "Auto Quest: " .. (CFG.AutoQuest and "ON" or "OFF")
    btnQuest.BackgroundColor3 = CFG.AutoQuest and Color3.fromRGB(40, 150, 70) or Color3.fromRGB(150, 40, 40)
    btnRace.Text = "Auto Race: " .. (CFG.AutoRace and "ON" or "OFF")
    btnRace.BackgroundColor3 = CFG.AutoRace and Color3.fromRGB(40, 150, 70) or Color3.fromRGB(150, 40, 40)
end
refreshButtons()

btnQuest.MouseButton1Click:Connect(function()
    CFG.AutoQuest = not CFG.AutoQuest
    refreshButtons()
end)
btnRace.MouseButton1Click:Connect(function()
    CFG.AutoRace = not CFG.AutoRace
    if CFG.AutoRace and state.raceText:find("stuck") then state.raceText = "waiting" end
    refreshButtons()
end)

-- dragging
do
    local dragging, dragStart, startPos
    table.insert(conns, title.InputBegan:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseButton1 then
            dragging, dragStart, startPos = true, input.Position, frame.Position
        end
    end))
    table.insert(conns, UIS.InputChanged:Connect(function(input)
        if dragging and input.UserInputType == Enum.UserInputType.MouseMovement then
            local d = input.Position - dragStart
            frame.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + d.X,
                startPos.Y.Scale, startPos.Y.Offset + d.Y)
        end
    end))
    table.insert(conns, UIS.InputEnded:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = false end
    end))
end

-- F6 = toggle both
table.insert(conns, UIS.InputBegan:Connect(function(input, processed)
    if not processed and input.KeyCode == Enum.KeyCode.F6 then
        local on = not (CFG.AutoQuest or CFG.AutoRace)
        CFG.AutoQuest, CFG.AutoRace = on, on
        refreshButtons()
    end
end))

-- status text
task.spawn(function()
    while alive do
        statusLabel.Text = "Quest: " .. state.questText
            .. "\nRace: " .. state.raceText
            .. "\nCompleted: " .. state.completed .. "   (F6 = both)"
        task.wait(0.5)
    end
end)

getgenv().HorseFarm = {
    Stop = function()
        alive = false
        CFG.AutoQuest, CFG.AutoRace = false, false
        for _, c in ipairs(conns) do pcall(function() c:Disconnect() end) end
        gui:Destroy()
    end,
}

log("Loaded. Turn on Auto Quest and Auto Race (or press F6).")

Features & Information

completes the horse race quest for spook tokens so you can afk it overnight doesnt work for the desert location bc it finishes it too fast if u want add a buffer for when the desert quest is detected -vibecoded with claude code

Community Comments (0)

No comments yet for this script.

More Scripts for 🎭 Arcane Lineage