
Key Required

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local StarterGui = game:GetService("StarterGui")
local UserInputService = game:GetService("UserInputService")
local HttpService = game:GetService("HttpService")
local Workspace = game:GetService("Workspace")
local player = Players.LocalPlayer
local UI_URL = "https://raw.githubusercontent.com/madonchik123/NEW_UI_LIB/refs/heads/executor/UI_LIB.lua"
if game.PlaceId ~= 537413528 then
warn("Open Build a Boat for Treasure first")
return
end
if type(loadstring) ~= "function" then
warn("This executor does not support loadstring")
return
end
local playerGui = player:WaitForChild("PlayerGui", 10)
if not playerGui then
warn("PlayerGui has not loaded")
return
end
local cfg = {
Farm = false,
TravelTime = 30,
Height = 65,
SpawnDelay = 3,
ChestWait = 35,
Dashboard = false,
SoccerMode = "Goal then chest",
}
local state = {
Alive = true,
Status = "Idle",
Job = nil,
Motion = nil,
Connections = {},
ValueConnections = {},
ChestCharacters = setmetatable({}, { __mode = "k" }),
ClaimedFrames = setmetatable({}, { __mode = "k" }),
Started = os.clock(),
Arrivals = 0,
Deaths = 0,
GoldEarned = 0,
BlocksEarned = 0,
QuestCooldown = 0,
GlueRequests = 0,
}
local ui = {}
local library, window
local setDashboard, updateDashboard, buildImporterUi, openBrowser, clearPreview, removeSoccerGlue
local syncingToggle = false
local function findPath(root, ...)
for _, name in ipairs({ ... }) do
root = root and root:FindFirstChild(name)
end
return root
end
local function zone()
return Workspace:FindFirstChild(tostring(player.TeamColor) .. "Zone")
end
local function activeQuest()
local value = findPath(zone(), "QuestNum")
return if value then value.Value else 0
end
local function questCompleted(id)
local key = "Q" .. tostring(id)
if id == 101 then
key ..= tostring(os.date("!*t").year)
end
return findPath(player, "OtherData", key) ~= nil
end
local function questCard(id)
return findPath(
player,
"PlayerGui",
"ShopGui",
"MainFrame",
"TabFrame",
"QuestFrame",
"ScrollingFrame",
"Quest" .. id
) or findPath(StarterGui, "ShopGui", "MainFrame", "TabFrame", "QuestFrame", "ScrollingFrame", "Quest" .. id)
end
local function questAvailable(id)
local card = questCard(id)
return card ~= nil and card.Visible
end
local function numberValue(folder, name)
local value = findPath(player, folder, name)
return if value and type(value.Value) == "number" then value.Value else nil
end
local function characterParts()
local character = player.Character
local root = character and character:FindFirstChild("HumanoidRootPart")
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
if character and character.Parent and root and humanoid and humanoid.Health > 0 then
return character, root, humanoid
end
return nil
end
local function current(job)
return state.Alive and state.Job == job
end
local function sameCharacter(job)
return current(job)
and player.Character == job.Character
and job.Character.Parent ~= nil
and job.Root.Parent == job.Character
and job.Humanoid.Health > 0
end
local function pause(job, seconds, requireCharacter)
local deadline = os.clock() + seconds
repeat
if not current(job) or (requireCharacter and not sameCharacter(job)) then
return false
end
task.wait(math.min(0.1, math.max(0, deadline - os.clock())))
until os.clock() >= deadline
return current(job) and (not requireCharacter or sameCharacter(job))
end
local function requestsPending()
return state.BuildBusy == true or state.GlueRequests > 0 or state.PendingGlue ~= nil
end
local function syncToggles()
syncingToggle = true
if ui.Farm then
ui.Farm:Set(cfg.Farm, true)
end
if ui.Quests then
ui.Quests:Set(state.Job ~= nil and state.Job.Kind == "Quest", true)
end
if ui.Build then
ui.Build:SetDisabled(state.Job ~= nil or requestsPending())
end
if ui.StopBuild then
ui.StopBuild:SetDisabled(not state.BuildBusy and not (state.Job and state.Job.Kind == "Build"))
end
if ui.Preview then
ui.Preview:SetDisabled(state.Job ~= nil or requestsPending())
end
syncingToggle = false
end
local function releaseMotion(job)
local motion = state.Motion
if job and motion and motion.Job ~= job then
return
end
state.Motion = nil
if not motion then
return
end
if motion.Glue then
removeSoccerGlue(motion)
end
for _, connection in ipairs(motion.Connections) do
connection:Disconnect()
end
for _, object in ipairs(motion.Objects) do
object:Destroy()
end
if motion.Ball and motion.Ball.Parent then
motion.Ball.CanCollide = motion.BallCollision
end
for part, collision in pairs(motion.Collisions) do
if part.Parent then
part.CanCollide = collision
end
end
if motion.Humanoid.Parent then
motion.Humanoid.AutoRotate = motion.AutoRotate
motion.Humanoid.PlatformStand = motion.PlatformStand
end
if motion.Root.Parent then
motion.Root.AssemblyLinearVelocity = Vector3.zero
motion.Root.AssemblyAngularVelocity = Vector3.zero
end
end
local function stopJob(message)
local job = state.Job
state.Job = nil
cfg.Farm = false
if job and job.Kind == "Farm" and job.Thread and job.Thread ~= coroutine.running() then
pcall(task.cancel, job.Thread)
end
releaseMotion(job)
syncToggles()
if message then
state.Status = message
end
end
local function acquireMotion(job)
if not sameCharacter(job) then
error("Character changed before movement", 0)
end
releaseMotion(job)
if job.Root.Anchored then
error("Wait for the game to release your anchored character", 0)
end
local motion = {
Job = job,
Root = job.Root,
Humanoid = job.Humanoid,
AutoRotate = job.Humanoid.AutoRotate,
PlatformStand = job.Humanoid.PlatformStand,
Collisions = {},
Connections = {},
Objects = {},
}
state.Motion = motion
local function trackPart(part)
if part:IsA("BasePart") and motion.Collisions[part] == nil then
motion.Collisions[part] = part.CanCollide
part.CanCollide = false
end
end
for _, part in ipairs(job.Character:GetDescendants()) do
trackPart(part)
end
table.insert(motion.Connections, job.Character.DescendantAdded:Connect(trackPart))
local attachment = Instance.new("Attachment")
table.insert(motion.Objects, attachment)
attachment.Name = "BABFTMovement"
attachment.Parent = job.Root
local velocity = Instance.new("LinearVelocity")
table.insert(motion.Objects, velocity)
velocity.Attachment0 = attachment
velocity.RelativeTo = Enum.ActuatorRelativeTo.World
velocity.VelocityConstraintMode = Enum.VelocityConstraintMode.Vector
velocity.ForceLimitsEnabled = false
velocity.VectorVelocity = Vector3.zero
velocity.Parent = job.Root
motion.Velocity = velocity
job.Humanoid.Sit = false
job.Humanoid.AutoRotate = false
job.Humanoid.PlatformStand = true
job.Root.AssemblyLinearVelocity = Vector3.zero
table.insert(
motion.Connections,
RunService.Stepped:Connect(function()
if state.Motion ~= motion then
return
end
if not sameCharacter(job) then
releaseMotion(job)
return
end
for part in pairs(motion.Collisions) do
if part.Parent then
part.CanCollide = false
else
motion.Collisions[part] = nil
end
end
job.Root.AssemblyAngularVelocity = Vector3.zero
end)
)
end
local function moveTo(job, target, duration)
if not sameCharacter(job) or not state.Motion then
return false
end
local motion = state.Motion
if motion.Ball then
duration = math.max(duration, (job.Root.Position - target.Position).Magnitude / 100)
end
local speed = math.clamp((job.Root.Position - target.Position).Magnitude / math.max(0.1, duration), 4, 800)
local deadline = os.clock() + duration + 4
while sameCharacter(job) and state.Motion == motion and os.clock() < deadline do
if job.Kind == "Quest" and job.QuestId and (zone() ~= job.QuestZone or activeQuest() ~= job.QuestId) then
break
end
if motion.Ball then
if not motion.Ball.Parent or motion.Ball.Anchored or not motion.Glue or not motion.Glue.Parent then
break
end
local destination = job.Root.CFrame:PointToWorldSpace(motion.BallLocal)
if (motion.Ball.Position - destination).Magnitude > 3 then
break
end
end
local delta = target.Position - job.Root.Position
if delta.Magnitude < 0.5 then
motion.Velocity.VectorVelocity = Vector3.zero
job.Root.AssemblyLinearVelocity = Vector3.zero
return true
end
motion.Velocity.VectorVelocity = delta.Unit * math.min(speed, delta.Magnitude * 12)
RunService.Heartbeat:Wait()
end
if state.Motion == motion then
motion.Velocity.VectorVelocity = Vector3.zero
end
return false
end
local function waitForCharacter(job, previousCharacter, timeout)
local deadline = os.clock() + timeout
while current(job) and os.clock() < deadline do
local character, root, humanoid = characterParts()
if character and character ~= previousCharacter and not root.Anchored then
job.Character, job.Root, job.Humanoid = character, root, humanoid
if pause(job, cfg.SpawnDelay, true) then
return true
end
end
task.wait(0.1)
end
return false
end
local function getRoute()
local stages = findPath(Workspace, "BoatStages", "NormalStages")
local trigger = findPath(stages, "TheEnd", "GoldenChest", "Trigger")
if not (trigger and trigger:IsA("BasePart")) then
error("Golden chest is not loaded", 0)
end
local gates = {}
for _, model in ipairs(stages:GetChildren()) do
if model.Name:match("^CaveStage%d+$") then
local gate = model:FindFirstChild("DarknessPart")
if gate and gate:IsA("BasePart") then
table.insert(gates, gate)
end
end
end
if #gates ~= 10 then
error("River route is incomplete; expected 10 loaded cave gates", 0)
end
table.sort(gates, function(a, b)
return a.Position.Z < b.Position.Z
end)
return gates, trigger
end
local function claimResults()
local gui = findPath(player, "PlayerGui", "RiverResultsGui")
local frame = gui and gui:FindFirstChild("Frame")
if not gui or not gui.Enabled or not frame or not frame.Visible or state.ClaimedFrames[frame] then
return
end
local remote = Workspace:FindFirstChild("ClaimRiverResultsGold")
if not remote or not remote:IsA("RemoteEvent") then
return
end
state.ClaimedFrames[frame] = true
remote:FireServer()
frame.Visible = false
if not cfg.Dashboard then
pcall(StarterGui.SetCoreGuiEnabled, StarterGui, Enum.CoreGuiType.Backpack, true)
end
end
local function chestAcknowledged(job)
local goldBlocks = numberValue("Data", "GoldBlock")
local endValue = findPath(player, "OtherData", "End")
return (goldBlocks and job.GoldBlocks and goldBlocks > job.GoldBlocks)
or (endValue and endValue.Value ~= "" and endValue.Value ~= job.EndValue)
or job.Character:FindFirstChild("TreasureEndEffectsLS", true) ~= nil
end
local function runTrip(job)
local gates, trigger = getRoute()
local first, last = gates[1].Position, gates[#gates].Position
local start = Vector3.new(first.X, cfg.Height, first.Z - 1540)
local finish = Vector3.new(last.X, cfg.Height, last.Z + 450)
state.Status = "Moving to river entrance"
acquireMotion(job)
if not moveTo(job, CFrame.new(start), 1.5) then
return false
end
local distance, previousZ = finish.Z - start.Z, start.Z
for index, gate in ipairs(gates) do
state.Status = string.format("River gate %d / %d", index, #gates)
local target = Vector3.new(gate.Position.X, cfg.Height, gate.Position.Z)
local duration = cfg.TravelTime * (target.Z - previousZ) / distance
if not moveTo(job, CFrame.new(target), math.max(0.2, duration)) then
return false
end
previousZ = target.Z
end
if not moveTo(job, CFrame.new(finish), cfg.TravelTime * (finish.Z - previousZ) / distance) then
return false
end
if not (trigger.Parent and not state.ChestCharacters[job.Character]) then
error("Respawn before another chest activation", 0)
end
state.Status = "Approaching chest"
local approach = trigger.CFrame * CFrame.new(0, 0, trigger.Size.Z / 2 + 6)
if not moveTo(job, approach, 2.5) or not sameCharacter(job) then
return false
end
job.GoldBlocks = numberValue("Data", "GoldBlock")
local endValue = findPath(player, "OtherData", "End")
job.EndValue = endValue and endValue.Value
state.ChestCharacters[job.Character] = true
state.Status = "Chest activated; waiting for server"
if type(firetouchinterest) == "function" then
local ok, problem = pcall(firetouchinterest, job.Root, trigger, 0)
pcall(firetouchinterest, job.Root, trigger, 1)
if not ok then
error(tostring(problem), 0)
end
else
if not moveTo(job, trigger.CFrame, 0.6) then
if chestAcknowledged(job) then
state.Arrivals += 1
return true
end
return false
end
end
state.Arrivals += 1
local deadline = os.clock() + cfg.ChestWait
while sameCharacter(job) and os.clock() < deadline do
if chestAcknowledged(job) then
state.Status = "Chest acknowledged; waiting for respawn"
releaseMotion(job)
end
task.wait(0.1)
end
releaseMotion(job)
if sameCharacter(job) then
state.Status = "Chest wait expired; resetting this character"
job.Humanoid.Health = 0
end
return true
end
local function farm(job)
local previousCharacter
while current(job) and cfg.Farm do
state.Status = "Waiting for a fresh character"
if not (waitForCharacter(job, previousCharacter, 45)) then
error("No fresh character within 45 seconds; farm paused", 0)
end
if state.ChestCharacters[job.Character] or job.Root.Position.Z > 900 then
error("Respawn at your plot before restarting the farm", 0)
end
if activeQuest() ~= 0 then
error("Finish or cancel your active quest before farming", 0)
end
local reached = runTrip(job)
releaseMotion(job)
if not current(job) then
return
end
previousCharacter = job.Character
if not reached then
if sameCharacter(job) then
error("Movement interrupted", 0)
end
state.Deaths += 1
end
end
end
local function teamLeader()
local leader = findPath(player.Team, "TeamLeader")
return leader and leader.Value == player.Name
end
local function requestQuest(id)
if not teamLeader() then
error("Only your team leader can start or cancel quests", 0)
end
if os.clock() < state.QuestCooldown then
error("Wait before another quest request", 0)
end
if id ~= 0 then
if activeQuest() ~= 0 then
error("Finish or cancel the active quest first", 0)
end
if not questAvailable(id) then
error("This quest is unavailable or its menu has not loaded", 0)
end
local blocks = Workspace:FindFirstChild("Blocks")
if not blocks then
error("Building data has not loaded", 0)
end
for _, teammate in ipairs(player.Team:GetPlayers()) do
local folder = blocks:FindFirstChild(teammate.Name)
if folder then
for _, block in ipairs(folder:GetChildren()) do
if block:IsA("Model") and block.PrimaryPart and block.PrimaryPart.Position.Z < 900 then
error("Clear your team's build before starting a quest; builds are never cleared automatically")
end
end
end
end
end
local remote = Workspace:FindFirstChild("QuestMakerEvent")
if not (remote and remote:IsA("RemoteEvent")) then
error("QuestMakerEvent is unavailable", 0)
end
state.QuestCooldown = os.clock() + 1.5
remote:FireServer(id)
state.Status = "Starting quest"
end
local function awaitQuest(job, id)
local deadline = os.clock() + 10
while current(job) and activeQuest() ~= id and os.clock() < deadline do
task.wait(0.1)
end
if not (current(job) and activeQuest() == id) then
error("Quest state did not update", 0)
end
end
local function questObjectName(object, root)
local names = {}
while object and object ~= root do
table.insert(names, object.Name:lower())
object = object.Parent
end
return table.concat(names, "/")
end
local function questTargetScore(name, id, height)
for _, word in ipairs({ "kill", "lava", "water", "death", "reset", "fail", "boundary", "ground", "floor" }) do
if name:find(word, 1, true) then
return nil
end
end
local hint = if id == 1 then "cloud" elseif id == 2 then "target" else "find"
return (if name:find(hint, 1, true) then 100 else 0) + math.clamp(height, 0, 60)
end
local function questInteractions(id, root)
local candidates, seen = {}, {}
local plot = zone()
for _, object in ipairs(root:GetDescendants()) do
local part
if id == 4 and object:IsA("ClickDetector") and object.MaxActivationDistance > 0 then
local parent = object.Parent
part = if parent:IsA("BasePart") then parent elseif parent:IsA("Model") then parent.PrimaryPart else nil
elseif id ~= 4 and object:IsA("TouchTransmitter") and object.Parent:IsA("BasePart") then
part = object.Parent
end
if part and (id == 4 or part.CanTouch) and not seen[part] then
seen[part] = true
local name = questObjectName(part, root)
local score = questTargetScore(name, id, part.Position.Y - (if plot then plot.Position.Y else 0))
if score then
table.insert(candidates, { Part = part, Interaction = object, Score = score, Name = name })
end
end
end
table.sort(candidates, function(a, b)
if a.Score == b.Score then
return a.Name < b.Name
end
return a.Score > b.Score
end)
return candidates
end
local function pulseTouch(first, second)
if type(firetouchinterest) ~= "function" then
return
end
local ok, problem = pcall(firetouchinterest, first, second, 0)
pcall(firetouchinterest, first, second, 1)
if not ok then
warn("[BABFT] Touch: " .. tostring(problem))
end
end
local function waitQuestResult(job, id, seconds)
local deadline = os.clock() + seconds
repeat
if not current(job) or zone() ~= job.QuestZone then
return false
end
if questCompleted(id) then
return true
end
if activeQuest() ~= 0 and activeQuest() ~= id then
return false
end
task.wait(0.1)
until os.clock() >= deadline
return current(job) and zone() == job.QuestZone and questCompleted(id)
end
local function assistQuest(job, id)
if not waitForCharacter(job, nil, 15) then
error("No living character available", 0)
end
local origin = job.Root.CFrame
local deadline, emptySince = os.clock() + 45, os.clock()
local tried, lastNames = {}, {}
while sameCharacter(job) and zone() == job.QuestZone and os.clock() < deadline do
if questCompleted(id) then
break
end
if activeQuest() ~= id then
break
end
local root = findPath(job.QuestZone, "Quest")
local candidates = if root then questInteractions(id, root) else {}
local selected, leastAttempts
for index, candidate in ipairs(candidates) do
if index > 16 then
break
end
local previous = tried[candidate.Part]
if previous and (candidate.Part.Position - previous.Position).Magnitude > 2 then
previous = nil
end
local count = if previous then previous.Count else 0
if count < 2 and (leastAttempts == nil or count < leastAttempts) then
selected = candidate
leastAttempts = count
end
end
if not selected then
releaseMotion(job)
if #candidates > 0 or os.clock() - emptySince > 8 then
break
end
state.Status = "Waiting for quest objects"
if not pause(job, 0.25, true) then
break
end
continue
end
emptySince = os.clock()
local part, interaction = selected.Part, selected.Interaction
table.insert(lastNames, selected.Name)
if not state.Motion then
acquireMotion(job)
end
state.Status = (if id == 1 then "Cloud" elseif id == 2 then "Target" else "Find Me") .. ": " .. part.Name
local position = part.Position
local previous = tried[part]
local attempts = if previous and (position - previous.Position).Magnitude < 2 then previous.Count else 0
tried[part] = { Position = position, Count = attempts + 1 }
local approach = part.CFrame
* (
if id == 4
then CFrame.new(0, 0, math.min(3, interaction.MaxActivationDistance / 2))
else CFrame.new(0, part.Size.Y / 2 + 4, 0)
)
local duration = math.clamp((job.Root.Position - approach.Position).Magnitude / 180, 0.4, 5)
local reached = moveTo(job, approach, duration)
if reached and part.Parent and sameCharacter(job) and activeQuest() == id then
if id == 4 then
fireclickdetector(interaction, (job.Root.Position - part.Position).Magnitude, "MouseClick")
else
reached = moveTo(job, part.CFrame, 0.5)
if reached and sameCharacter(job) and part.Parent and activeQuest() == id then
pulseTouch(job.Root, part)
end
end
end
if waitQuestResult(job, id, 1.25) then
break
end
end
job.QuestId = nil
if sameCharacter(job) and zone() == job.QuestZone and state.Motion then
moveTo(job, origin, math.clamp((job.Root.Position - origin.Position).Magnitude / 200, 0.5, 5))
end
releaseMotion(job)
if not waitQuestResult(job, id, 1.5) then
error("Quest " .. id .. " was not confirmed; tried " .. table.concat(lastNames, ", "), 0)
end
state.Status = "Quest completed"
end
local function soccerBall(root)
local best, bestScore, tied
for _, part in ipairs(root:GetDescendants()) do
if not part:IsA("BasePart") or part.Anchored then
continue
end
local assembly = part.AssemblyRootPart
if assembly and assembly.Anchored then
continue
end
local name = questObjectName(part, root)
local kick = part:FindFirstChild("KickRE")
local round = part:IsA("Part") and part.Shape == Enum.PartType.Ball
local mesh = part:FindFirstChildOfClass("SpecialMesh")
round = round or (mesh and mesh.MeshType == Enum.MeshType.Sphere)
local score = (if kick and kick:IsA("RemoteEvent") then 100 else 0)
+ (if name:find("ball", 1, true) then 50 else 0)
+ (if round then 25 else 0)
if score == 0 then
continue
end
if not bestScore or score > bestScore then
best, bestScore, tied = part, score, false
elseif score == bestScore and part.AssemblyRootPart ~= best.AssemblyRootPart then
tied = true
end
end
return if not tied then best else nil
end
local function soccerGoal(root, ball)
local best, bestScore
for _, object in ipairs(root:GetDescendants()) do
if not object:IsA("BasePart") or object == ball or object.AssemblyRootPart == ball.AssemblyRootPart then
continue
end
local name = questObjectName(object, root)
if not (name:find("goal", 1, true) or name:find("net", 1, true)) then
continue
end
local trigger = object:FindFirstChildOfClass("TouchTransmitter") ~= nil
if not trigger then
continue
end
local score = (if object.Name:lower():find("goal", 1, true) then 50 else 0)
+ (if object.Transparency > 0.8 then 20 else 0)
+ math.min(object.Size.Magnitude, 30)
if not bestScore or score > bestScore then
best, bestScore = object, score
end
end
if best then
return { Frame = best.CFrame, Part = best }
end
for _, object in ipairs(root:GetDescendants()) do
local name = object.Name:lower()
if object:IsA("Model") and (name == "goal" or name == "net" or name == "soccergoal") then
local frame = object:GetBoundingBox()
return { Frame = frame }
end
end
end
local function soccerTool(name)
local tool = findPath(player, "Backpack", name) or findPath(player.Character, name)
local remote = findPath(tool, "RF")
if not remote or not remote:IsA("RemoteFunction") then
error("Soccer needs " .. name, 0)
end
return remote
end
removeSoccerGlue = function(motion)
local glue = motion.Glue
if motion.GlueDeleting or not glue then
return
end
if not glue.Parent then
motion.Glue = nil
if state.PendingGlue == motion then
state.PendingGlue = nil
end
return
end
motion.GlueDeleting = true
state.PendingGlue = motion
state.GlueRequests += 1
task.spawn(function()
local problem
for _ = 1, 2 do
if glue.Parent ~= motion.GlueFolder then
break
end
local ok, failure = pcall(function()
motion.GlueDelete:InvokeServer(glue)
end)
if not ok then
problem = failure
end
local deadline = os.clock() + 5
while glue.Parent == motion.GlueFolder and os.clock() < deadline do
task.wait(0.1)
end
end
motion.GlueDeleting = false
state.GlueRequests -= 1
if glue.Parent ~= motion.GlueFolder then
motion.Glue = nil
if state.PendingGlue == motion then
state.PendingGlue = nil
end
else
warn(
"[BABFT] Delete the temporary Soccer Glue with DeleteTool before continuing: "
.. tostring(problem or "removal unconfirmed")
)
if state.Alive then
state.Status = "Delete temporary Soccer Glue to continue"
end
end
if state.Alive then
syncToggles()
end
end)
end
local function soccerGluePoint(plot, ball, size)
local center = plot.CFrame:PointToObjectSpace(ball.Position)
local margin = size.Magnitude / 2 + 0.1
local maxX, maxZ = plot.Size.X / 2 - margin, plot.Size.Z / 2 - margin
local minX = 62.6 + margin
if maxX < minX or maxZ <= 0 then
return nil
end
local position = plot.CFrame:PointToWorldSpace(
Vector3.new(math.clamp(center.X, minX, maxX), center.Y, math.clamp(center.Z, -maxZ, maxZ))
)
local relative = ball.CFrame:PointToObjectSpace(position)
local radii = ball.Size / 2
if (relative / radii).Magnitude > 0.95 then
return nil
end
return position
end
local function soccerConnected(glue, root, ball)
local part = glue and glue.Parent and glue.PrimaryPart
if not part then
return false
end
local characterConnected, ballConnected = false, false
for _, connected in ipairs(part:GetConnectedParts(true)) do
if connected == root then
characterConnected = true
end
if connected == ball then
ballConnected = true
end
end
return characterConnected and ballConnected
end
local function attachSoccerBall(job, ball)
if not sameCharacter(job) or not ball.Parent or ball.Anchored or activeQuest() ~= 8 then
return false
end
if job.GlueAttempted then
error("Soccer Glue attachment already attempted; restart the quest to retry", 0)
end
local plot = job.QuestZone
local folder = findPath(Workspace, "Blocks", player.Name)
local data = findPath(player, "Data", "Glue")
local used = findPath(data, "Used")
local template = findPath(game:GetService("ReplicatedStorage"), "BuildingParts", "Glue")
if not folder or not template or not template.PrimaryPart then
error("Glue building data is unavailable", 0)
end
if not data or data.Value - (if used then used.Value else 0) < 1 then
error("Soccer needs one available Glue block", 0)
end
if ball:HasTag("NoNewWelds") or job.Root:HasTag("NoNewWelds") then
error("Soccer attachment is blocked by NoNewWelds", 0)
end
local build, delete = soccerTool("BuildingTool"), soccerTool("DeleteTool")
local point = soccerGluePoint(plot, ball, template.PrimaryPart.Size)
if not point then
error("Move the ball into the Soccer building area before attaching Glue", 0)
end
if not state.Motion then
acquireMotion(job)
end
state.Status = "Soccer: placing Glue inside character and ball"
if not moveTo(job, CFrame.new(point), 2) or not pause(job, 0.5, true) then
return false
end
point = soccerGluePoint(plot, ball, template.PrimaryPart.Size)
if not point or (job.Root.Position - point).Magnitude > 1 then
error("Ball moved before Glue placement", 0)
end
local motion = state.Motion
local frame = CFrame.new(point)
local before = {}
for _, model in ipairs(folder:GetChildren()) do
before[model] = true
end
motion.GlueDelete, motion.GlueFolder = delete, folder
job.GlueAttempted = true
state.GlueRequests += 1
local ok, problem = pcall(function()
local sent, response = pcall(function()
return build:InvokeServer(
"Glue",
data.Value,
ball,
ball.CFrame:ToObjectSpace(frame),
false,
frame,
false,
false
)
end)
local deadline = os.clock() + 12
repeat
local match
for _, model in ipairs(folder:GetChildren()) do
if not before[model] and model:IsA("Model") and model.Name == "Glue" and model.PrimaryPart then
if model == response or (model.PrimaryPart.Position - point).Magnitude <= 3 then
if match then
error("Multiple new Glue blocks matched the request", 0)
end
match = model
end
end
end
if match then
motion.Glue = match
return
end
task.wait(0.1)
until os.clock() >= deadline
if not sent then
error(tostring(response), 0)
end
error("Server did not place Soccer Glue; no second block was requested", 0)
end)
state.GlueRequests -= 1
if state.Alive then
syncToggles()
end
if not sameCharacter(job) or state.Motion ~= motion then
removeSoccerGlue(motion)
return false
end
if not ok then
error(problem, 0)
end
local deadline = os.clock() + 3
while sameCharacter(job) and activeQuest() == 8 and os.clock() < deadline do
if soccerConnected(motion.Glue, job.Root, ball) then
break
end
task.wait(0.1)
end
if not sameCharacter(job) or state.Motion ~= motion then
return false
end
if not soccerConnected(motion.Glue, job.Root, ball) then
error("Glue appeared, but did not connect both the character and ball", 0)
end
motion.Ball, motion.BallCollision = ball, ball.CanCollide
motion.BallOffset = job.Root.Position - ball.Position
motion.BallLocal = job.Root.CFrame:PointToObjectSpace(ball.Position)
ball.CanCollide = false
local start = ball.Position
state.Status = "Soccer: checking Glue connection"
if not moveTo(job, CFrame.new(job.Root.Position + Vector3.new(0, 5, 0)), 1) then
if sameCharacter(job) and activeQuest() == 8 then
error("Soccer Glue lift failed", 0)
end
return false
end
if not pause(job, 0.5, true) then
return false
end
if not ball.Parent or ball.Position.Y < start.Y + 2 or not soccerConnected(motion.Glue, job.Root, ball) then
error("Glue connected, but the ball did not move with the character", 0)
end
return true
end
local function scoreSoccerGoal(job, root, ball)
local goal = soccerGoal(root, ball)
if not goal then
return false
end
state.Status = "Soccer: scoring a goal"
local kick = ball:FindFirstChild("KickRE")
if kick and kick:IsA("RemoteEvent") then
acquireMotion(job)
for _ = 1, 3 do
if not sameCharacter(job) or not ball:IsDescendantOf(root) or activeQuest() ~= 8 then
break
end
local delta = goal.Frame.Position - ball.Position
if delta.Magnitude < 0.1 then
break
end
local direction = delta.Unit
if not moveTo(job, CFrame.new(ball.Position - direction * 5), 1) then
break
end
if (job.Root.Position - ball.Position).Magnitude < 9 then
kick:FireServer((direction + Vector3.new(0, 0.15, 0)).Unit)
end
if waitQuestResult(job, 8, 2) then
return true
end
end
end
if not sameCharacter(job) or activeQuest() ~= 8 then
return false
end
if not attachSoccerBall(job, ball) then
return false
end
local motion = state.Motion
local delta = goal.Frame.Position - ball.Position
local direction = if delta.Magnitude > 0.1 then delta.Unit else goal.Frame.LookVector
local target = goal.Frame.Position + motion.BallOffset
if not moveTo(job, CFrame.new(target), math.max(1, delta.Magnitude / 70)) then
return questCompleted(8)
end
if not pause(job, 0.5, true) then
return false
end
if
goal.Part
and goal.Part.Parent
and (ball.Position - goal.Part.Position).Magnitude < goal.Part.Size.Magnitude / 2 + ball.Size.Magnitude / 2
then
pulseTouch(ball, goal.Part)
end
if waitQuestResult(job, 8, 2) then
return true
end
moveTo(job, CFrame.new(target + direction * (ball.Size.Magnitude / 2 + 6)), 1)
return waitQuestResult(job, 8, 2)
end
local function soccerToChest(job, ball)
state.Status = "Soccer: carrying ball to the chest"
if not state.Motion or state.Motion.Ball ~= ball then
if not attachSoccerBall(job, ball) then
error("Soccer ball attachment failed", 0)
end
end
local motion = state.Motion
local gates, trigger = getRoute()
local height = math.max(cfg.Height, ball.Size.Y / 2 + 35) + motion.BallOffset.Y
if not moveTo(job, CFrame.new(job.Root.Position.X, height, job.Root.Position.Z), 2) then
return false
end
for index, gate in ipairs(gates) do
state.Status = string.format("Soccer: carrying ball %d / %d", index, #gates)
local target = Vector3.new(gate.Position.X, height, gate.Position.Z)
if not moveTo(job, CFrame.new(target), (target - job.Root.Position).Magnitude / 100) then
return questCompleted(8)
end
end
local target = trigger.Position + motion.BallOffset
if not moveTo(job, CFrame.new(target), (target - job.Root.Position).Magnitude / 100) then
return questCompleted(8)
end
if not pause(job, 1, true) then
return false
end
if (ball.Position - trigger.Position).Magnitude > 6 then
return false
end
pulseTouch(ball, trigger)
if waitQuestResult(job, 8, 2) then
return true
end
if not sameCharacter(job) or activeQuest() ~= 8 then
return false
end
removeSoccerGlue(motion)
while current(job) and state.GlueRequests > 0 do
task.wait(0.1)
end
if not sameCharacter(job) then
return false
end
for _, connected in ipairs(job.Root:GetConnectedParts(true)) do
if connected == ball then
error("Ball is still attached after Glue removal", 0)
end
end
ball.CanCollide = motion.BallCollision
motion.Ball = nil
if not state.ChestCharacters[job.Character] then
state.ChestCharacters[job.Character] = true
if moveTo(job, trigger.CFrame, 1) and sameCharacter(job) then
pulseTouch(job.Root, trigger)
end
end
return waitQuestResult(job, 8, 8)
end
local function assistSoccer(job)
if not waitForCharacter(job, nil, 15) then
error("Character unavailable", 0)
end
local root, ball
local deadline = os.clock() + 8
repeat
if not sameCharacter(job) or activeQuest() ~= 8 then
return
end
root = findPath(job.QuestZone, "Quest")
ball = root and soccerBall(root)
if ball then
break
end
task.wait(0.25)
until os.clock() >= deadline
if not ball then
error("No unambiguous movable quest ball found", 0)
end
local mode = cfg.SoccerMode
local completed = false
if mode ~= "End chest" then
completed = scoreSoccerGoal(job, root, ball)
end
if not completed and mode ~= "Goal" and sameCharacter(job) and activeQuest() == 8 and ball:IsDescendantOf(root) then
completed = soccerToChest(job, ball)
end
releaseMotion(job)
if not current(job) then
return
end
if not completed and not questCompleted(8) then
error("Soccer route ended without a completion marker", 0)
end
state.Status = "Soccer completed"
end
local function autoQuests(job)
if not teamLeader() then
error("Only your team leader can start quests", 0)
end
local plot = zone()
local supported = { [1] = true, [2] = true, [4] = type(fireclickdetector) == "function", [8] = true, [9] = true }
local queue, completed, skipped = {}, 0, 0
local active = activeQuest()
if active ~= 0 then
if not supported[active] then
state.Status = "Finish the active quest first"
return
end
table.insert(queue, active)
end
for _, id in ipairs({ 1, 2, 4, 8, 9 }) do
if id ~= active and not questCompleted(id) then
table.insert(queue, id)
end
end
for _, id in ipairs(queue) do
if not current(job) or zone() ~= plot or not teamLeader() then
return
end
if not supported[id] or not questAvailable(id) then
skipped += 1
continue
end
job.QuestId, job.QuestZone = id, plot
local owned = activeQuest() == id
local ok, problem = pcall(function()
if not owned then
if not pause(job, math.max(0, state.QuestCooldown - os.clock())) then
return
end
requestQuest(id)
owned = true
awaitQuest(job, id)
end
if not questCompleted(id) then
if id == 9 then
if not waitForCharacter(job, nil, 45) then
error("Character unavailable", 0)
end
if state.ChestCharacters[job.Character] then
local previous = job.Character
job.Humanoid.Health = 0
if not waitForCharacter(job, previous, 45) then
error("Respawn timed out", 0)
end
end
runTrip(job)
elseif id == 8 then
assistSoccer(job)
else
assistQuest(job, id)
end
end
end)
releaseMotion(job)
while current(job) and state.GlueRequests > 0 do
task.wait(0.1)
end
if current(job) and state.PendingGlue then
error("Delete temporary Soccer Glue before continuing quests", 0)
end
if not current(job) or zone() ~= plot or not teamLeader() then
return
end
if not questCompleted(id) and owned then
if not pause(job, 2) then
return
end
end
if questCompleted(id) then
completed += 1
else
skipped += 1
if not ok then
warn("[BABFT] Quest " .. id .. ": " .. tostring(problem))
end
end
if activeQuest() ~= 0 then
if not owned or activeQuest() ~= id then
return
end
if not pause(job, math.max(0, state.QuestCooldown - os.clock())) then
return
end
requestQuest(0)
awaitQuest(job, 0)
end
if not pause(job, 2) then
return
end
end
state.Status = string.format("Quests: %d completed, %d skipped", completed, skipped)
end
local function beginJob(kind, callback)
if not state.Alive then
error("Controller unloaded", 0)
end
if state.Job or requestsPending() then
error("Stop the current activity and wait for its pending build request", 0)
end
local job = { Kind = kind }
state.Job = job
syncToggles()
job.Thread = task.defer(function()
local ok, problem = xpcall(callback, debug.traceback, job)
if current(job) then
releaseMotion(job)
state.Job = nil
cfg.Farm = false
syncToggles()
if not ok then
warn("[BABFT] " .. tostring(problem))
state.Status = "Stopped"
end
end
end)
end
local function guarded(callback)
return function(...)
if not state.Alive then
return
end
local ok, problem = xpcall(callback, debug.traceback, ...)
if not ok then
warn("[BABFT] " .. tostring(problem))
end
end
end
local dashboardCore = {}
local function createDashboard()
local gui = Instance.new("ScreenGui")
ui.Dashboard = gui
gui.Name = "BABFTFarmMonitor"
gui.ResetOnSpawn = false
gui.IgnoreGuiInset = true
gui.DisplayOrder = 2147483645
gui.Enabled = false
gui.Parent = playerGui
local backdrop = Instance.new("Frame")
backdrop.Size = UDim2.fromScale(1, 1)
backdrop.BackgroundColor3 = Color3.new(0, 0, 0)
backdrop.BorderSizePixel = 0
backdrop.Parent = gui
local stats = Instance.new("Frame")
stats.AnchorPoint = Vector2.new(0, 1)
stats.Position = UDim2.new(0, 24, 1, -24)
stats.Size = UDim2.new(1, -48, 0, 174)
stats.BackgroundTransparency = 1
stats.Parent = backdrop
local function label(y, height, size, muted)
local object = Instance.new("TextLabel")
object.Position = UDim2.fromOffset(0, y)
object.Size = UDim2.new(1, 0, 0, height)
object.BackgroundTransparency = 1
object.Font = Enum.Font.Gotham
object.TextColor3 = if muted then Color3.fromRGB(130, 130, 130) else Color3.fromRGB(235, 235, 235)
object.Text = ""
object.TextSize = size
object.TextXAlignment = Enum.TextXAlignment.Left
object.TextYAlignment = Enum.TextYAlignment.Top
object.Parent = stats
return object
end
ui.DashboardStatus = label(0, 25, 16, false)
ui.DashboardTime = label(29, 20, 12, true)
ui.DashboardRates = label(59, 66, 15, false)
ui.DashboardTotals = label(137, 34, 12, true)
table.insert(
state.Connections,
UserInputService.InputBegan:Connect(function(input, processed)
if input.KeyCode == Enum.KeyCode.F8 and not processed then
setDashboard(not cfg.Dashboard)
end
end)
)
end
setDashboard = function(enabled)
cfg.Dashboard = enabled
if ui.Dashboard then
ui.Dashboard.Enabled = enabled
end
if enabled then
for _, kind in ipairs(Enum.CoreGuiType:GetEnumItems()) do
if kind ~= Enum.CoreGuiType.All and dashboardCore[kind] == nil then
local ok, value = pcall(StarterGui.GetCoreGuiEnabled, StarterGui, kind)
if ok then
dashboardCore[kind] = value
pcall(StarterGui.SetCoreGuiEnabled, StarterGui, kind, false)
end
end
end
else
for kind, value in pairs(dashboardCore) do
pcall(StarterGui.SetCoreGuiEnabled, StarterGui, kind, value)
end
table.clear(dashboardCore)
end
if ui.DashboardToggle then
ui.DashboardToggle:Set(enabled, true)
end
end
updateDashboard = function()
local elapsed = math.max(1, os.clock() - state.Started)
if ui.DashboardStatus then
ui.DashboardStatus.Text = state.Status
ui.DashboardTime.Text = string.format(
"%02d:%02d:%02d",
math.floor(elapsed / 3600),
math.floor(elapsed / 60) % 60,
math.floor(elapsed) % 60
)
ui.DashboardRates.Text = string.format(
"%.1f gold / min\n%.0f gold / hour\n%.1f gold blocks / hour",
state.GoldEarned * 60 / elapsed,
state.GoldEarned * 3600 / elapsed,
state.BlocksEarned * 3600 / elapsed
)
ui.DashboardTotals.Text = string.format(
"+%d gold / +%d gold blocks\n%d runs / %d interrupted",
state.GoldEarned,
state.BlocksEarned,
state.Arrivals,
state.Deaths
)
end
end
local function buildUi()
library = loadstring(game:HttpGet(UI_URL))()
window = library.new({
Name = "BABFT",
Title = "BABFT",
Parent = playerGui,
Personal = false,
AutoInitialize = false,
AutoSave = false,
LoadSettings = false,
})
ui.Window = window.Gui
ui.Window.DisplayOrder = 2147483646
window.Overview.Page:Destroy()
window.Overview = nil
local main = window:Page({ Name = "Main", Icon = "home" })
local section = main:Card({ Title = "Farm", Collapsible = false })
ui.Farm = section:Toggle({
Name = "Autofarm",
Persist = false,
Callback = guarded(function(enabled)
if syncingToggle then
return
end
if not enabled then
if state.Job and state.Job.Kind == "Farm" then
stopJob("Stopped")
end
return
end
if state.Job or requestsPending() or activeQuest() ~= 0 then
syncToggles()
return
end
cfg.Farm = true
beginJob("Farm", farm)
end),
})
ui.DashboardToggle = section:Toggle({
Name = "Farm monitor",
Persist = false,
Callback = guarded(function(enabled)
if not syncingToggle then
setDashboard(enabled)
end
end),
})
ui.Quests = section:Toggle({
Name = "Auto complete quests",
Persist = false,
Callback = guarded(function(enabled)
if syncingToggle then
return
end
if not enabled then
if state.Job and state.Job.Kind == "Quest" then
stopJob("Stopped")
end
return
end
if state.Job or requestsPending() then
syncToggles()
return
end
beginJob("Quest", autoQuests)
end),
})
section:Dropdown({
Name = "Soccer route",
Options = { "Goal then chest", "Goal", "End chest" },
Default = cfg.SoccerMode,
Persist = false,
Callback = function(value)
cfg.SoccerMode = value
end,
})
ui.Activity = section:Label({ Name = "Idle", RawText = true })
createDashboard()
buildImporterUi(window)
main:Select()
table.insert(
state.Connections,
ui.Window.Destroying:Connect(function()
state.Alive = false
stopJob()
setDashboard(false)
if ui.Dashboard then
ui.Dashboard:Destroy()
end
if ui.Browser then
ui.Browser:Destroy()
end
if clearPreview then
clearPreview()
end
for _, connection in ipairs(state.Connections) do
connection:Disconnect()
end
for _, entry in pairs(state.ValueConnections) do
entry.Connection:Disconnect()
end
table.clear(state.Connections)
table.clear(state.ValueConnections)
end)
)
end
local importer = {
Path = "",
Rows = {},
Created = {},
Declared = {},
Delay = 0.12,
Pending = nil,
Origin = nil,
Plot = nil,
}
local function buildStatus(text)
if state.Alive then
state.Status = text
end
end
local function finite(value)
return type(value) == "number" and value == value and math.abs(value) < math.huge
end
local function buildVector(value, label)
if type(value) ~= "string" then
error(label .. " must be a comma-separated triple", 0)
end
local components = string.split(value, ",")
if #components ~= 3 then
error(label .. " must contain three numbers", 0)
end
for index, component in ipairs(components) do
components[index] = tonumber(component)
if not finite(components[index]) then
error(label .. " contains an invalid number", 0)
end
end
return Vector3.new(components[1], components[2], components[3])
end
local function buildArray(value, label)
if type(value) ~= "table" then
error(label .. " must be an array", 0)
end
local count = 0
for key in pairs(value) do
if not finite(key) or key % 1 ~= 0 or key < 1 or key > #value then
error(label .. " is not a dense array", 0)
end
count += 1
end
if count ~= #value then
error(label .. " has missing entries", 0)
end
end
local function editableBuild()
if requestsPending() or state.Job then
error("Stop the current activity before changing the build plan", 0)
end
if importer.Pending or next(importer.Created) then
error("This import has placed or pending blocks; resume it before changing the plan", 0)
end
importer.Origin, importer.Plot = nil, nil
end
local function buildTool(name, remoteName)
local tool = findPath(player, "Backpack", name) or findPath(player.Character, name)
local remote = findPath(tool, remoteName or "RF")
if not remote or not remote:IsA("RemoteFunction") then
error(name .. " is required for this import", 0)
end
return remote
end
local function buildProperties(record, material)
local properties = {}
for key, name in pairs({ ss = "Cast shadow", cc = "Collision" }) do
if record[key] ~= nil then
if type(record[key]) ~= "boolean" then
error(material .. ": invalid " .. key, 0)
end
properties[name] = record[key]
end
end
if record.a ~= nil and type(record.a) ~= "boolean" then
error(material .. ": invalid anchored flag", 0)
end
if record.tr ~= nil then
if not finite(record.tr) or record.tr < 0 or record.tr > 1 then
error(material .. ": invalid transparency", 0)
end
properties.Transparency = record.tr
end
local unsupported = 0
local numberNames = {
Piston = { ExtendLength = "Piston length", Speed = "Piston speed" },
Servo = { TargetAngle = "Servo angle" },
Motor = { MaxSpeed = "Wheel speed" },
JetTurbine = { MaxSpeed = "Jet speed" },
}
local booleanNames = { ReverseRotation = "Reverse rotation", ShowCrosshairs = "Crosshairs" }
for _, field in ipairs({ "nv", "bv" }) do
if record[field] ~= nil and type(record[field]) ~= "table" then
error(material .. ": invalid " .. field, 0)
end
for key, value in pairs(record[field] or {}) do
if (field == "nv" and not finite(value)) or (field == "bv" and type(value) ~= "boolean") then
error(material .. ": invalid property " .. tostring(key), 0)
end
local name = if field == "nv" then (numberNames[material] or {})[key] else booleanNames[key]
if name then
properties[name] = value
else
unsupported += 1
end
end
end
if record.c ~= nil then
if type(record.c) ~= "table" then
error(material .. ": invalid constraint settings", 0)
end
for key, values in pairs(record.c) do
if key == "hg" and (material == "Servo" or material == "Motor") then
buildArray(values, "Hinge settings")
if #values ~= 3 then
error("Hinge settings must have three values", 0)
end
for _, value in ipairs(values) do
if not finite(value) then
error("Invalid hinge value", 0)
end
end
local torque = values[if material == "Servo" then 2 else 1]
if torque < 1e6 or torque > 1e10 then
error("Unsupported hinge torque", 0)
end
local level = math.log10(torque / 1e6) + 1
if math.abs(level - math.round(level)) > 0.001 then
error("Hinge torque is not a supported level", 0)
end
properties[if material == "Servo" then "Servo torque" else "Wheel torque"] = math.round(level)
if material == "Servo" then
properties["Servo speed"] = values[3]
end
else
unsupported += 1
end
end
end
if material == "JetTurbine" and record.nv and record.nv.MaxForce then
local level = math.log10(record.nv.MaxForce / 1e6) + 1
if not finite(level) or level < 1 or level > 5 or math.abs(level - math.round(level)) > 0.001 then
error("Jet force is not a supported level", 0)
end
properties["Jet force"] = math.round(level)
unsupported -= 1
end
return properties, unsupported
end
local function decodeBuild(source)
if #source > 20 * 1024 * 1024 then
error("Build file exceeds 20 MB", 0)
end
source = source:gsub("^\239\187\191", "")
local body = source
local declared = {}
if not source:match("^%s*{") then
if not source:match("^BuilderHub_") or not source:find("\nBH2", 1, true) then
error("Unsupported build format", 0)
end
body = source:match("\nBuild%s*=%s*({.*})%s*$")
if not body then
error("BH2 Build section is missing", 0)
end
local header = source:match("\nBlocksNeeded%s*=%s*([^\r\n]+)")
if header then
local decoded = HttpService:JSONDecode(header)
if type(decoded) ~= "table" or type(decoded.Blocks) ~= "table" then
error("Invalid BlocksNeeded section", 0)
end
declared = decoded.Blocks
end
end
local data = HttpService:JSONDecode(body)
if type(data) ~= "table" or (data.v ~= 2 and data.v ~= 3) or type(data.b) ~= "table" then
error("Only BH2 version 2 or 3 is supported", 0)
end
return data, declared
end
local function loadBuild()
editableBuild()
if importer.Path == "" then
error("Select a build first", 0)
end
local data, declared = decodeBuild(readfile(importer.Path))
local templates = findPath(game:GetService("ReplicatedStorage"), "BuildingParts")
if not templates then
error("Building templates are unavailable", 0)
end
local rows, ids, unresolved, unsupported = {}, {}, 0, 0
for material, records in pairs(data.b) do
if type(material) ~= "string" then
error("Invalid material name", 0)
end
local template = templates:FindFirstChild(material)
if not template or not template:IsA("Model") or not template.PrimaryPart then
error("Unknown material: " .. material, 0)
end
if
material == "Rope"
or material == "Bar"
or material == "Spring"
or material == "Cut"
or material:find("Tool")
then
error("Unsupported placement type: " .. material, 0)
end
buildArray(records, material)
for _, record in ipairs(records) do
if
type(record) ~= "table"
or not finite(record.i)
or record.i % 1 ~= 0
or record.i < 1
or ids[record.i]
then
error("Missing or duplicate block ID in " .. material, 0)
end
ids[record.i] = true
local position = buildVector(record.p, material .. " position")
local rotation = buildVector(record.r, material .. " rotation")
if position.Magnitude > 10000 or rotation.Magnitude > 1000000 then
error("Build coordinates exceed supported bounds", 0)
end
local size = if record.sz then buildVector(record.sz, material .. " size") else template.PrimaryPart.Size
if math.min(size.X, size.Y, size.Z) < 0.0499 or math.max(size.X, size.Y, size.Z) > 2048 then
error("Invalid block size", 0)
end
if record.sz and not material:find("Block", 1, true) then
error("Cannot resize " .. material, 0)
end
local color
if record.cl then
local rgb = buildVector(record.cl, material .. " color")
if math.min(rgb.X, rgb.Y, rgb.Z) < 0 or math.max(rgb.X, rgb.Y, rgb.Z) > 1 then
error("Invalid block color", 0)
end
color = Color3.new(rgb.X, rgb.Y, rgb.Z)
end
if record.tx ~= nil and (material ~= "Sign" or type(record.tx) ~= "string") then
error("Invalid sign text", 0)
end
if record.bd ~= nil then
buildArray(record.bd, "Bindings")
unresolved += #record.bd
end
local properties, omitted = buildProperties(record, material)
unsupported += omitted
local baseSize = template.PrimaryPart.Size
local cost = math.max(1, size.X * size.Y * size.Z / (baseSize.X * baseSize.Y * baseSize.Z))
table.insert(rows, {
Id = record.i,
Material = material,
Size = size,
Color = color,
Properties = properties,
Text = record.tx,
Cost = cost,
BaseVolume = baseSize.X * baseSize.Y * baseSize.Z,
Scaled = record.sz ~= nil,
Frame = CFrame.new(position)
* CFrame.Angles(math.rad(rotation.X), math.rad(rotation.Y), math.rad(rotation.Z)),
})
if #rows > 15000 then
error("Build exceeds 15,000 records", 0)
end
end
end
if #rows == 0 then
error("Build contains no blocks", 0)
end
for material, amount in pairs(declared) do
if
type(material) ~= "string"
or not data.b[material]
or not finite(amount)
or amount < 0
or amount % 1 ~= 0
then
error("Invalid declared inventory requirement", 0)
end
end
table.sort(rows, function(a, b)
return a.Id < b.Id
end)
importer.Rows, importer.Declared = rows, declared
importer.Unresolved = unresolved
buildStatus(string.format("Loaded %d blocks | %d unresolved bindings", #rows, unresolved))
print(
string.format(
"[BABFT Builder] %s: %d blocks, XYZ Euler degrees, plot-relative placement; all imported blocks remain anchored",
importer.Path,
#rows
)
)
if unresolved > 0 or unsupported > 0 then
warn(
string.format(
"[BABFT Builder] %d binding links and %d unsupported settings will not be restored; this import does not reproduce working vehicle controls",
unresolved,
unsupported
)
)
end
end
local function buildOrigin()
if #importer.Rows == 0 then
error("Load a build file first", 0)
end
local plot = zone()
if not plot or not plot:IsA("BasePart") then
error("Your building plot is unavailable", 0)
end
if importer.Plot and importer.Plot ~= plot then
error("The import belongs to a different plot", 0)
end
return importer.Origin or plot.CFrame, plot
end
local function buildInventory()
local needs, committed = {}, {}
for _, row in ipairs(importer.Rows) do
local entry = importer.Created[row.Id]
local part = entry and entry.Model.Parent and entry.Model.PrimaryPart
local consumed = if part then math.max(1, part.Size.X * part.Size.Y * part.Size.Z / row.BaseVolume) else 0
committed[row.Material] = (committed[row.Material] or 0) + consumed
needs[row.Material] = (needs[row.Material] or 0) + math.max(0, row.Cost - consumed)
end
for material, amount in pairs(importer.Declared) do
needs[material] = math.max(needs[material] or 0, amount - (committed[material] or 0))
end
local missing = {}
for material, amount in pairs(needs) do
local data = findPath(player, "Data", material)
local used = findPath(data, "Used")
local available = if data and finite(data.Value)
then data.Value - (if used and finite(used.Value) then used.Value else 0)
else 0
if available + 1e-4 < amount then
table.insert(missing, material .. " (-" .. math.ceil(amount - available) .. ")")
end
end
table.sort(missing)
return missing
end
local function validateBuildBounds(origin, plot)
for _, row in ipairs(importer.Rows) do
local relative = plot.CFrame:ToObjectSpace(origin * row.Frame)
for _, x in ipairs({ -1, 1 }) do
for _, y in ipairs({ -1, 1 }) do
for _, z in ipairs({ -1, 1 }) do
local point = relative * (row.Size * Vector3.new(x, y, z) / 2)
if math.abs(point.X) >= plot.Size.X / 2 or math.abs(point.Z) >= plot.Size.Z / 2 then
error("Block " .. row.Id .. " extends outside your plot; the model is too large", 0)
end
end
end
end
end
end
local function closeBuildFrame(actual, expected)
return (actual.Position - expected.Position).Magnitude <= 0.1
and actual.RightVector:Dot(expected.RightVector) >= 0.999
and actual.UpVector:Dot(expected.UpVector) >= 0.999
end
local function reconcileBuildPlacement()
local pending = importer.Pending
if not pending then
return
end
local deadline = os.clock() + 12
repeat
local match
for _, model in ipairs(pending.Folder:GetChildren()) do
if
not pending.Before[model]
and model:IsA("Model")
and model.Name == pending.Row.Material
and model.PrimaryPart
and closeBuildFrame(model.PrimaryPart.CFrame, pending.Frame)
then
if match then
error(
"Multiple new blocks match pending ID " .. pending.Row.Id .. "; placement cannot be attributed",
0
)
end
match = model
end
end
if match then
importer.Created[pending.Row.Id] = { Model = match, Done = false }
importer.Pending = nil
return
end
task.wait(0.15)
until os.clock() >= deadline
error(
"Placement #" .. pending.Row.Id .. " is unconfirmed; resume only retries observation, never places it twice",
0
)
end
local function buildCurrent(job)
if not current(job) then
return false
end
if zone() ~= importer.Plot or activeQuest() ~= 0 then
error("Plot or quest changed during import", 0)
end
local launched = findPath(importer.Plot, "Launched")
if launched and launched.Value then
error("The plot launched during import", 0)
end
return true
end
local function waitBuildValue(job, predicate)
local deadline = os.clock() + 3
repeat
if not buildCurrent(job) then
return false
end
if predicate() then
return true
end
task.wait(0.1)
until os.clock() >= deadline
error("The server did not confirm an imported block setting", 0)
end
local function setBuildProperty(job, model, descriptor, desired, remote)
local function equal(value)
return if type(desired) == "number"
then type(value) == "number" and math.abs(value - desired) < 0.001
else value == desired
end
for _ = 1, 5 do
if not buildCurrent(job) then
return false
end
local previous = descriptor.GetState(model)
if equal(previous) then
return true
end
if descriptor.ValueType == "number+" then
local text = if descriptor.Name == "Transparency" then tostring(desired * 100) .. "%" else tostring(desired)
remote:InvokeServer(descriptor.Name, { model }, text)
else
remote:InvokeServer(descriptor.Name, { model })
end
if not waitBuildValue(job, function()
return descriptor.GetState(model) ~= previous
end) then
return false
end
end
if not equal(descriptor.GetState(model)) then
error("Cannot restore property " .. descriptor.Name, 0)
end
return true
end
local function applyBuildRow(job, row, entry, tools, descriptors)
local model, frame = entry.Model, importer.Origin * row.Frame
if not model.Parent or not model.PrimaryPart then
error("Imported block #" .. row.Id .. " was removed", 0)
end
if not buildCurrent(job) then
return false
end
if not model.PrimaryPart.Anchored then
error("Imported blocks must stay anchored while resuming", 0)
end
if
row.Scaled
and (
(model.PrimaryPart.Size - row.Size).Magnitude > 0.015
or not closeBuildFrame(model.PrimaryPart.CFrame, frame)
)
then
tools.Scale:InvokeServer(model, row.Size, frame)
if
not waitBuildValue(job, function()
return model.PrimaryPart
and (model.PrimaryPart.Size - row.Size).Magnitude <= 0.015
and closeBuildFrame(model.PrimaryPart.CFrame, frame)
end)
then
return false
end
end
if not closeBuildFrame(model.PrimaryPart.CFrame, frame) then
error("Imported block #" .. row.Id .. " moved; stop editing while importing", 0)
end
if
row.Color
and (
Vector3.new(model.PrimaryPart.Color.R, model.PrimaryPart.Color.G, model.PrimaryPart.Color.B)
- Vector3.new(row.Color.R, row.Color.G, row.Color.B)
).Magnitude
> 0.008
then
if not buildCurrent(job) then
return false
end
tools.Paint:InvokeServer({ { model.PrimaryPart, row.Color } })
if
not waitBuildValue(job, function()
local color = model.PrimaryPart.Color
return math.abs(color.R - row.Color.R)
+ math.abs(color.G - row.Color.G)
+ math.abs(color.B - row.Color.B)
< 0.015
end)
then
return false
end
end
for name, value in pairs(row.Properties) do
local descriptor = (descriptors[row.Material] or {})[name] or descriptors.All[name]
if not setBuildProperty(job, model, descriptor, value, tools.Properties) then
return false
end
end
if row.Text and not entry.TextSent then
if not buildCurrent(job) then
return false
end
local remote = findPath(model, "ClickDetector", "Script", "UpdateSignRE")
if not remote or not remote:IsA("RemoteEvent") then
error("Sign text remote is unavailable", 0)
end
remote:FireServer(row.Text)
entry.TextSent = true
end
return buildCurrent(job)
end
local function runBuild(job)
if importer.Pending then
reconcileBuildPlacement()
end
if not current(job) then
return
end
local origin, plot = buildOrigin()
if activeQuest() ~= 0 then
error("Finish or cancel the active quest before importing", 0)
end
local launched = findPath(plot, "Launched")
if launched and launched.Value then
error("Return to an unlaunched plot before importing", 0)
end
local folder = findPath(Workspace, "Blocks", player.Name)
if not folder then
error("Your block folder is unavailable", 0)
end
local missing = buildInventory()
if #missing > 0 then
error("Missing inventory: " .. table.concat(missing, ", "), 0)
end
validateBuildBounds(origin, plot)
local tools = { Build = buildTool("BuildingTool") }
local module = findPath(game:GetService("ReplicatedStorage"), "Scripts", "BlockProperties")
if not module then
error("Block property definitions are unavailable", 0)
end
local descriptors = require(module)
for _, row in ipairs(importer.Rows) do
if row.Scaled then
tools.Scale = tools.Scale or buildTool("ScalingTool")
end
if row.Color then
tools.Paint = tools.Paint or buildTool("PaintingTool")
end
if next(row.Properties) then
tools.Properties = tools.Properties or buildTool("PropertiesTool", "SetPropertieRF")
end
for name, value in pairs(row.Properties) do
local descriptor = (descriptors[row.Material] or {})[name] or descriptors.All[name]
if not descriptor or type(descriptor.GetState) ~= "function" then
error(row.Material .. " does not support " .. name, 0)
end
if
type(value) == "number"
and ((descriptor.Min and value < descriptor.Min) or (descriptor.Max and value > descriptor.Max))
then
error(row.Material .. " has an unsupported " .. name .. " value", 0)
end
end
end
importer.Origin, importer.Plot = origin, plot
for index, row in ipairs(importer.Rows) do
if not buildCurrent(job) then
return
end
local entry = importer.Created[row.Id]
if entry and (entry.Model.Parent ~= folder or not entry.Model.PrimaryPart) then
error("Imported block #" .. row.Id .. " is missing", 0)
end
if
entry
and entry.Done
and (
not entry.Model.PrimaryPart.Anchored
or not closeBuildFrame(entry.Model.PrimaryPart.CFrame, origin * row.Frame)
)
then
error("Imported block #" .. row.Id .. " changed after import; keep the build anchored and in place", 0)
end
if not entry then
local data = findPath(player, "Data", row.Material)
if not data or not finite(data.Value) then
error("Inventory changed before placement", 0)
end
local before = {}
for _, model in ipairs(folder:GetChildren()) do
before[model] = true
end
local frame = origin * row.Frame
importer.Pending = { Row = row, Before = before, Folder = folder, Frame = frame }
buildStatus(string.format("Placing %d/%d: %s", index, #importer.Rows, row.Material))
local ok, problem = pcall(function()
tools.Build:InvokeServer(
row.Material,
data.Value,
plot,
plot.CFrame:ToObjectSpace(frame),
true,
frame,
false,
false
)
end)
reconcileBuildPlacement()
if not ok then
warn(
"[BABFT Builder] Placement response failed, but a matching block was observed: "
.. tostring(problem)
)
end
entry = importer.Created[row.Id]
end
if not entry.Done then
if not applyBuildRow(job, row, entry, tools, descriptors) then
return
end
entry.Done = true
end
buildStatus(
string.format(
"Imported %d/%d | anchored | %d unbound links",
index,
#importer.Rows,
importer.Unresolved or 0
)
)
if not pause(job, importer.Delay) then
return
end
end
buildStatus(
string.format(
"Geometry/settings complete: %d blocks | %d unbound links",
#importer.Rows,
importer.Unresolved or 0
)
)
end
local browser = {
Mode = "Workspace",
Query = "",
Files = {},
Results = {},
Metadata = {},
Page = 1,
Generation = 0,
Render = 0,
Selecting = false,
}
local function basename(path)
return path:gsub("\\", "/"):match("([^/]+)$") or path
end
local function cacheKey(path)
local hash = 5381
for index = 1, #path do
hash = (hash * 33 + string.byte(path, index)) % 4294967296
end
return string.format("%08x", hash)
end
local function buildRequest(endpoint)
local send = request or http_request
if type(send) ~= "function" then
error("HTTP requests are unavailable", 0)
end
local response = send({
Url = "https://chatgptqq.com/api/builds" .. endpoint,
Method = "GET",
Headers = { ["ngrok-skip-browser-warning"] = "true", Referer = "https://babft.com/", Accept = "*/*" },
})
if type(response) ~= "table" or type(response.Body) ~= "string" then
error("Empty catalog response", 0)
end
local status = tonumber(response.StatusCode)
if not status or status < 200 or status >= 300 then
error("Catalog HTTP " .. tostring(status), 0)
end
if #response.Body > 20 * 1024 * 1024 then
error("Catalog response is too large", 0)
end
return response.Body
end
local function storageFolders()
if type(makefolder) ~= "function" or type(isfolder) ~= "function" or type(writefile) ~= "function" then
error("Workspace writing is unavailable", 0)
end
for _, path in ipairs({ "BABFT", "BABFT/Downloads", "BABFT/Images" }) do
if not isfolder(path) then
makefolder(path)
end
end
end
local function buildInfo(source)
local data, declared = decodeBuild(source)
local count, links, materialCount = 0, 0, 0
local materials = {}
for material, records in pairs(data.b) do
if type(material) ~= "string" then
error("Invalid material name", 0)
end
buildArray(records, material)
materialCount += 1
for _, record in ipairs(records) do
if type(record) ~= "table" then
error("Invalid block record", 0)
end
count += 1
if count > 15000 then
error("Build exceeds 15,000 records", 0)
end
if type(record.bd) == "table" then
links += #record.bd
end
end
local amount = declared[material]
materials[material] = if finite(amount) and amount >= 0 then amount else #records
end
local list = {}
for material, amount in pairs(materials) do
table.insert(list, material .. ": " .. tostring(amount))
end
table.sort(list)
return {
Count = count,
Summary = string.format(
"BH2 v%d | %d parts | %d materials | %.1f KB",
data.v,
count,
materialCount,
#source / 1024
),
Details = table.concat(list, "\n")
.. (if links > 0 then "\n\n" .. links .. " binding links (not restored)" else ""),
}
end
clearPreview = function()
if state.BuildPreview then
state.BuildPreview:Destroy()
state.BuildPreview = nil
end
end
local function previewBuild()
if state.Job or requestsPending() or browser.Selecting then
return
end
if #importer.Rows == 0 then
loadBuild()
end
local origin = buildOrigin()
clearPreview()
local model = Instance.new("Model")
model.Name = "BABFTPreview"
state.BuildPreview = model
model.Parent = Workspace
for index, row in ipairs(importer.Rows) do
if not state.Alive or state.BuildPreview ~= model then
return
end
local part = Instance.new("Part")
part.Name = row.Material
part.Anchored, part.CanCollide, part.CanTouch, part.CanQuery = true, false, false, false
part.CastShadow = false
part.Size, part.CFrame = row.Size, origin * row.Frame
part.Color = row.Color or Color3.fromRGB(72, 187, 200)
part.Transparency = 0.45
part.Parent = model
if index % 100 == 0 then
task.wait()
end
end
end
local function selectedInfo(info)
ui.Selected:Set(basename(importer.Path))
ui.BuildInfo:Set(info.Summary)
ui.Materials.Text = info.Details
end
local function selectBuild(path)
if state.Job or requestsPending() or importer.Pending then
return false
end
local info = buildInfo(readfile(path))
if path ~= importer.Path then
clearPreview()
importer.Rows, importer.Created, importer.Declared = {}, {}, {}
importer.Origin, importer.Plot, importer.Unresolved = nil, nil, nil
end
importer.Path = path
browser.Metadata[path] = info
selectedInfo(info)
if ui.Browser then
ui.Browser.Enabled = false
browser.Render += 1
end
return true
end
local renderBrowser, refreshBrowser
local function browserStatus(text)
if state.Alive and ui.BrowserStatus then
ui.BrowserStatus.Text = text
end
end
local function scanWorkspace(generation)
if type(listfiles) ~= "function" or type(isfolder) ~= "function" or type(readfile) ~= "function" then
error("Workspace scanning needs listfiles, isfolder and readfile", 0)
end
local folders, visited, files = { "." }, {}, {}
local index, seen, failed = 1, 0, 0
while index <= #folders and seen < 10000 do
if not state.Alive or generation ~= browser.Generation then
return nil
end
local folder = folders[index]
index += 1
local ok, paths = pcall(listfiles, folder)
if ok and type(paths) == "table" then
for _, path in ipairs(paths) do
if type(path) ~= "string" then
continue
end
local key = path:gsub("\\", "/"):gsub("^%./", ""):gsub("/+$", ""):lower()
if visited[key] then
continue
end
visited[key] = true
seen += 1
if seen > 10000 then
break
end
if isfolder(path) then
if #folders < 1000 and not key:match("babft/images$") then
table.insert(folders, path)
end
elseif key:sub(-6) == ".build" then
table.insert(files, { Path = path, Name = basename(path) })
end
end
else
failed += 1
end
task.wait()
end
table.sort(files, function(a, b)
return a.Path:lower() < b.Path:lower()
end)
return files, (seen >= 10000 or #folders >= 1000), failed
end
local function filteredFiles()
local results, query = {}, browser.Query:lower()
for _, file in ipairs(browser.Files) do
if query == "" or file.Name:lower():find(query, 1, true) then
table.insert(results, file)
end
end
return results
end
local function imageAsset(path)
if type(getcustomasset) ~= "function" or type(isfile) ~= "function" then
return nil
end
storageFolders()
local stem = "BABFT/Images/" .. cacheKey(path)
for _, extension in ipairs({ ".png", ".jpg", ".webp" }) do
if isfile(stem .. extension) then
return getcustomasset(stem .. extension)
end
end
local body = buildRequest("/image?path=" .. HttpService:UrlEncode(path))
local extension
if body:sub(1, 8) == "\137PNG\r\n\26\n" then
extension = ".png"
elseif body:sub(1, 3) == "\255\216\255" then
extension = ".jpg"
elseif body:sub(1, 4) == "RIFF" and body:sub(9, 12) == "WEBP" then
extension = ".webp"
else
return nil
end
writefile(stem .. extension, body)
return getcustomasset(stem .. extension)
end
local function downloadBuild(file)
if browser.Selecting or state.Job or requestsPending() or importer.Pending then
browserStatus("Stop the current activity before selecting a build")
return
end
browser.Selecting = true
browserStatus("Downloading " .. file.Name)
local ok, problem = pcall(function()
storageFolders()
local source = buildRequest("/download?path=" .. HttpService:UrlEncode(file.Path))
local info = buildInfo(source)
if not state.Alive then
return
end
local name = basename(file.Name):gsub('[%c<>:"/\\|?*]', "_"):sub(1, 140)
if name:lower():sub(-6) ~= ".build" then
name ..= ".Build"
end
local path = "BABFT/Downloads/" .. cacheKey(file.Path) .. "_" .. name
if type(isfile) ~= "function" then
error("isfile is unavailable", 0)
end
if isfile(path) and readfile(path) ~= source then
local index = 1
local stem = path:sub(1, -7)
repeat
path = stem .. "_" .. index .. ".Build"
index += 1
until not isfile(path)
end
if not isfile(path) then
writefile(path, source)
end
if selectBuild(path) then
browserStatus("Selected " .. basename(path) .. " | " .. info.Summary)
end
end)
browser.Selecting = false
if not ok then
browserStatus(tostring(problem))
end
end
local function browserObject(class, parent, properties)
local object = Instance.new(class)
for key, value in pairs(properties) do
object[key] = value
end
object.Parent = parent
return object
end
local function browserText(parent, text, position, size, fontSize)
return browserObject("TextLabel", parent, {
Position = position,
Size = size,
BackgroundTransparency = 1,
Text = text,
TextColor3 = Color3.fromRGB(225, 232, 243),
TextSize = fontSize or 14,
Font = Enum.Font.GothamMedium,
TextWrapped = true,
TextXAlignment = Enum.TextXAlignment.Left,
})
end
local function browserButton(parent, text, position, size, callback)
local button = browserObject("TextButton", parent, {
Position = position,
Size = size,
BackgroundColor3 = Color3.fromRGB(34, 83, 87),
BorderSizePixel = 0,
Text = text,
TextColor3 = Color3.new(1, 1, 1),
TextSize = 14,
Font = Enum.Font.GothamMedium,
})
Instance.new("UICorner", button).CornerRadius = UDim.new(0, 7)
button.Activated:Connect(guarded(callback))
return button
end
renderBrowser = function()
browser.Render += 1
local render = browser.Render
for _, child in ipairs(ui.BrowserGrid:GetChildren()) do
if child:IsA("Frame") then
child:Destroy()
end
end
local pages = math.max(1, math.ceil(#browser.Results / 6))
browser.Page = math.clamp(browser.Page, 1, pages)
ui.BrowserPage.Text = string.format("%d / %d · %d builds", browser.Page, pages, #browser.Results)
ui.LocalTab.BackgroundColor3 = if browser.Mode == "Workspace"
then Color3.fromRGB(34, 115, 105)
else Color3.fromRGB(34, 44, 59)
ui.OnlineTab.BackgroundColor3 = if browser.Mode == "Online"
then Color3.fromRGB(34, 115, 105)
else Color3.fromRGB(34, 44, 59)
local images, locals = {}, {}
for index = (browser.Page - 1) * 6 + 1, math.min(browser.Page * 6, #browser.Results) do
local file = browser.Results[index]
local card = browserObject("Frame", ui.BrowserGrid, {
BackgroundColor3 = Color3.fromRGB(25, 33, 46),
BorderSizePixel = 0,
LayoutOrder = index,
})
Instance.new("UICorner", card).CornerRadius = UDim.new(0, 10)
local fallback = browserText(
card,
if file.Online then "Image unavailable" else ".Build",
UDim2.fromOffset(12, 8),
UDim2.new(1, -24, 1, -100),
18
)
fallback.TextXAlignment = Enum.TextXAlignment.Center
local thumb = browserObject("ImageLabel", card, {
Position = UDim2.fromOffset(8, 8),
Size = UDim2.new(1, -16, 1, -100),
BackgroundTransparency = 1,
Image = "",
ScaleType = Enum.ScaleType.Fit,
})
browserText(card, file.Name, UDim2.new(0, 12, 1, -88), UDim2.new(1, -24, 0, 42), 12)
browserButton(
card,
if file.Online then "Download & select" else "Select",
UDim2.new(0, 10, 1, -38),
UDim2.new(1, -20, 0, 28),
function()
if file.Online then
downloadBuild(file)
return
end
if browser.Selecting then
return
end
local ok, result = pcall(selectBuild, file.Path)
if not ok then
browserStatus(tostring(result))
elseif result then
browserStatus("Selected " .. file.Name .. " | " .. browser.Metadata[file.Path].Summary)
else
browserStatus("Stop the current activity before selecting a build")
end
end
)
if file.Online and file.HasImage then
table.insert(images, { File = file, Image = thumb, Fallback = fallback })
elseif not file.Online then
table.insert(locals, { File = file, Label = fallback })
end
end
task.spawn(function()
for _, entry in ipairs(locals) do
if not state.Alive or render ~= browser.Render then
return
end
local info = browser.Metadata[entry.File.Path]
if not info then
local ok, result = pcall(function()
return buildInfo(readfile(entry.File.Path))
end)
info = if ok
then result
else { Summary = "Unsupported or unreadable build", Details = tostring(result) }
browser.Metadata[entry.File.Path] = info
end
if render == browser.Render and entry.Label.Parent then
entry.Label.Text = info.Summary
end
task.wait()
end
end)
task.spawn(function()
for _, entry in ipairs(images) do
if not state.Alive or render ~= browser.Render or not ui.Browser.Enabled then
return
end
local ok, asset = pcall(imageAsset, entry.File.Path)
if ok and asset and state.Alive and render == browser.Render and entry.Image.Parent then
entry.Image.Image = asset
entry.Fallback.Text = ""
entry.Image:GetPropertyChangedSignal("IsLoaded"):Connect(function()
if entry.Image.IsLoaded then
entry.Fallback.Text = ""
end
end)
task.delay(5, function()
if entry.Image.Parent and not entry.Image.IsLoaded then
entry.Fallback.Text = "Image format unavailable"
end
end)
end
end
end)
end
refreshBrowser = function()
browser.Generation += 1
browser.Render += 1
local generation, mode, query = browser.Generation, browser.Mode, browser.Query
browserStatus(if mode == "Online" then "Searching babft.com…" else "Scanning workspace…")
local ok, problem = pcall(function()
local results, note
if mode == "Workspace" then
local files, limited, failed = scanWorkspace(generation)
if not files then
return
end
if generation ~= browser.Generation then
return
end
browser.Files, browser.Metadata = files, {}
results = filteredFiles()
note = if limited
then "Scan limit reached"
elseif failed > 0 then "Some folders could not be read"
else "Workspace"
else
local response = HttpService:JSONDecode(buildRequest("?q=" .. HttpService:UrlEncode(query)))
if type(response) ~= "table" or type(response.builds) ~= "table" then
error("Invalid build catalog", 0)
end
results = {}
for _, file in ipairs(response.builds) do
if type(file) == "table" and type(file.path) == "string" and type(file.filename) == "string" then
table.insert(
results,
{ Path = file.path, Name = file.filename, Online = true, HasImage = file.has_image == true }
)
end
if #results >= 10000 then
break
end
end
note = "babft.com"
end
if not state.Alive or generation ~= browser.Generation then
return
end
browser.Results, browser.Page = results, 1
renderBrowser()
browserStatus(note .. " | " .. #results .. " builds")
end)
if not ok and generation == browser.Generation then
browserStatus(tostring(problem))
end
end
local function createBrowser()
local gui = browserObject("ScreenGui", playerGui, {
Name = "BABFTBuildBrowser",
ResetOnSpawn = false,
IgnoreGuiInset = true,
DisplayOrder = 2147483647,
Enabled = false,
ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
})
ui.Browser = gui
local shade = browserObject("TextButton", gui, {
Size = UDim2.fromScale(1, 1),
BackgroundColor3 = Color3.new(),
BackgroundTransparency = 0.3,
Text = "",
Modal = true,
AutoButtonColor = false,
BorderSizePixel = 0,
})
local panel = browserObject("Frame", shade, {
AnchorPoint = Vector2.new(0.5, 0.5),
Position = UDim2.fromScale(0.5, 0.5),
Size = UDim2.new(1, -32, 1, -32),
BackgroundColor3 = Color3.fromRGB(14, 19, 27),
BorderSizePixel = 0,
})
browserObject("UISizeConstraint", panel, { MaxSize = Vector2.new(1050, 740) })
Instance.new("UICorner", panel).CornerRadius = UDim.new(0, 14)
browserText(panel, "Build browser", UDim2.fromOffset(20, 12), UDim2.new(1, -90, 0, 34), 24)
browserButton(panel, "X", UDim2.new(1, -50, 0, 12), UDim2.fromOffset(32, 32), function()
gui.Enabled = false
browser.Render += 1
end)
ui.LocalTab = browserButton(panel, "Workspace", UDim2.fromOffset(20, 58), UDim2.fromOffset(116, 32), function()
browser.Mode = "Workspace"
refreshBrowser()
end)
ui.OnlineTab = browserButton(panel, "Online", UDim2.fromOffset(144, 58), UDim2.fromOffset(92, 32), function()
browser.Mode = "Online"
refreshBrowser()
end)
local search = browserObject("TextBox", panel, {
Position = UDim2.fromOffset(20, 100),
Size = UDim2.new(1, -140, 0, 36),
Text = "",
PlaceholderText = "Search builds…",
ClearTextOnFocus = false,
TextSize = 15,
Font = Enum.Font.Gotham,
TextColor3 = Color3.new(1, 1, 1),
BackgroundColor3 = Color3.fromRGB(25, 33, 46),
BorderSizePixel = 0,
})
Instance.new("UICorner", search).CornerRadius = UDim.new(0, 8)
local function searchFiles()
browser.Query = search.Text
refreshBrowser()
end
search.FocusLost:Connect(function(enter)
if enter then
searchFiles()
end
end)
browserButton(panel, "Search", UDim2.new(1, -110, 0, 100), UDim2.fromOffset(90, 36), searchFiles)
browserButton(panel, "Refresh", UDim2.new(1, -110, 0, 58), UDim2.fromOffset(90, 32), searchFiles)
ui.BrowserGrid = browserObject("ScrollingFrame", panel, {
Position = UDim2.fromOffset(20, 150),
Size = UDim2.new(1, -40, 1, -236),
BackgroundTransparency = 1,
BorderSizePixel = 0,
ScrollBarThickness = 3,
CanvasSize = UDim2.new(),
AutomaticCanvasSize = Enum.AutomaticSize.Y,
})
local grid = browserObject("UIGridLayout", ui.BrowserGrid, {
CellSize = UDim2.new(1 / 3, -8, 0, 218),
CellPadding = UDim2.fromOffset(10, 10),
SortOrder = Enum.SortOrder.LayoutOrder,
})
local function resize()
local columns = if ui.BrowserGrid.AbsoluteSize.X < 600 then 2 else 3
grid.CellSize = UDim2.new(1 / columns, -8, 0, 218)
end
ui.BrowserGrid:GetPropertyChangedSignal("AbsoluteSize"):Connect(resize)
ui.BrowserPage = browserText(panel, "", UDim2.new(0, 72, 1, -76), UDim2.new(1, -144, 0, 28), 14)
ui.BrowserPage.TextXAlignment = Enum.TextXAlignment.Center
browserButton(panel, "<", UDim2.new(0, 20, 1, -76), UDim2.fromOffset(42, 28), function()
browser.Page -= 1
renderBrowser()
end)
browserButton(panel, ">", UDim2.new(1, -62, 1, -76), UDim2.fromOffset(42, 28), function()
browser.Page += 1
renderBrowser()
end)
ui.BrowserStatus = browserText(panel, "", UDim2.new(0, 20, 1, -42), UDim2.new(1, -40, 0, 34), 12)
end
openBrowser = function()
if not ui.Browser then
createBrowser()
end
ui.Browser.Enabled = true
refreshBrowser()
end
buildImporterUi = function(builderWindow)
local page = builderWindow:Page({ Name = "Builder", Icon = "hammer" })
local section = page:Card({ Title = "Builds", Collapsible = false })
section:Button({ Name = "Browse builds", Callback = guarded(openBrowser) })
ui.Selected = section:Label({ Name = "Select a build", RawText = true })
ui.BuildInfo = section:Label({ Name = "Workspace files and babft.com", RawText = true })
ui.Preview = section:Button({
Name = "Preview build",
Callback = guarded(function()
local ok, problem = pcall(previewBuild)
if not ok then
clearPreview()
warn("[BABFT] " .. tostring(problem))
end
end),
})
section:Button({ Name = "Clear preview", Callback = guarded(clearPreview) })
ui.Build = section:Button({
Name = "Build / Resume",
Callback = guarded(function()
if state.Job or requestsPending() or browser.Selecting then
return
end
local loaded, problem = pcall(function()
if #importer.Rows == 0 then
loadBuild()
end
end)
if not loaded then
warn("[BABFT] " .. tostring(problem))
return
end
clearPreview()
beginJob("Build", function(job)
state.BuildBusy = true
local ok, failure = xpcall(runBuild, debug.traceback, job)
state.BuildBusy = false
syncToggles()
if not ok then
state.Status = "Import stopped"
warn("[BABFT] " .. tostring(failure))
elseif not current(job) then
state.Status = "Import stopped"
end
end)
end),
})
ui.StopBuild = section:Button({
Name = "Stop build",
Disabled = true,
Callback = guarded(function()
if state.Job and state.Job.Kind == "Build" then
stopJob("Import stopped")
end
end),
})
ui.BuilderStatus = section:Label({ Name = "Idle", RawText = true })
local materials = page:Card({ Title = "Materials", Collapsible = true })
local detail = materials:Custom({ Name = "Inventory", AutomaticSize = true, Height = 0, Searchable = false })
ui.Materials = browserText(detail.Body, "", UDim2.new(), UDim2.new(1, 0, 0, 0), 12)
ui.Materials.TextYAlignment = Enum.TextYAlignment.Top
ui.Materials.AutomaticSize = Enum.AutomaticSize.Y
end
local function watchCounter(name, totalKey)
local value = findPath(player, "Data", name)
local previous = state.ValueConnections[name]
if previous and previous.Object == value then
return
end
if previous then
previous.Connection:Disconnect()
state.ValueConnections[name] = nil
end
if not value or not value:IsA("ValueBase") or type(value.Value) ~= "number" then
return
end
local last = value.Value
state.ValueConnections[name] = {
Object = value,
Connection = value.Changed:Connect(function(amount)
if state.Alive and type(amount) == "number" then
state[totalKey] += math.max(0, amount - last)
last = amount
end
end),
}
end
local ok, problem = xpcall(buildUi, debug.traceback)
if not ok then
warn("[BABFT] " .. tostring(problem))
state.Alive = false
stopJob()
setDashboard(false)
if ui.Window then
window:Destroy()
end
if ui.Dashboard then
ui.Dashboard:Destroy()
end
for _, connection in ipairs(state.Connections) do
connection:Disconnect()
end
return
end
watchCounter("Gold", "GoldEarned")
watchCounter("GoldBlock", "BlocksEarned")
task.spawn(function()
while state.Alive do
local updated, failure = pcall(function()
local pending = state.PendingGlue
if
pending
and not pending.GlueDeleting
and (not pending.Glue or pending.Glue.Parent ~= pending.GlueFolder)
then
pending.Glue = nil
state.PendingGlue = nil
syncToggles()
end
watchCounter("Gold", "GoldEarned")
watchCounter("GoldBlock", "BlocksEarned")
claimResults()
updateDashboard()
if ui.Activity then
ui.Activity:Set(state.Status)
end
if ui.BuilderStatus then
ui.BuilderStatus:Set(state.Status)
end
end)
if not updated then
warn("[BABFT] " .. tostring(failure))
stopJob("Stopped")
break
end
task.wait(1)
end
end)
![[FREE] AutoFarm, No Damage, Player Modifications](https://cdn.kocakz.me/images/d3931412ff708ca535785f51d3a1f358e59670f5e02fd525eff4d6a293817bcc.webp)


an actual working one thx
@turbotruster0 you are welcome
cool auto farm
sometimes when i hit the chest i just stay there and dont movve
it will use firetouchinterest to collect gold
WELL MADE THX
like it more so that the community knows it works
Why it kicks me out of the game?
when i get in the chest ¿it bugs
cool
W script
hey bro your script is old it dosent launch it just directly goes and does not even collect gold please fix it ,thanks