--[[ BABFT - Block Spawner (GUI) Credits: TopbarPlus Library by @Member Source: https://scriptblox.com/script/Universal-Script-TopBarPlus-Library-233495 HOW TO USE: 1. Open "Options" in the topbar to choose the block and settings. The default block is the one you have the most of in your inventory. 2. Press "Place" (topbar or panel), then tap the ground of ANY zone. The zone is detected from the spot you tap. 3. The BuildingTool is equipped only AFTER your tap is registered (PICK_DELAY), and unequipped again after spawning. NOTES: - Block must exist in ReplicatedStorage.BuildingParts and be in your inventory. - Blocks are placed as a ground grid (no tower), so the gap holds. - The result is counted from your folder under workspace.Blocks, because the InvokeServer return value is nil even on success. ]] local Players = game:GetService("Players") local UserInputService = game:GetService("UserInputService") local lp = Players.LocalPlayer local pg = lp:WaitForChild("PlayerGui") local camera = workspace.CurrentCamera -- wait after your tap before the tool is equipped (seconds) local PICK_DELAY = 0.01 if _G.BabftSpawner then local s = _G.BabftSpawner for _, ic in ipairs(s.icons or {}) do pcall(function() ic:destroy() end) end if s.pickConn then s.pickConn:Disconnect() end end if pg:FindFirstChild("BabftSpawnerGui") then pg.BabftSpawnerGui:Destroy() end local gui = Instance.new("ScreenGui") gui.Name = "BabftSpawnerGui" gui.ResetOnSpawn = false gui.IgnoreGuiInset = true gui.Parent = pg local function corner(parent, r) local c = Instance.new("UICorner") c.CornerRadius = UDim.new(0, r) c.Parent = parent end local function isZone(inst) return inst and inst:IsA("BasePart") and string.find(inst.Name, "Zone") ~= nil end -- blocks you have in stock, with their amounts local function stockList() local out = {} local bp = game.ReplicatedStorage:FindFirstChild("BuildingParts") local data = lp:FindFirstChild("Data") if not bp or not data then return out end for _, m in ipairs(bp:GetChildren()) do local s = data:FindFirstChild(m.Name) if s and s.Value > 0 and m.PrimaryPart then table.insert(out, { name = m.Name, amount = s.Value }) end end return out end local function ownedBlocks() local names = {} for _, e in ipairs(stockList()) do table.insert(names, e.name) end table.sort(names) return names end -- default: the block you have the most of local function defaultBlock() local best, bestAmount = nil, -1 for _, e in ipairs(stockList()) do if e.amount > bestAmount then best, bestAmount = e.name, e.amount end end return best or "WoodBlock" end local cfg = { block = defaultBlock(), count = "100", gap = "0.1", } local busy = false local picking = false -- ==================== TOOL HANDLING ==================== local function findTool() local char = lp.Character local inHand = char and char:FindFirstChild("BuildingTool") if inHand then return inHand, true end local bp = lp:FindFirstChildOfClass("Backpack") local inBag = bp and bp:FindFirstChild("BuildingTool") return inBag, false end local function unequipTool() local hum = lp.Character and lp.Character:FindFirstChildOfClass("Humanoid") if not hum then return end pcall(function() hum:UnequipTools() end) local dl = os.clock() + 1.5 while os.clock() < dl do local _, inHand = findTool() if not inHand then return end task.wait(0.05) end end local function equipTool() local hum = lp.Character and lp.Character:FindFirstChildOfClass("Humanoid") if not hum then return nil, "character not ready" end local tool, inHand local dl = os.clock() + 2 repeat tool, inHand = findTool() if tool then break end task.wait(0.05) until os.clock() >= dl if not tool then return nil, "BuildingTool not found" end if not inHand then hum:EquipTool(tool) local dl2 = os.clock() + 3 while os.clock() < dl2 and not lp.Character:FindFirstChild("BuildingTool") do task.wait(0.05) end end local equipped = lp.Character:FindFirstChild("BuildingTool") if not equipped then return nil, "could not equip BuildingTool" end return equipped end -- ==================== PANEL ==================== local panel = Instance.new("Frame") panel.Name = "Panel" panel.AnchorPoint = Vector2.new(0.5, 0.5) panel.Position = UDim2.fromScale(0.5, 0.5) panel.Size = UDim2.new(0.88, 0, 0.62, 0) panel.BackgroundColor3 = Color3.fromRGB(20, 20, 24) panel.BorderSizePixel = 0 panel.Visible = false panel.Parent = gui local sl = Instance.new("UISizeConstraint") sl.MaxSize = Vector2.new(380, 440) sl.MinSize = Vector2.new(260, 280) sl.Parent = panel corner(panel, 14) local pad = Instance.new("UIPadding") pad.PaddingTop = UDim.new(0, 14) pad.PaddingBottom = UDim.new(0, 14) pad.PaddingLeft = UDim.new(0, 14) pad.PaddingRight = UDim.new(0, 14) pad.Parent = panel local list = Instance.new("UIListLayout") list.Padding = UDim.new(0, 8) list.SortOrder = Enum.SortOrder.LayoutOrder list.Parent = panel local function label(parent, text, order, h) local l = Instance.new("TextLabel") l.Size = UDim2.new(1, 0, 0, h or 34) l.BackgroundTransparency = 1 l.Text = text l.TextColor3 = Color3.fromRGB(255, 255, 255) l.TextScaled = true l.Font = Enum.Font.GothamBold l.LayoutOrder = order l.Parent = parent return l end label(panel, "Block Spawner", 0) local function row(order, labelText) local r = Instance.new("Frame") r.Size = UDim2.new(1, 0, 0, 42) r.BackgroundTransparency = 1 r.LayoutOrder = order r.Parent = panel local l = Instance.new("TextLabel") l.Size = UDim2.new(0.42, 0, 1, 0) l.BackgroundTransparency = 1 l.Text = labelText l.TextColor3 = Color3.fromRGB(210, 210, 215) l.TextScaled = true l.Font = Enum.Font.Gotham l.TextXAlignment = Enum.TextXAlignment.Left l.Parent = r return r end local function textRow(order, labelText, key) local r = row(order, labelText) local box = Instance.new("TextBox") box.Position = UDim2.fromScale(0.44, 0) box.Size = UDim2.new(0.56, 0, 1, 0) box.BackgroundColor3 = Color3.fromRGB(35, 35, 42) box.Text = cfg[key] box.TextColor3 = Color3.fromRGB(255, 255, 255) box.TextScaled = true box.Font = Enum.Font.Gotham box.ClearTextOnFocus = false box.Parent = r corner(box, 8) box.FocusLost:Connect(function() cfg[key] = box.Text end) end local function dropdownRow(order, labelText, key, getOptions) local r = row(order, labelText) local btn = Instance.new("TextButton") btn.Position = UDim2.fromScale(0.44, 0) btn.Size = UDim2.new(0.56, 0, 1, 0) btn.BackgroundColor3 = Color3.fromRGB(35, 35, 42) btn.Text = cfg[key] .. " ▾" btn.TextColor3 = Color3.fromRGB(255, 255, 255) btn.TextScaled = true btn.Font = Enum.Font.GothamBold btn.Parent = r corner(btn, 8) local menu = Instance.new("ScrollingFrame") menu.Position = UDim2.new(0.44, 0, 1, 4) menu.Size = UDim2.new(0.56, 0, 0, 0) menu.BackgroundColor3 = Color3.fromRGB(28, 28, 34) menu.BorderSizePixel = 0 menu.ScrollBarThickness = 6 menu.CanvasSize = UDim2.new(0, 0, 0, 0) menu.AutomaticCanvasSize = Enum.AutomaticSize.Y menu.Visible = false menu.ZIndex = 10 menu.Parent = r corner(menu, 8) local ml = Instance.new("UIListLayout") ml.Padding = UDim.new(0, 4) ml.SortOrder = Enum.SortOrder.LayoutOrder ml.Parent = menu local mp = Instance.new("UIPadding") mp.PaddingTop = UDim.new(0, 4) mp.PaddingBottom = UDim.new(0, 4) mp.Parent = menu local function close() menu.Visible = false end local function open() for _, child in ipairs(menu:GetChildren()) do if child:IsA("TextButton") then child:Destroy() end end local opts = getOptions() if #opts == 0 then btn.Text = "none in stock" return end for i, name in ipairs(opts) do local o = Instance.new("TextButton") o.Size = UDim2.new(1, -8, 0, 40) o.BackgroundColor3 = Color3.fromRGB(45, 45, 55) o.Text = name o.TextColor3 = Color3.fromRGB(255, 255, 255) o.TextScaled = true o.Font = Enum.Font.Gotham o.LayoutOrder = i o.ZIndex = 11 o.Parent = menu corner(o, 6) o.Activated:Connect(function() cfg[key] = name btn.Text = name .. " ▾" close() end) end menu.Size = UDim2.new(0.56, 0, 0, math.min(#opts, 5) * 44 + 8) menu.Visible = true end btn.Activated:Connect(function() if menu.Visible then close() else open() end end) return close end local closeBlockMenu = dropdownRow(1, "Block", "block", ownedBlocks) textRow(2, "Count", "count") textRow(3, "Gap", "gap") local status = label(panel, "tap Place, then tap the ground of any zone", 4, 40) status.Font = Enum.Font.Gotham status.TextColor3 = Color3.fromRGB(160, 160, 170) status.TextWrapped = true local function button(text, color, order) local b = Instance.new("TextButton") b.Size = UDim2.new(1, 0, 0, 46) b.BackgroundColor3 = color b.Text = text b.TextColor3 = Color3.fromRGB(255, 255, 255) b.TextScaled = true b.Font = Enum.Font.GothamBold b.LayoutOrder = order b.Parent = panel corner(b, 10) return b end local placeBtn = button("Place", Color3.fromRGB(46, 160, 90), 5) local closeBtn = button("Close", Color3.fromRGB(60, 60, 70), 6) local banner = Instance.new("Frame") banner.Name = "PickBanner" banner.AnchorPoint = Vector2.new(0.5, 0) banner.Position = UDim2.new(0.5, 0, 0, 12) banner.Size = UDim2.new(0.9, 0, 0, 56) banner.BackgroundColor3 = Color3.fromRGB(20, 20, 24) banner.BorderSizePixel = 0 banner.Visible = false banner.Parent = gui local bsl = Instance.new("UISizeConstraint") bsl.MaxSize = Vector2.new(420, 60) bsl.Parent = banner corner(banner, 12) local bText = Instance.new("TextLabel") bText.Size = UDim2.new(0.62, 0, 1, 0) bText.Position = UDim2.fromScale(0.03, 0) bText.BackgroundTransparency = 1 bText.Text = "Tap the ground of any zone" bText.TextColor3 = Color3.fromRGB(255, 255, 255) bText.TextScaled = true bText.Font = Enum.Font.GothamBold bText.TextXAlignment = Enum.TextXAlignment.Left bText.Parent = banner local cancelPick = Instance.new("TextButton") cancelPick.Size = UDim2.new(0.28, 0, 0, 40) cancelPick.Position = UDim2.new(0.69, 0, 0.5, -20) cancelPick.BackgroundColor3 = Color3.fromRGB(170, 60, 60) cancelPick.Text = "Cancel" cancelPick.TextColor3 = Color3.fromRGB(255, 255, 255) cancelPick.TextScaled = true cancelPick.Font = Enum.Font.GothamBold cancelPick.Parent = banner corner(cancelPick, 10) -- ==================== SPAWN ==================== local function spawnAt(c, zone, hitPos) local count = tonumber(c.count) or 100 local gap = tonumber(c.gap) or 0.1 local name = c.block local tool, toolErr = equipTool() if not tool then return toolErr end local rf = tool:FindFirstChild("RF", true) if not rf or not rf:IsA("RemoteFunction") then return "RF missing" end local bp2 = game.ReplicatedStorage:FindFirstChild("BuildingParts") local model = bp2 and bp2:FindFirstChild(name) if not model or not model.PrimaryPart then return "block '" .. name .. "' not found" end local data = lp:FindFirstChild("Data") local stock = data and data:FindFirstChild(name) if not stock then return "no '" .. name .. "' in inventory" end local size = model.PrimaryPart.Size local stepX = size.X + gap local stepZ = size.Z + gap local cols = math.ceil(math.sqrt(count)) local rows = math.ceil(count / cols) local offset = stepX / 2 local base = zone.CFrame:PointToObjectSpace(hitPos) local half = zone.Size / 2 local maxX = base.X + (cols - 1) * stepX + offset + size.X / 2 local maxZ = base.Z + (rows - 1) * stepZ + size.Z / 2 if math.abs(base.X) > half.X or math.abs(base.Z) > half.Z or math.abs(maxX) > half.X or math.abs(maxZ) > half.Z then return "grid does not fit in " .. zone.Name .. " from this spot, pick closer to the middle" end local function countMine() local blocks = workspace:FindFirstChild("Blocks") local mine = blocks and blocks:FindFirstChild(lp.Name) if not mine then return 0 end local n = 0 for _, d in ipairs(mine:GetChildren()) do if d.Name == name then n += 1 end end return n end local before = countMine() local owned = stock.Value local toSend = math.min(count, owned) if toSend <= 0 then return "no stock of " .. name end local done = 0 for i = 1, toSend do local c0 = (i - 1) % cols local r0 = math.floor((i - 1) / cols) local shift = (r0 % 2 == 1) and offset or 0 local rel = CFrame.new(base.X + c0 * stepX + shift, half.Y + size.Y / 2 + 0.2, base.Z + r0 * stepZ) local world = zone.CFrame * rel local args = { name, owned, zone, zone.CFrame:ToObjectSpace(world), false, world, false, false } task.spawn(function() pcall(function() rf:InvokeServer(table.unpack(args)) end) done += 1 end) end while done < toSend do task.wait() end task.wait(3) return string.format("placed %d of %d in %s", countMine() - before, toSend, zone.Name) end -- ==================== FLOW ==================== local function setStatus(t) status.Text = t end local function stopPick() picking = false banner.Visible = false end local function startPick() if busy then setStatus("already placing...") return end closeBlockMenu() unequipTool() picking = true banner.Visible = true bText.Text = "Tap the ground of any zone" setStatus("waiting for a spot...") end local function runSpawn(zone, hitPos) busy = true placeBtn.Text = "Placing..." setStatus("placing...") task.wait(PICK_DELAY) local ok, msg = pcall(spawnAt, cfg, zone, hitPos) unequipTool() setStatus(ok and tostring(msg) or ("error: " .. tostring(msg))) placeBtn.Text = "Place" busy = false end local pickConn = UserInputService.InputBegan:Connect(function(input, processed) if not picking or processed then return end local t = input.UserInputType if t ~= Enum.UserInputType.Touch and t ~= Enum.UserInputType.MouseButton1 then return end local ray = camera:ViewportPointToRay(input.Position.X, input.Position.Y) local rp = RaycastParams.new() rp.FilterType = Enum.RaycastFilterType.Exclude rp.FilterDescendantsInstances = { lp.Character, gui } local hit = workspace:Raycast(ray.Origin, ray.Direction * 1000, rp) if not hit or not isZone(hit.Instance) then setStatus("tap on the ground of a zone") return end local zone = hit.Instance stopPick() task.spawn(runSpawn, zone, hit.Position) end) cancelPick.Activated:Connect(function() stopPick() setStatus("cancelled") end) placeBtn.Activated:Connect(startPick) closeBtn.Activated:Connect(function() panel.Visible = false closeBlockMenu() if _G.BabftSpawner and _G.BabftSpawner.optIcon then pcall(function() _G.BabftSpawner.optIcon:deselect() end) end end) -- ==================== TOPBAR ==================== -- TopbarPlus Library by @Member -- https://scriptblox.com/script/Universal-Script-TopBarPlus-Library-233495 local icons = {} local optIcon local okLib, Icon = pcall(function() local genv = (getgenv and getgenv()) or _G genv.TopbarPlusLibrary = nil return loadstring(game:HttpGet("https://raw.githubusercontent.com/MemberLol/TopBarPlus/refs/heads/main/Library"))() end) if okLib and Icon then optIcon = Icon.new() :setName("BabftOptions") :setLabel("Options") :setCaption("Block spawner settings") :setLeft() optIcon.selected:Connect(function() panel.Visible = true end) optIcon.deselected:Connect(function() panel.Visible = false closeBlockMenu() end) table.insert(icons, optIcon) local placeIcon = Icon.new() :setName("BabftPlace") :setLabel("Place") :setCaption("Pick a zone spot and spawn") :oneClick() :setLeft() placeIcon.selected:Connect(startPick) table.insert(icons, placeIcon) else warn("[Spawner] TopbarPlus failed to load, use the panel only") panel.Visible = true end _G.BabftSpawner = { icons = icons, optIcon = optIcon, gui = gui, pickConn = pickConn } print("[Spawner] ready | default block: " .. cfg.block .. " | PICK_DELAY=" .. PICK_DELAY)