Description
Hello, i just want to share script for Build a Great Wall! honestly everything created using Artificial Intelligence (known as AI) and @Gouang helped provide idea & goals of the script. Note: OpenSource & Keyless What the configuration/settings even does? [*] Building Tab Fill Strategy Ordering β Which ghost gets picked next: Closest first β nearest ghost to you Whole section β stay within your last placed section One column β stay within your last placed column Group size β Single ghost β one block at a time Cluster of ghosts β fill an entire area before moving on Travel method β How you move to the next target: Instant teleport Fast walk Stay still (build from one spot) Placement Method How to place blocks β Which firing method to use: Direct (remote) β fastest, fires the RemoteEvent directly Game controls (E key) β safest, simulates pressing E Game function β calls the game's internal placeInto function (slow & kinda buggy) Hybrid (remote + E key) β both at once Speed & Range Blocks per second β Fire rate. Higher = faster but server may reject. Reach β Max distance (studs) you can place blocks from. Cluster size β Radius (studs) that counts as one cluster. Tween speed β Studs/sec, only used in Fast walk travel mode. [*] Training Tab Auto Training Train strength β Fires the bench press prompt once, then holds position. Re-fires if you drift away. Train speed β Anchors you on the treadmill. Server grants stats while touching. Pause building while training β Blocks won't register while training, so pause building. Re-fire when this far away β Distance (studs) the character can drift from the bench before re-firing the prompt. Training Area Current area β Shows which training plot is being used + requirement + blocks built. Refresh best area now β Manually re-picks the best training area. Boost Zone Current boost β Shows if Hot Springs boost is open, multiplier, and time left. Go to boost zone β Manually teleport to Hot Springs. Come back β Return to where you were before entering Hot Springs. Auto-travel to boost zone β Auto-goes to Hot Springs whenever the wall boost is active. [*] Walls Tab Current Wall Wall info β Which wall you're building, its gym boost multiplier, and coin reward. Your Position Position β Your current section, column, and layer. Wall Selection Filter Filter by wall type β Only build if the wall is at least a certain tier. Minimum wall type β Picks which tier is the minimum. Quick Jump Section β Dropdown to pick a section number. Jump to section at ground level β Teleports you to that section at ground height. Jump to section at top of ghosts β Teleports you to the top of the highest ghost block in that section. Section Markers Show section markers β Floating labels above each section with name, status, progress bar, and layer count. Only show nearby sections β Only display markers close to you. Nearby range β How many sections on each side count as "nearby." Hide finished sections β Don't show markers for completed sections. Shrink far markers β Scale markers down with distance for readability. Hide all markers β Immediately destroy all section markers. Other tabs are self-explanatory.
Script source Luau
-- [[ BloxScripts Risk Notice ]]
-- This script has not been verified by BloxScripts.fun. Deal with caution.
--
-- Stay safe:
-- β’ Never log in on unofficial Roblox sites or lookalike domains.
-- β’ Real Roblox links use roblox.com (check the .com ending).
-- β’ Treat fake Roblox login / "claim reward" pages as phishing.
-- [[ End BloxScripts Risk Notice ]]
--[[
GreatWall AutoPlace β Rayfield
- Input modes: remote / key / internal / hybrid (remote + E key)
- Camera see-through (CanQuery=false)
- Training: bench press fired once, treadmill anchor
- Cluster: BFS-ordered fill
- Anti-AFK via Player.Idled hook
]]
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local LocalPlayer = Players.LocalPlayer
local GreatWall = ReplicatedStorage:WaitForChild("GreatWall")
local Remotes = GreatWall:WaitForChild("Remotes")
do
local old = workspace:FindFirstChild("AutoPlace_SectionViz")
if old then old:Destroy() end
local old2 = workspace:FindFirstChild("AutoPlace_FireRing")
if old2 then old2:Destroy() end
local cam = workspace.CurrentCamera
if cam then
local old3 = cam:FindFirstChild("AutoPlace_Highlights")
if old3 then old3:Destroy() end
end
end
local Rayfield = loadstring(game:HttpGet("https://sirius.menu/rayfield"))()
local Window = Rayfield:CreateWindow({
Name = "Build a Greatwall",
LoadingTitle = "Automation",
LoadingSubtitle = "by ChatGPT & Gouang",
ConfigurationSaving = { Enabled = false },
Icon = 0,
})
local CFG = {
running = false, rate = 1500, adaptive = true, minRate = 15, regrowEvery = 1.5,
moveMode = "teleport", placeMode = "nearest", targetMode = "cluster",
placeInput = "hybrid", stepSpeed = 400, fireRadius = 40,
noclip = true, cameraSeeThrough = true, useFlight = true,
refillEnabled = true, refillInterval = 0.08, refillGroundY = 4,
inflightTimeout = 2.0, pendingMaxAge = 5.0, suspendPoll = 0.5,
clusterRadius = 30,
trainStrength = false,
trainSpeed = false,
trainPauseBuild = true,
trainReengageDist = 15,
trainSpeedAnchorOffset = 3,
hsAuto = false,
hsReturnBefore = 30,
hsTeleportYOffset = 3,
hsStayMin = 60,
tierFilterOn = false, tierFilterMin = 1,
showPlaceHighlight = true, showFireRing = false,
fireRingColor = Color3.fromRGB(143, 0, 255),
placeHighlightColor = Color3.fromRGB(143, 0, 255),
verbose = false,
antiAfk = true,
}
local MAX_FIRES_PER_TICK = 12
local function log(...)
local parts = {}
for i = 1, select("#", ...) do parts[#parts + 1] = tostring(select(i, ...)) end
print("[AutoPlace] " .. table.concat(parts, " "))
end
-- ============================================================
-- Reflection
-- ============================================================
local function safeScripts()
local ok, list = pcall(getscripts)
if ok and type(list) == "table" then return list end
local out = {}
for _, d in LocalPlayer.PlayerScripts:GetDescendants() do
if d:IsA("LocalScript") then out[#out + 1] = d end
end
return out
end
local function findClientScript()
for _, s in ipairs(safeScripts()) do
if s and s.Name and s.Name:lower():find("greatwall") then return s end
end
end
local function safeClosure(scr)
local ok, c = pcall(getscriptclosure, scr)
return ok and type(c) == "function" and c or nil
end
local function safeProtos(fn)
local ok, p = pcall(function() return debug.getprotos(fn) end)
return ok and type(p) == "table" and p or {}
end
local function safeUpvalue(fn, i)
local ok, name, val = pcall(function() return debug.getupvalue(fn, i) end)
return ok and name or nil, val
end
local function findUpvalue(fn, match, depth)
depth = depth or 0
if depth > 8 or type(fn) ~= "function" then return nil end
local i = 1
while true do
local name, val = safeUpvalue(fn, i)
if not name then break end
if match(name, val) then return val end
i = i + 1
if i > 200 then break end
end
for _, p in ipairs(safeProtos(fn)) do
local f = findUpvalue(p, match, depth + 1)
if f then return f end
end
end
local function findPlaceIntoHeuristic(root)
local found = nil
local function walk(fn, depth)
if found or depth > 8 or type(fn) ~= "function" then return end
local hasPlace, hasU2 = false, false
local i = 1
while true do
local name, val = safeUpvalue(fn, i)
if not name then break end
if typeof(val) == "Instance" and val:IsA("RemoteEvent") and val.Name == "Place" then hasPlace = true end
if type(val) == "table" and val.seq ~= nil and val.spent ~= nil and val.sent ~= nil then hasU2 = true end
i = i + 1
if i > 200 then break end
end
if hasPlace and hasU2 then found = fn; return end
for _, p in ipairs(safeProtos(fn)) do
walk(p, depth + 1)
if found then return end
end
end
walk(root, 0)
return found
end
local Config, Place, Fill = nil, nil, nil
local WallTiersModule = nil
local clientPlaceInto = nil
local clientRoot = nil
do
local scr = findClientScript()
clientRoot = scr and safeClosure(scr)
if clientRoot then
Config = findUpvalue(clientRoot, function(n, v) return n == "Config" and type(v) == "table" end)
Place = findUpvalue(clientRoot, function(n, v)
return n == "Place" and typeof(v) == "Instance" and v:IsA("RemoteEvent")
end)
clientPlaceInto = findUpvalue(clientRoot, function(n, v)
return n == "placeInto" and type(v) == "function"
end)
if not clientPlaceInto then clientPlaceInto = findPlaceIntoHeuristic(clientRoot) end
end
if not Config then
local ok, mod = pcall(require, GreatWall:WaitForChild("Config"))
if ok then Config = mod end
end
if not Place then Place = Remotes:WaitForChild("Place") end
Fill = Remotes:WaitForChild("Fill", 5)
local ok, mod = pcall(require, GreatWall:WaitForChild("WallTiers"))
if ok and type(mod) == "table" then WallTiersModule = mod end
end
log("Ready. Config=" .. tostring(Config) .. " placeInto=" .. tostring(clientPlaceInto))
local function getLiveAttr(name)
local ok, v = pcall(function() return LocalPlayer:GetAttribute(name) end)
if ok then return v end
return nil
end
local LIVE_ATTRS = {
PlaceRange=true, PlaceCooldown=true, Capacity=true, Blocks=true, Skin=true,
PracticeAt=true, TriedEmpty=true, AtPit=true, Filling=true,
ShowVfx=true, ShowAnims=true, ShowBags=true,
}
local function allLiveAttrs()
local out = {}
for k in pairs(LIVE_ATTRS) do
local v = getLiveAttr(k)
if v ~= nil then out[#out + 1] = { key = k, value = v } end
end
table.sort(out, function(a, b) return a.key < b.key end)
return out
end
local BLOCK = (Config and Config.Block) or 4
local ORIGIN_X = (Config and Config.Origin and Config.Origin.X) or 0
local ORIGIN_Y = (Config and Config.Origin and Config.Origin.Y) or 0
local ORIGIN_Z = (Config and Config.Origin and Config.Origin.Z) or -10
local LENGTH = (Config and Config.Length) or 330
local SECTIONS = (Config and Config.Sections) or 16
local THICKNESS = (Config and Config.Thickness) or 14
local STAGES = Config and Config.Stages or nil
local function columnOf(x) return math.clamp(math.floor((x - ORIGIN_X) / BLOCK), 0, LENGTH - 1) end
local function layerOf(y) return math.floor((y - ORIGIN_Y) / BLOCK) end
local function columnWorldX(col) return ORIGIN_X + col * BLOCK end
local function defaultZ() return ORIGIN_Z - THICKNESS * BLOCK * 0.5 - 6 end
local function sectionOf(x)
local col = columnOf(x)
if STAGES then
for i, v in ipairs(STAGES) do
if v.x0 <= col and col <= v.x1 then return i end
end
end
local per = math.max(1, math.ceil(LENGTH / SECTIONS))
return math.clamp(math.floor(col / per) + 1, 1, SECTIONS)
end
local function cellOf(g)
local col = math.floor((g.Position.X - ORIGIN_X) / BLOCK + 0.5)
local lay = math.floor((g.Position.Y - ORIGIN_Y) / BLOCK + 0.5)
local zb = math.floor(g.Position.Z * 4 + 0.5)
return col, lay, zb
end
local function keyOf(col, lay, zb) return col .. ":" .. lay .. ":" .. zb end
local sList, lList, iList = {}, {}, {}
local partList = {}
local firedSet = {}
local inflight = {}
local pendingFires = {}
local listDirty = true
local seq = 1000000
local lastPlacedPos = nil
local lastSuspendCheck = 0
local activeCluster = nil
local analytics = {
totalFires=0, totalAcks=0, firesThisSec=0, acksThisSec=0,
lastBucket=os.clock(), recent={}, RECENT_SECONDS=5,
lastAckAt=0, lastFireAt=0,
}
local function onFire()
analytics.totalFires = analytics.totalFires + 1
analytics.firesThisSec = analytics.firesThisSec + 1
analytics.lastFireAt = os.clock()
end
local function onAck()
analytics.totalAcks = analytics.totalAcks + 1
analytics.acksThisSec = analytics.acksThisSec + 1
analytics.lastAckAt = os.clock()
end
local function tickAnalytics()
local now = os.clock()
if now - analytics.lastBucket >= 1 then
table.insert(analytics.recent, { fires = analytics.firesThisSec, acks = analytics.acksThisSec })
while #analytics.recent > analytics.RECENT_SECONDS do table.remove(analytics.recent, 1) end
analytics.firesThisSec = 0
analytics.acksThisSec = 0
analytics.lastBucket = now
end
for k, v in pairs(pendingFires) do
if now - v.at > CFG.pendingMaxAge then
if v.ghost then inflight[v.ghost] = nil end
pendingFires[k] = nil
end
end
end
local function recentAccuracy()
local f, a = 0, 0
for _, s in ipairs(analytics.recent) do f = f + s.fires; a = a + s.acks end
if f == 0 then return nil end
return a / f
end
local flightAttachment, flightVelocity
local noclipSaved = {}
local function getRoot()
local char = LocalPlayer.Character
if not char then return nil end
return char.PrimaryPart or char:FindFirstChild("HumanoidRootPart") or char:FindFirstChildOfClass("BasePart")
end
local function setNoclip(on)
local char = LocalPlayer.Character
if not char then return end
if on then
for _, d in ipairs(char:GetDescendants()) do
if d:IsA("BasePart") then
if noclipSaved[d] == nil then noclipSaved[d] = d.CanCollide end
d.CanCollide = false
end
end
else
for _, d in ipairs(char:GetDescendants()) do
if d:IsA("BasePart") then
local saved = noclipSaved[d]
d.CanCollide = (saved ~= nil) and saved or (d.Name == "HumanoidRootPart")
end
end
noclipSaved = {}
end
end
local function enableFlight()
local root = getRoot()
if not root or flightVelocity then return end
pcall(function()
flightAttachment = Instance.new("Attachment", root)
flightVelocity = Instance.new("LinearVelocity")
flightVelocity.Attachment0 = flightAttachment
flightVelocity.VelocityConstraintMode = Enum.VelocityConstraintMode.Vector
flightVelocity.VectorVelocity = Vector3.zero
flightVelocity.MaxForce = math.huge
flightVelocity.Parent = root
end)
end
function disableFlight()
if flightVelocity then flightVelocity:Destroy(); flightVelocity = nil end
if flightAttachment then flightAttachment:Destroy(); flightAttachment = nil end
end
local function dropToGround()
local char = LocalPlayer.Character
local root = getRoot()
if not char or not root then return end
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { char }
local hit = workspace:Raycast(root.Position, Vector3.new(0, -1000, 0), params)
if hit then
root.CFrame = CFrame.new(hit.Position + Vector3.new(0, 3, 0), root.CFrame.LookVector + hit.Position)
end
local hum = char:FindFirstChildOfClass("Humanoid")
if hum then hum:ChangeState(Enum.HumanoidStateType.Freefall) end
end
local function groundYAt(x, z)
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { LocalPlayer.Character }
local hit = workspace:Raycast(Vector3.new(x, ORIGIN_Y + 500, z), Vector3.new(0, -1200, 0), params)
return hit and hit.Position.Y or ORIGIN_Y
end
-- camera see-through
local camIgnoreSaved = {}
local camWallFolder = nil
local camWallConn = nil
local camGhostConn = nil
local function camSetIgnore(part, ignore)
if not part or not part:IsA("BasePart") then return end
if ignore then
if camIgnoreSaved[part] == nil then camIgnoreSaved[part] = part.CanQuery end
part.CanQuery = false
else
local saved = camIgnoreSaved[part]
if saved ~= nil then
part.CanQuery = saved
camIgnoreSaved[part] = nil
end
end
end
local function camScanFolder(folder)
if not folder then return end
for _, d in ipairs(folder:GetDescendants()) do
if d:IsA("BasePart") then camSetIgnore(d, true) end
end
end
local function cameraSeeThroughEnable()
for _, part in ipairs(CollectionService:GetTagged("WallGhost")) do
camSetIgnore(part, true)
end
if not camGhostConn then
camGhostConn = CollectionService:GetInstanceAddedSignal("WallGhost"):Connect(function(p)
if CFG.cameraSeeThrough then camSetIgnore(p, true) end
end)
end
camWallFolder = workspace:FindFirstChild("GreatWall")
if camWallFolder then
camScanFolder(camWallFolder)
if not camWallConn then
camWallConn = camWallFolder.DescendantAdded:Connect(function(d)
if CFG.cameraSeeThrough and d:IsA("BasePart") then camSetIgnore(d, true) end
end)
end
end
end
local function cameraSeeThroughDisable()
for part, saved in pairs(camIgnoreSaved) do
if part and part.Parent then part.CanQuery = saved end
end
camIgnoreSaved = {}
if camWallConn then camWallConn:Disconnect(); camWallConn = nil end
if camGhostConn then camGhostConn:Disconnect(); camGhostConn = nil end
camWallFolder = nil
end
local function phaseBlocksPlacement()
if GreatWall:GetAttribute("Phase") == "Event" then return true, "event active" end
if GreatWall:GetAttribute("JadeEventAt") ~= nil then return true, "jade event" end
if LocalPlayer:GetAttribute("JadePreviewAt") ~= nil then return true, "jade preview" end
return false, nil
end
-- ============================================================
-- Input β E key
-- ============================================================
local m1Held = false
local VK_E = 0x45
local function simulateM1Down()
if type(keypress) ~= "function" then return false end
local ok = pcall(keypress, VK_E)
return ok
end
local function simulateM1Up()
if type(keyrelease) == "function" then pcall(keyrelease, VK_E) end
end
local function setM1Held(on)
if on and not m1Held then
if simulateM1Down() then m1Held = true end
elseif not on and m1Held then
simulateM1Up(); m1Held = false
end
end
-- ============================================================
-- Anti-AFK
-- ============================================================
local antiAfkConn = nil
local function vUserClick()
if VirtualUser then
local cam = workspace.CurrentCamera
if cam then
pcall(function()
VirtualUser:Button2Down(Vector2.new(0, 0), cam.CFrame)
task.wait(0.05)
VirtualUser:Button2Up(Vector2.new(0, 0), cam.CFrame)
end)
end
return
end
local VIM = game:GetService("VirtualInputManager")
if VIM then
pcall(function()
VIM:SendMouseButtonEvent(0, 0, 1, true, game, 0)
task.wait(0.05)
VIM:SendMouseButtonEvent(0, 0, 1, false, game, 0)
end)
end
end
local function enableAntiAfk()
if antiAfkConn then return end
antiAfkConn = LocalPlayer.Idled:Connect(function()
vUserClick()
log("anti-afk: idled, sent input")
end)
end
local function disableAntiAfk()
if antiAfkConn then antiAfkConn:Disconnect(); antiAfkConn = nil end
end
if CFG.antiAfk then enableAntiAfk() end
-- ============================================================
-- Visualization
-- ============================================================
local HIGHLIGHT_POOL_SIZE = 6
local HIGHLIGHT_HOLD = 0.5
local hlPool, hlPoolIdx, hlFolder = {}, 1, nil
local function ensureHighlightPool()
if #hlPool > 0 and hlPool[1] and hlPool[1].Parent then return end
local cam = workspace.CurrentCamera
if not cam then return end
if not hlFolder or not hlFolder.Parent then
hlFolder = Instance.new("Folder")
hlFolder.Name = "AutoPlace_Highlights"
hlFolder.Parent = cam
end
hlPool = {}
for i = 1, HIGHLIGHT_POOL_SIZE do
local h = Instance.new("Highlight")
h.Name = "AP_HL_" .. i
h.FillColor = CFG.placeHighlightColor
h.FillTransparency = 0.5
h.OutlineColor = CFG.placeHighlightColor
h.OutlineTransparency = 0
h.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop
h.Adornee = nil
h.Enabled = false
h.Parent = hlFolder
hlPool[i] = h
end
end
local function destroyHighlightPool()
if hlFolder then hlFolder:Destroy(); hlFolder = nil end
hlPool = {}
end
local function showPlaceHighlight(ghost)
if not CFG.showPlaceHighlight or not ghost or not ghost.Parent then return end
ensureHighlightPool()
if #hlPool == 0 then return end
local h = hlPool[hlPoolIdx]
if not h or not h.Parent then
ensureHighlightPool()
h = hlPool[hlPoolIdx]
if not h or not h.Parent then return end
end
hlPoolIdx = (hlPoolIdx % HIGHLIGHT_POOL_SIZE) + 1
h.Enabled = false
h.FillColor = CFG.placeHighlightColor
h.OutlineColor = CFG.placeHighlightColor
h.Adornee = ghost
h.Enabled = true
task.delay(HIGHLIGHT_HOLD, function()
if h and h.Parent and h.Adornee == ghost then
h.Enabled = false
h.Adornee = nil
end
end)
end
local fireRing = nil
local function destroyFireRing()
if fireRing then fireRing:Destroy(); fireRing = nil end
end
local function updateFireRing()
if not CFG.showFireRing or not CFG.running then destroyFireRing(); return end
local root = getRoot()
if not root then destroyFireRing(); return end
if not fireRing then
fireRing = Instance.new("Part")
fireRing.Name = "AutoPlace_FireRing"
fireRing.Shape = Enum.PartType.Cylinder
fireRing.Material = Enum.Material.Neon
fireRing.CanCollide = false
fireRing.CanQuery = false
fireRing.CanTouch = false
fireRing.CastShadow = false
fireRing.Anchored = true
fireRing.Parent = workspace
end
local r = CFG.fireRadius
fireRing.Size = Vector3.new(0.15, r * 2, r * 2)
fireRing.CFrame = CFrame.new(root.Position) * CFrame.Angles(0, 0, math.rad(90))
fireRing.Color = CFG.fireRingColor
fireRing.Transparency = 0.75
end
-- ============================================================
-- Training
-- ============================================================
local trainState = {
lastSpeedAnchor = 0,
lastStrengthFire = 0,
strengthEngaged = false,
currentArea = nil,
}
local function partOf(obj)
if not obj then return nil end
if obj:IsA("BasePart") then return obj end
if obj:IsA("Model") then
return obj.PrimaryPart or obj:FindFirstChildWhichIsA("BasePart", true)
end
return nil
end
local function getWallAmount()
local gui = LocalPlayer.PlayerGui
local left = gui and gui:FindFirstChild("Left")
local inner = left and left:FindFirstChild("Left")
local label = inner and inner:FindFirstChild("WallAmount")
if not label then return 0 end
return tonumber(label.Text:match("%d+")) or 0
end
local function getTrainingRequirement(plot)
local anchor = plot:FindFirstChild("LabelAnchor")
if not anchor then return 0 end
local bb = anchor:FindFirstChildWhichIsA("BillboardGui", true)
if not bb then return 0 end
local req = bb:FindFirstChild("Requirement", true)
if not req then return 0 end
return tonumber(req.Text:match("%d+")) or 0
end
local function getBestTrainingArea()
local areaRoot = workspace:FindFirstChild("TrainingArea")
if not areaRoot then return nil end
local wall = getWallAmount()
local best = nil
local bestReq = -1
for _, v in ipairs(areaRoot:GetDescendants()) do
if v:IsA("Model")
and v:FindFirstChild("Treadmill")
and v:FindFirstChild("BenchPress")
and v:FindFirstChild("LabelAnchor")
then
local req = getTrainingRequirement(v)
if wall >= req and req > bestReq then
best = v
bestReq = req
end
end
end
if not best then
best = areaRoot:FindFirstChild("Plot_Free")
end
trainState.currentArea = best
return best
end
local function getBenchPrompt(plot)
local bench = plot and plot:FindFirstChild("BenchPress")
if not bench then return nil, nil end
local prompt = bench:FindFirstChildWhichIsA("ProximityPrompt", true)
return bench, prompt
end
local function getTreadmill(plot)
return plot and plot:FindFirstChild("Treadmill")
end
local function firePrompt(prompt)
if not prompt then return false end
if type(fireproximityprompt) == "function" then
local ok = pcall(fireproximityprompt, prompt)
if ok then return true end
end
local ok = pcall(function()
if prompt.HoldDuration and prompt.HoldDuration > 0 then
prompt:InputHoldBegin()
task.wait(prompt.HoldDuration + 0.05)
prompt:InputHoldEnd()
else
prompt:InputHoldBegin()
task.wait(0.05)
prompt:InputHoldEnd()
end
end)
return ok
end
local function teleportNear(obj, offset)
local root = getRoot()
if not root or not obj then return end
local part = partOf(obj)
if not part then return end
local base = part.Position
local off = offset or Vector3.new(0, 3, 4)
root.CFrame = CFrame.new(base + off, base)
root.AssemblyLinearVelocity = Vector3.zero
root.AssemblyAngularVelocity = Vector3.zero
end
local function teleportOnto(obj, heightOffset)
local root = getRoot()
if not root or not obj then return end
local part = partOf(obj)
if not part then return end
local top = part.Position + Vector3.new(0, (part.Size.Y / 2) + (heightOffset or 3), 0)
root.CFrame = CFrame.new(top, top + root.CFrame.LookVector)
root.AssemblyLinearVelocity = Vector3.zero
root.AssemblyAngularVelocity = Vector3.zero
end
local function runTraining()
if not (CFG.trainStrength or CFG.trainSpeed) then return false end
local plot = trainState.currentArea
if not plot or not plot.Parent then
plot = getBestTrainingArea()
end
if not plot then return false end
local now = os.clock()
local did = false
if CFG.trainSpeed then
local treadmill = getTreadmill(plot)
local tPart = partOf(treadmill)
if tPart then
if now - trainState.lastSpeedAnchor > 1.0 then
trainState.lastSpeedAnchor = now
disableFlight()
teleportOnto(treadmill, CFG.trainSpeedAnchorOffset)
did = true
end
end
end
if CFG.trainStrength then
local bench, prompt = getBenchPrompt(plot)
local benchPart = partOf(bench)
if benchPart then
local root = getRoot()
if root then
local dist = (root.Position - benchPart.Position).Magnitude
if (not trainState.strengthEngaged) or dist > CFG.trainReengageDist then
if dist > 8 then
disableFlight()
teleportNear(bench, Vector3.new(0, 3, 4))
task.wait(0.15)
end
if prompt then
firePrompt(prompt)
trainState.strengthEngaged = true
end
did = true
end
end
end
end
return did
end
local function resetTrainingEngagement()
trainState.strengthEngaged = false
end
-- ============================================================
-- Boost zone
-- ============================================================
local hsState = {
isOpen=false, secondsLeft=0, multiplier=1,
wallEnd=0, gymEnd=0,
wallActive=false, gymActive=false,
wallMult=1, gymMult=1,
hasZone=false, zoneCFrame=nil,
}
local function refreshHotSprings()
local now = workspace:GetServerTimeNow()
local wallEnd = GreatWall:GetAttribute("WallBoostEnd") or 0
local gymEnd = GreatWall:GetAttribute("GymBoostEnd") or 0
local wallActive = wallEnd > now
local gymActive = gymEnd > now
local wallMult = wallActive and (GreatWall:GetAttribute("WallBoostMult") or 2) or 1
local gymMult = gymActive and (GreatWall:GetAttribute("GymBoostMult") or 2) or 1
hsState.wallEnd, hsState.gymEnd = wallEnd, gymEnd
hsState.wallActive, hsState.gymActive = wallActive, gymActive
hsState.wallMult, hsState.gymMult = wallMult, gymMult
local maxEnd = math.max(wallEnd, gymEnd)
hsState.secondsLeft = math.max(0, maxEnd - now)
hsState.isOpen = maxEnd > now
hsState.multiplier = hsState.isOpen and math.max(wallMult, gymMult) or 1
local zone = workspace:FindFirstChild("HotSprings")
zone = zone and zone:FindFirstChild("RelaxZone")
if zone and zone:IsA("BasePart") then
hsState.hasZone = true
hsState.zoneCFrame = zone.CFrame
else
hsState.hasZone = false
end
end
local function formatClock(sec)
sec = math.max(0, math.floor(sec or 0))
return ("%d:%02d"):format(sec // 60, sec % 60)
end
local hsInSprings, hsSavedCFrame, hsEnteredAt, hsReason = false, nil, 0, nil
local function teleportToHotSprings(reason)
if not hsState.hasZone then return false end
local root = getRoot()
if not root then return false end
hsSavedCFrame = root.CFrame
local target = hsState.zoneCFrame.Position + Vector3.new(0, CFG.hsTeleportYOffset, 0)
root.CFrame = CFrame.new(target, target + root.CFrame.LookVector)
hsInSprings = true
hsEnteredAt = os.clock()
hsReason = reason or "manual"
return true
end
local function teleportBackFromHotSprings()
local root = getRoot()
if not root then hsInSprings = false; hsReason = nil; return end
if hsSavedCFrame then root.CFrame = hsSavedCFrame else dropToGround() end
hsInSprings = false
hsReason = nil
end
-- ============================================================
-- Wall Tiers
-- ============================================================
local function getTiers()
if not WallTiersModule or not WallTiersModule.List then return {} end
return WallTiersModule.List
end
local function currentTier() return Config and (tonumber(Config.Tier) or 1) or 1 end
local function tierName(i)
local t = getTiers()[i]
return t and (t.Name or t.Short or ("Wall " .. i)) or ("Wall " .. i)
end
local function tierGymMult(i)
local t = getTiers()[i]
return t and t.Reward and t.Reward.Gym or 1
end
local function tierCoins(i)
local t = getTiers()[i]
return t and t.Reward and t.Reward.Coins or 0
end
local function tierPassesFilter()
if not CFG.tierFilterOn then return true end
return currentTier() >= CFG.tierFilterMin
end
-- ============================================================
-- Section markers
-- ============================================================
local SectionViz = {
enabled=false, folder=nil, markers={}, maxDistance=800, yBase=nil,
lastUpdate=0, UPDATE_INTERVAL=0.4, nearOnly=true, nearRadius=3,
hideComplete=false, scaleByDist=true, baseSizeX=160, baseSizeY=70,
}
local function targetLayers()
if Config and type(Config.WallHeight) == "number" then return Config.WallHeight end
local ti = Config and Config.TierInfo
if ti and type(ti.WallHeight) == "number" then return ti.WallHeight end
return 20
end
local function destroySectionViz()
if SectionViz.folder then SectionViz.folder:Destroy(); SectionViz.folder = nil end
SectionViz.markers = {}
end
local function buildSectionViz()
destroySectionViz()
if not STAGES then return end
local folder = Instance.new("Folder")
folder.Name = "AutoPlace_SectionViz"
folder.Parent = workspace
SectionViz.folder = folder
local bw, bh = SectionViz.baseSizeX, SectionViz.baseSizeY
for i, v in ipairs(STAGES) do
local part = Instance.new("Part")
part.Anchored = true; part.CanCollide = false
part.CanQuery = false; part.CanTouch = false
part.CastShadow = false; part.Transparency = 1
part.Size = Vector3.new(0.5, 0.5, 0.5)
part.Parent = folder
local bb = Instance.new("BillboardGui")
bb.Size = UDim2.fromOffset(bw, bh)
bb.AlwaysOnTop = true; bb.LightInfluence = 0
bb.MaxDistance = SectionViz.maxDistance
bb.Adornee = part; bb.Parent = part
local bg = Instance.new("Frame")
bg.Size = UDim2.fromScale(1, 1)
bg.BackgroundColor3 = Color3.fromRGB(22, 24, 32)
bg.BackgroundTransparency = 0.1; bg.BorderSizePixel = 0
bg.Parent = bb
Instance.new("UICorner", bg).CornerRadius = UDim.new(0, 6)
local stroke = Instance.new("UIStroke", bg)
stroke.Color = Color3.fromRGB(60, 65, 82); stroke.Thickness = 1.5
local nl = Instance.new("TextLabel")
nl.BackgroundTransparency = 1
nl.Position = UDim2.new(0, 0, 0, 3); nl.Size = UDim2.new(1, 0, 0, 15)
nl.Font = Enum.Font.GothamBold; nl.TextSize = 11
nl.TextColor3 = Color3.fromRGB(255, 226, 120)
nl.Text = ("[%d] %s"):format(i, v.name or "Section")
nl.Parent = bg
local sl = Instance.new("TextLabel")
sl.BackgroundTransparency = 1
sl.Position = UDim2.new(0, 0, 0, 19); sl.Size = UDim2.new(1, 0, 0, 13)
sl.Font = Enum.Font.GothamBold; sl.TextSize = 10
sl.TextColor3 = Color3.fromRGB(180, 190, 210)
sl.Text = "PENDING"; sl.Parent = bg
local barBg = Instance.new("Frame")
barBg.Position = UDim2.new(0, 8, 0, 34)
barBg.Size = UDim2.new(1, -16, 0, 9)
barBg.BackgroundColor3 = Color3.fromRGB(40, 44, 58)
barBg.BorderSizePixel = 0; barBg.Parent = bg
Instance.new("UICorner", barBg).CornerRadius = UDim.new(0, 3)
local barFill = Instance.new("Frame")
barFill.Size = UDim2.fromScale(0, 1)
barFill.BackgroundColor3 = Color3.fromRGB(80, 160, 240)
barFill.BorderSizePixel = 0; barFill.Parent = barBg
Instance.new("UICorner", barFill).CornerRadius = UDim.new(0, 3)
local pl = Instance.new("TextLabel")
pl.BackgroundTransparency = 1
pl.Position = UDim2.new(0, 0, 0, 46); pl.Size = UDim2.new(1, 0, 0, 15)
pl.Font = Enum.Font.Code; pl.TextSize = 10
pl.TextColor3 = Color3.fromRGB(200, 210, 230)
pl.Text = "L0 / 20 (0%)"; pl.Parent = bg
SectionViz.markers[i] = { part=part, bb=bb, stroke=stroke, name=nl, status=sl, barFill=barFill, pct=pl }
end
end
local function updateSectionViz()
if not SectionViz.enabled or not SectionViz.folder then return end
local now = os.clock()
if now - SectionViz.lastUpdate < SectionViz.UPDATE_INTERVAL then return end
SectionViz.lastUpdate = now
local cam = workspace.CurrentCamera
local camPos = cam and cam.CFrame.Position
local currentStage = GreatWall:GetAttribute("Stage") or 1
local wallHeight = targetLayers()
local wallBaseY = SectionViz.yBase or (ORIGIN_Y + 3)
local zFace = defaultZ()
local root = getRoot()
local playerSec = root and sectionOf(root.Position.X) or 0
for i, m in pairs(SectionViz.markers) do
if not m.part or not m.part.Parent then
SectionViz.markers[i] = nil
else
local v = STAGES and STAGES[i]
if v then
local c0, c1 = tonumber(v.x0) or 0, tonumber(v.x1) or 0
local cx = columnWorldX((c0 + c1) / 2)
local worldPos = Vector3.new(cx, wallBaseY, zFace)
m.part.CFrame = CFrame.new(worldPos)
local layerAttr = GreatWall:GetAttribute("S" .. i .. "Layer")
local current = tonumber(layerAttr) or 0
local target = wallHeight
local statusText, statusColor, barColor, isComplete
if target > 0 and current >= target then
statusText, statusColor = "COMPLETE", Color3.fromRGB(80, 220, 120)
barColor = Color3.fromRGB(80, 220, 120); isComplete = true
elseif current > 0 then
statusText, statusColor = "BUILDING", Color3.fromRGB(240, 200, 80)
barColor = Color3.fromRGB(240, 200, 80)
elseif i < currentStage then
statusText, statusColor = "COMPLETE", Color3.fromRGB(80, 220, 120)
barColor = Color3.fromRGB(80, 220, 120); current = target; isComplete = true
elseif i == currentStage then
statusText, statusColor = "BUILDING", Color3.fromRGB(240, 200, 80)
barColor = Color3.fromRGB(240, 200, 80)
else
statusText, statusColor = "PENDING", Color3.fromRGB(140, 150, 170)
barColor = Color3.fromRGB(80, 160, 240)
end
local show = true
if SectionViz.nearOnly and playerSec > 0 and math.abs(i - playerSec) > SectionViz.nearRadius then show = false end
if show and SectionViz.hideComplete and isComplete then show = false end
if show and camPos and SectionViz.maxDistance > 0 then
local dx, dy, dz = camPos.X - cx, camPos.Y - wallBaseY, camPos.Z - zFace
if dx*dx + dy*dy + dz*dz > SectionViz.maxDistance * SectionViz.maxDistance then show = false end
end
if m.bb.Enabled ~= show then m.bb.Enabled = show end
if show then
local scale = 1
if SectionViz.scaleByDist and camPos then
local d = (camPos - worldPos).Magnitude
scale = math.clamp(1 - (d - 80) / 700, 0.45, 1)
end
local sx, sy = SectionViz.baseSizeX * scale, SectionViz.baseSizeY * scale
if math.abs(m.bb.AbsoluteSize.X - sx) > 4 then
m.bb.Size = UDim2.fromOffset(sx, sy)
end
end
local pct = target > 0 and math.clamp(current / target, 0, 1) or 0
m.name.Text = ("[%d] %s"):format(i, v.name or "Section")
m.status.Text = statusText
m.status.TextColor3 = statusColor
m.barFill.Size = UDim2.fromScale(pct, 1)
m.barFill.BackgroundColor3 = barColor
m.pct.Text = ("L%d / %d (%d%%)"):format(current, target, math.floor(pct * 100 + 0.5))
if i == currentStage then
m.stroke.Color = Color3.fromRGB(255, 226, 120); m.stroke.Thickness = 2.5
else
m.stroke.Color = Color3.fromRGB(60, 65, 82); m.stroke.Thickness = 1.5
end
end
end
end
end
-- ============================================================
-- Ghost list
-- ============================================================
local function collectGhosts()
local out = {}
local now = os.clock()
for _, part in ipairs(CollectionService:GetTagged("WallGhost")) do
if part:IsA("BasePart") and part.Parent and not firedSet[part] then
local t = inflight[part]
if not (t and (now - t) < CFG.inflightTimeout) then
if t then inflight[part] = nil end
local s, l, i = part:GetAttribute("S"), part:GetAttribute("L"), part:GetAttribute("I")
if s and l and i then
out[#out + 1] = { part = part, s = s, l = l, i = i, pos = part.Position }
end
end
end
end
return out
end
function rebuildList()
sList, lList, iList, partList = {}, {}, {}, {}
local entries = collectGhosts()
local root = getRoot()
if root and #entries > 1 then
local pp = root.Position
for _, e in ipairs(entries) do
local dx, dy, dz = pp.X - e.pos.X, pp.Y - e.pos.Y, pp.Z - e.pos.Z
e.d2 = dx*dx + dy*dy + dz*dz
end
table.sort(entries, function(a, b) return a.d2 < b.d2 end)
end
for _, e in ipairs(entries) do
sList[#sList + 1] = e.s
lList[#lList + 1] = e.l
iList[#iList + 1] = e.i
partList[#partList + 1] = e.part
end
listDirty = false
end
CollectionService:GetInstanceAddedSignal("WallGhost"):Connect(function(p)
if p then
firedSet[p] = nil; inflight[p] = nil
if CFG.cameraSeeThrough then camSetIgnore(p, true) end
end
listDirty = true
if activeCluster and p and p:IsA("BasePart") and p.Parent then
local dx = p.Position.X - activeCluster.anchorPos.X
local dy = p.Position.Y - activeCluster.anchorPos.Y
local dz = p.Position.Z - activeCluster.anchorPos.Z
if dx*dx + dy*dy + dz*dz <= CFG.clusterRadius * CFG.clusterRadius then
rebuildClusterOrder()
end
end
end)
CollectionService:GetInstanceRemovedSignal("WallGhost"):Connect(function(p)
if p then firedSet[p] = nil; inflight[p] = nil end
listDirty = true
end)
local function pickNearest(ref, filter)
if not ref then return nil end
local best, bestD = nil, math.huge
for i = 1, #partList do
local g = partList[i]
if g and g.Parent and (not filter or filter(g)) then
local d = (g.Position - ref).Magnitude
if d < bestD then bestD = d; best = g end
end
end
return best
end
local function anyPasses(filter)
if not filter then return true end
for i = 1, #partList do
local g = partList[i]
if g and g.Parent and filter(g) then return true end
end
return false
end
local function modeFilterAndRef(playerPos)
if CFG.placeMode == "nearest" or not lastPlacedPos then return playerPos, nil end
local ref = lastPlacedPos
if CFG.placeMode == "section" then
local s = sectionOf(ref.X)
return ref, function(g) return sectionOf(g.Position.X) == s end
elseif CFG.placeMode == "column" then
local c = columnOf(ref.X)
return ref, function(g) return columnOf(g.Position.X) == c end
end
return playerPos, nil
end
local function clusterGhosts(anchorPos)
local CR = CFG.clusterRadius
local cr2 = CR * CR
local out = {}
for i = 1, #partList do
local g = partList[i]
if g and g.Parent then
local dx = g.Position.X - anchorPos.X
local dy = g.Position.Y - anchorPos.Y
local dz = g.Position.Z - anchorPos.Z
if dx*dx + dy*dy + dz*dz <= cr2 then out[#out + 1] = g end
end
end
return out
end
local function clusterInflightCount(anchorPos)
local CR = CFG.clusterRadius
local cr2 = CR * CR
local now = os.clock()
local n = 0
for g, t in pairs(inflight) do
if g and g.Parent and (now - t) < CFG.inflightTimeout then
local dx = g.Position.X - anchorPos.X
local dy = g.Position.Y - anchorPos.Y
local dz = g.Position.Z - anchorPos.Z
if dx*dx + dy*dy + dz*dz <= cr2 then n = n + 1 end
end
end
return n
end
local function bfsOrder(ghosts, anchorPos)
if #ghosts == 0 then return {} end
local map = {}
for _, g in ipairs(ghosts) do
local c, l, z = cellOf(g)
map[keyOf(c, l, z)] = g
end
local startKey, bestD = nil, math.huge
for _, g in ipairs(ghosts) do
local d = (g.Position - anchorPos).Magnitude
if d < bestD then
bestD = d
local c, l, z = cellOf(g)
startKey = keyOf(c, l, z)
end
end
if not startKey then return ghosts end
local order = {}
local seen = { [startKey] = true }
local queue = { startKey }
local head = 1
while head <= #queue do
local key = queue[head]; head = head + 1
local g = map[key]
if g then order[#order + 1] = g end
local c, l, z = key:match("^(%-?%d+):(%-?%d+):(%-?%d+)$")
if c then
c, l, z = tonumber(c), tonumber(l), tonumber(z)
local nbrs = { keyOf(c+1, l, z), keyOf(c-1, l, z), keyOf(c, l+1, z), keyOf(c, l-1, z) }
for _, nk in ipairs(nbrs) do
if not seen[nk] and map[nk] then
seen[nk] = true
queue[#queue + 1] = nk
end
end
end
end
for _, g in ipairs(ghosts) do
local c, l, z = cellOf(g)
local k = keyOf(c, l, z)
if not seen[k] then
seen[k] = true
order[#order + 1] = g
end
end
return order
end
function rebuildClusterOrder()
if not activeCluster then return end
local prevIdx = activeCluster.orderIdx or 1
local ghosts = clusterGhosts(activeCluster.anchorPos)
activeCluster.order = bfsOrder(ghosts, activeCluster.anchorPos)
-- keep progress: advance past any ghosts that are already fired/inflight
local idx = 1
while idx <= #activeCluster.order
and (firedSet[activeCluster.order[idx]] or inflight[activeCluster.order[idx]])
do
idx = idx + 1
end
activeCluster.orderIdx = math.min(idx, #activeCluster.order + 1)
end
-- ============================================================
-- Server feedback
-- ============================================================
local pausedUntil = 0
local currentRate = CFG.rate
local lastRegrow = os.clock()
local Fx = Remotes:WaitForChild("Fx", 5)
if Fx then
Fx.OnClientEvent:Connect(function(kind, a)
if kind == "SlowDown" then
pausedUntil = os.clock() + 1.0
if CFG.adaptive then
currentRate = math.max(CFG.minRate, math.floor(currentRate * 0.6))
end
elseif kind == "PlaceAck" then
local rec = pendingFires[a]
if rec then
onAck()
if rec.ghost then
inflight[rec.ghost] = nil
firedSet[rec.ghost] = true
end
if rec.pos then lastPlacedPos = rec.pos end
pendingFires[a] = nil
end
end
end)
end
-- ============================================================
-- Refill
-- ============================================================
local refilling = false
local function readCarry()
local gui = LocalPlayer.PlayerGui
local left = gui and gui:FindFirstChild("Left")
local label
if left then
local inner = left:FindFirstChild("Left") or left:FindFirstChild("Inner")
if inner then label = inner:FindFirstChild("CarryAmount") end
end
if not label then return 0, 0 end
local a, b = label.Text:match("(%d+)/(%d+)")
return tonumber(a) or 0, tonumber(b) or 0
end
local function groundUnder(pos)
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { LocalPlayer.Character }
local hit = workspace:Raycast(pos, Vector3.new(0, -500, 0), params)
return hit and hit.Position or pos - Vector3.new(0, 10, 0)
end
local function refillBlocks()
if refilling then return end
local root = getRoot()
if not root or not Fill then return end
local quarry = workspace:FindFirstChild("Quarry")
local anchor = quarry and quarry:FindFirstChild("TitleAnchor")
if not anchor then return end
refilling = true
local saved = root.CFrame
local hadFlight = flightVelocity ~= nil
if hadFlight then disableFlight() end
local dest = groundUnder(anchor.Position) + Vector3.new(0, CFG.refillGroundY, 0)
root.CFrame = CFrame.new(dest, dest + root.CFrame.LookVector)
task.wait(0.35)
Fill:FireServer(1, true)
task.wait(0.1)
local start = os.clock()
repeat
task.wait(CFG.refillInterval)
Fill:FireServer(1, false)
local cur, max = readCarry()
if cur >= max and max > 0 then break end
if os.clock() - start > 20 then break end
until false
root.CFrame = saved
if hadFlight then enableFlight() end
refilling = false
end
-- ============================================================
-- Token bucket
-- ============================================================
local tokenBucket = 0
local function primeBucket() tokenBucket = math.min(currentRate * 0.05, 6) end
local function refillTokens(dt) tokenBucket = math.min(currentRate * 0.5, tokenBucket + currentRate * dt) end
local function spendToken()
if tokenBucket >= 1 then tokenBucket = tokenBucket - 1; return true end
return false
end
-- ============================================================
-- Fire routines (remote / internal / hybrid aware)
-- ============================================================
local function fireInRange()
local root = getRoot()
if not root then return 0, false end
-- key-only: game's own client does the firing, nothing for us
if CFG.placeInput == "key" then
return (#partList > 0) and 1 or 0, #partList > 0
end
local effR2 = CFG.fireRadius * CFG.fireRadius
local origin = root.Position
local fired = 0
local hasInRange = false
local useInternal = (CFG.placeInput == "internal" and clientPlaceInto ~= nil)
local useRemote = (CFG.placeInput == "remote" or CFG.placeInput == "hybrid")
local cap = (CFG.placeInput == "hybrid") and 4 or MAX_FIRES_PER_TICK
local i = 1
while i <= #partList do
if fired >= cap then break end
local g = partList[i]
if not g or not g.Parent then
table.remove(partList, i); table.remove(sList, i); table.remove(lList, i); table.remove(iList, i)
else
local dx = g.Position.X - origin.X
local dy = g.Position.Y - origin.Y
local dz = g.Position.Z - origin.Z
local d2 = dx*dx + dy*dy + dz*dz
if d2 > effR2 then i = i + 1
else
hasInRange = true
if not spendToken() then i = i + 1
else
local success = false
if useInternal then
success = pcall(clientPlaceInto, g)
elseif useRemote then
seq = seq + 1
Place:FireServer(sList[i], lList[i], iList[i], seq)
inflight[g] = os.clock()
pendingFires[seq] = { ghost = g, pos = g.Position, dist = math.sqrt(d2), at = os.clock() }
success = true
end
if success then
fired = fired + 1
onFire()
showPlaceHighlight(g)
table.remove(partList, i); table.remove(sList, i); table.remove(lList, i); table.remove(iList, i)
else
i = i + 1
end
end
end
end
end
return fired, hasInRange
end
local function fireClusterOrdered()
if not activeCluster then return 0, false, nil end
if CFG.placeInput == "key" then
return (#partList > 0) and 1 or 0, false, nil
end
local order = activeCluster.order
local idx = activeCluster.orderIdx
local root = getRoot()
if not root then return 0, false, nil end
local origin = root.Position
local r2 = CFG.fireRadius * CFG.fireRadius
local fired = 0
local nextOutOfRange = nil
local useInternal = (CFG.placeInput == "internal" and clientPlaceInto ~= nil)
local useRemote = (CFG.placeInput == "remote" or CFG.placeInput == "hybrid")
local cap = (CFG.placeInput == "hybrid") and 4 or MAX_FIRES_PER_TICK
while idx <= #order and fired < cap do
local g = order[idx]
if not g or not g.Parent then idx = idx + 1
elseif firedSet[g] or inflight[g] then idx = idx + 1
else
local dx = g.Position.X - origin.X
local dy = g.Position.Y - origin.Y
local dz = g.Position.Z - origin.Z
local d2 = dx*dx + dy*dy + dz*dz
if d2 > r2 then nextOutOfRange = g; break end
if not spendToken() then nextOutOfRange = g; break end
local success = false
if useInternal then
success = pcall(clientPlaceInto, g)
elseif useRemote then
seq = seq + 1
Place:FireServer(g:GetAttribute("S"), g:GetAttribute("L"), g:GetAttribute("I"), seq)
inflight[g] = os.clock()
pendingFires[seq] = { ghost = g, pos = g.Position, dist = math.sqrt(d2), at = os.clock() }
success = true
end
if success then
fired = fired + 1
onFire()
showPlaceHighlight(g)
end
idx = idx + 1
end
end
activeCluster.orderIdx = idx
return fired, nextOutOfRange ~= nil, nextOutOfRange
end
-- ============================================================
-- State machine
-- ============================================================
local phase = "idle"
local cycleTarget = nil
local settleUntil = 0
local function applyMovementState()
if CFG.running then
if CFG.noclip then setNoclip(true) end
if CFG.useFlight then enableFlight() else disableFlight() end
cameraSeeThroughEnable()
primeBucket()
if CFG.placeInput == "key" or CFG.placeInput == "hybrid" then setM1Held(true) end
else
setM1Held(false)
disableFlight()
dropToGround()
setNoclip(false)
cameraSeeThroughDisable()
destroyHighlightPool()
destroyFireRing()
activeCluster = nil
resetTrainingEngagement()
end
end
LocalPlayer.CharacterAdded:Connect(function()
noclipSaved = {}
flightAttachment, flightVelocity = nil, nil
task.wait(0.5)
resetTrainingEngagement()
if CFG.running then applyMovementState() end
end)
if CFG.cameraSeeThrough then cameraSeeThroughEnable() end
local AUTO = {
settleTime=0.12, lastAdapt=0, ADAPT_PERIOD=2.0,
MIN_SETTLE=0.06, MAX_SETTLE=0.5, STEP=0.03,
}
local function adaptTuning(now, ackRatio)
if now - AUTO.lastAdapt < AUTO.ADAPT_PERIOD then return end
AUTO.lastAdapt = now
if ackRatio and ackRatio < 0.85 then
AUTO.settleTime = math.min(AUTO.MAX_SETTLE, AUTO.settleTime + AUTO.STEP)
elseif (not ackRatio or ackRatio > 0.97) then
AUTO.settleTime = math.max(AUTO.MIN_SETTLE, AUTO.settleTime - AUTO.STEP)
end
end
local function regrowRate(now)
if analytics.lastFireAt > 0 and now - analytics.lastFireAt > 2.0 then
if currentRate < CFG.rate then currentRate = CFG.rate end
end
if now - lastRegrow > CFG.regrowEvery then
lastRegrow = now
if currentRate < CFG.rate then
currentRate = math.min(CFG.rate, currentRate + math.max(10, math.floor(CFG.rate * 0.25)))
end
end
end
local function hotSpringsTick(now)
refreshHotSprings()
if hsInSprings then
if hsReason == "manual" then
if not hsState.isOpen and CFG.hsAuto then teleportBackFromHotSprings() end
return true
end
local stayTime = now - hsEnteredAt
local canReturn = stayTime >= CFG.hsStayMin
local expiring = hsState.secondsLeft <= CFG.hsReturnBefore
local closed = not hsState.isOpen
if closed or (expiring and canReturn) then teleportBackFromHotSprings() end
return true
end
if CFG.hsAuto and hsState.isOpen then teleportToHotSprings("auto"); return true end
return false
end
-- ============================================================
-- Rayfield UI
-- ============================================================
local function safeCreateTab(name, icon)
if not Window then return nil end
if type(Window.CreateTab) == "function" then
local ok, t = pcall(Window.CreateTab, Window, name, icon)
if ok and t then return t end
local ok2, t2 = pcall(Window.CreateTab, Window, name)
if ok2 and t2 then return t2 end
end
if type(Window.Tab) == "function" then
local ok, t = pcall(Window.Tab, Window, name)
if ok and t then return t end
end
return nil
end
local BuildingTab = safeCreateTab("Building", 4483362458)
local TrainingTab = safeCreateTab("Training", 4483362458)
local StatsTab = safeCreateTab("Stats", 4483362458)
local WallsTab = safeCreateTab("Walls", 4483362458)
local VisualsTab = safeCreateTab("Visuals", 4483362458)
local FALLBACKS = {
CreateParagraph = { "CreateParagraph", "CreateLabel", "Paragraph", "Label" },
CreateDropdown = { "CreateDropdown", "Dropdown" },
CreateSlider = { "CreateSlider", "Slider" },
CreateToggle = { "CreateToggle", "Toggle" },
CreateButton = { "CreateButton", "Button" },
CreateSection = { "CreateSection", "Section" },
CreateColorPicker = { "CreateColorPicker", "ColorPicker" },
}
local function UI(tab, method, cfg)
if not tab then return nil end
local names = FALLBACKS[method] or { method }
for _, m in ipairs(names) do
local fn = tab[m]
if type(fn) == "function" then
local ok, res = pcall(fn, tab, cfg)
if ok then return res end
end
end
return nil
end
local function makeParagraph(tab, title, content)
local p = UI(tab, "CreateParagraph", { Title = title, Content = content })
if p and type(p.Set) == "function" then return p end
return nil
end
-- ---- Building tab ----
UI(BuildingTab, "CreateSection", "Main Control")
UI(BuildingTab, "CreateToggle", {
Name = "Start auto-building",
Tooltip = "Turn the bot on or off.",
CurrentValue = false, Flag = "Enabled",
Callback = function(v)
CFG.running = v
applyMovementState()
log("building " .. (v and "started" or "stopped"))
end,
})
UI(BuildingTab, "CreateSection", "Character")
UI(BuildingTab, "CreateToggle", {
Name = "Walk through walls",
Tooltip = "Disable collision so you can move freely along the wall.",
CurrentValue = CFG.noclip, Flag = "Noclip",
Callback = function(v) CFG.noclip = v; if CFG.running then setNoclip(v) end end,
})
UI(BuildingTab, "CreateToggle", {
Name = "Camera sees through blocks",
Tooltip = "Wall blocks ignore the camera raycast so the default camera doesn't pull in.",
CurrentValue = CFG.cameraSeeThrough, Flag = "CamSeeThrough",
Callback = function(v)
CFG.cameraSeeThrough = v
if v then cameraSeeThroughEnable() else cameraSeeThroughDisable() end
end,
})
UI(BuildingTab, "CreateToggle", {
Name = "Hover in the air",
Tooltip = "Stops gravity so you can build from any height.",
CurrentValue = CFG.useFlight, Flag = "UseFlight",
Callback = function(v)
CFG.useFlight = v
if CFG.running then if v then enableFlight() else disableFlight() end end
end,
})
UI(BuildingTab, "CreateToggle", {
Name = "Anti-AFK",
Tooltip = "Prevents Roblox from kicking you for being idle.",
CurrentValue = CFG.antiAfk, Flag = "AntiAfk",
Callback = function(v)
CFG.antiAfk = v
if v then enableAntiAfk() else disableAntiAfk() end
end,
})
UI(BuildingTab, "CreateSection", "Fill Strategy")
UI(BuildingTab, "CreateDropdown", {
Name = "Ordering",
Tooltip = "How to pick which ghost to fill next.",
Options = { "Closest first", "Whole section", "One column" },
CurrentOption = { "Closest first" }, Flag = "PlaceMode",
Callback = function(opt)
if type(opt) ~= "table" or not opt[1] then return end
local map = { ["Closest first"] = "nearest", ["Whole section"] = "section", ["One column"] = "column" }
CFG.placeMode = map[opt[1]] or "nearest"
lastPlacedPos = nil; listDirty = true
end,
})
UI(BuildingTab, "CreateDropdown", {
Name = "Group size",
Tooltip = "Single = one ghost at a time. Cluster = fill a whole area before moving on.",
Options = { "Single ghost", "Cluster of ghosts" },
CurrentOption = { "Cluster of ghosts" }, Flag = "TargetMode",
Callback = function(opt)
if type(opt) ~= "table" or not opt[1] then return end
CFG.targetMode = (opt[1] == "Single ghost") and "single" or "cluster"
activeCluster = nil
end,
})
UI(BuildingTab, "CreateDropdown", {
Name = "Travel method",
Tooltip = "How the character moves between targets.",
Options = { "Instant teleport", "Fast walk", "Stay still" },
CurrentOption = { "Instant teleport" }, Flag = "MoveMode",
Callback = function(opt)
if type(opt) ~= "table" or not opt[1] then return end
local map = { ["Instant teleport"] = "teleport", ["Fast walk"] = "step", ["Stay still"] = "off" }
CFG.moveMode = map[opt[1]] or "teleport"
end,
})
UI(BuildingTab, "CreateSection", "Placement Method")
UI(BuildingTab, "CreateDropdown", {
Name = "How to place blocks",
Tooltip = "Direct = fastest. Game controls = safest. Game function = fallback. Hybrid = both at once.",
Options = { "Direct (remote)", "Game controls (E key)", "Game function", "Hybrid (remote + E key)" },
CurrentOption = { "Hybrid (remote + E key)" }, Flag = "PlaceInput",
Callback = function(opt)
if type(opt) ~= "table" or not opt[1] then return end
local map = {
["Direct (remote)"] = "remote",
["Game controls (E key)"] = "key",
["Game function"] = "internal",
["Hybrid (remote + E key)"] = "hybrid",
}
local newMode = map[opt[1]] or "hybrid"
if newMode == CFG.placeInput then return end
if (CFG.placeInput == "key" or CFG.placeInput == "hybrid") and m1Held then
setM1Held(false)
end
CFG.placeInput = newMode
log("method -> " .. newMode)
if CFG.running and (newMode == "key" or newMode == "hybrid") then
setM1Held(true)
end
end,
})
UI(BuildingTab, "CreateSection", "Speed & Range")
UI(BuildingTab, "CreateSlider", {
Name = "Blocks per second",
Tooltip = "Higher = faster but server may reject.",
Range = { 20, 2000 }, Increment = 20,
CurrentValue = CFG.rate, Flag = "Rate",
Callback = function(v) CFG.rate = v; currentRate = v; primeBucket() end,
})
UI(BuildingTab, "CreateSlider", {
Name = "Reach",
Tooltip = "How far you can place blocks.",
Range = { 5, 150 }, Increment = 5, Suffix = " studs",
CurrentValue = CFG.fireRadius, Flag = "FireRadius",
Callback = function(v) CFG.fireRadius = v end,
})
UI(BuildingTab, "CreateSlider", {
Name = "Cluster size",
Tooltip = "How wide a group of ghosts counts as one cluster.",
Range = { 5, 100 }, Increment = 5, Suffix = " studs",
CurrentValue = CFG.clusterRadius, Flag = "ClusterRadius",
Callback = function(v) CFG.clusterRadius = v end,
})
UI(BuildingTab, "CreateSlider", {
Name = "Tween speed",
Tooltip = "Only used in Fast walk mode.",
Range = { 50, 2000 }, Increment = 50, Suffix = " studs/s",
CurrentValue = CFG.stepSpeed, Flag = "StepSpeed",
Callback = function(v) CFG.stepSpeed = v end,
})
UI(BuildingTab, "CreateSection", "Refill")
UI(BuildingTab, "CreateToggle", {
Name = "Auto-refill from quarry",
Tooltip = "Travels to the quarry to top up on blocks when empty.",
CurrentValue = CFG.refillEnabled, Flag = "RefillEnabled",
Callback = function(v) CFG.refillEnabled = v end,
})
-- ---- Training tab ----
UI(TrainingTab, "CreateSection", "Auto Training")
UI(TrainingTab, "CreateParagraph", {
Title = "About",
Content = "Speed: stand on the treadmill. Strength: stand at the bench and fire its prompt once β the game's own trainer runs from there.",
})
UI(TrainingTab, "CreateToggle", {
Name = "Train strength",
Tooltip = "Fire the bench prompt once, then hold position. Re-fires if you drift away.",
CurrentValue = CFG.trainStrength, Flag = "TrainStrength",
Callback = function(v)
CFG.trainStrength = v
trainState.strengthEngaged = false
end,
})
UI(TrainingTab, "CreateToggle", {
Name = "Train speed",
Tooltip = "Stay anchored on the treadmill. Server grants stats while touching.",
CurrentValue = CFG.trainSpeed, Flag = "TrainSpeed",
Callback = function(v)
CFG.trainSpeed = v
trainState.lastSpeedAnchor = 0
end,
})
UI(TrainingTab, "CreateToggle", {
Name = "Pause building while training",
Tooltip = "Blocks won't register while training, so pause building.",
CurrentValue = CFG.trainPauseBuild, Flag = "TrainPause",
Callback = function(v) CFG.trainPauseBuild = v end,
})
UI(TrainingTab, "CreateSlider", {
Name = "Re-fire when this far away",
Tooltip = "If the character drifts this far from the bench, fire the prompt again.",
Range = { 5, 40 }, Increment = 1, Suffix = " studs",
CurrentValue = CFG.trainReengageDist, Flag = "TrainReengage",
Callback = function(v) CFG.trainReengageDist = v end,
})
UI(TrainingTab, "CreateSection", "Training Area")
local trainingInfoParagraph = makeParagraph(TrainingTab, "Current area", "Reading...")
UI(TrainingTab, "CreateButton", {
Name = "Refresh best area now",
Callback = function()
local area = getBestTrainingArea()
local txt
if area then
txt = ("Using: %s\nRequirement: %d Blocks built: %d"):format(
area.Name, getTrainingRequirement(area), getWallAmount())
else
txt = "(no training area found)"
end
if trainingInfoParagraph then
pcall(function()
trainingInfoParagraph:Set({ Title = "Current area", Content = txt })
end)
end
end,
})
UI(TrainingTab, "CreateSection", "Boost Zone")
local hsInfoParagraph = makeParagraph(TrainingTab, "Current boost", "Reading...")
UI(TrainingTab, "CreateButton", {
Name = "Go to boost zone",
Tooltip = "Teleport to the Hot Springs relaxation zone.",
Callback = function()
refreshHotSprings()
if not hsState.hasZone then log("boost zone not found"); return end
teleportToHotSprings("manual")
end,
})
UI(TrainingTab, "CreateButton", {
Name = "Come back",
Tooltip = "Return to where you were before.",
Callback = teleportBackFromHotSprings,
})
UI(TrainingTab, "CreateToggle", {
Name = "Auto-travel to boost zone",
Tooltip = "Travel to the boost zone whenever the wall completion boost is active.",
CurrentValue = CFG.hsAuto, Flag = "HsAuto",
Callback = function(v)
CFG.hsAuto = v
if not v and hsInSprings and hsReason == "auto" then teleportBackFromHotSprings() end
end,
})
-- ---- Stats tab ----
UI(StatsTab, "CreateSection", "Live Progress")
local progressParagraph = makeParagraph(StatsTab, "Status", "Starting...")
UI(StatsTab, "CreateSection", "Server Values")
UI(StatsTab, "CreateParagraph", {
Title = "About",
Content = "These numbers come from the game server.",
})
local liveParagraph = makeParagraph(StatsTab, "Current values", "Reading...")
-- ---- Walls tab ----
UI(WallsTab, "CreateSection", "Current Wall")
local tierInfoParagraph = makeParagraph(WallsTab, "Wall info", "Reading...")
UI(WallsTab, "CreateSection", "Your Position")
local positionParagraph = makeParagraph(WallsTab, "Position", "Reading...")
UI(WallsTab, "CreateSection", "Wall Selection Filter")
UI(WallsTab, "CreateParagraph", {
Title = "About",
Content = "Only run on walls of a certain type or higher.",
})
local wallNames = {}
do
local list = getTiers()
for i, t in ipairs(list) do
wallNames[i] = ("%d. %s"):format(i, t.Name or t.Short or ("Wall " .. i))
end
if #wallNames == 0 then wallNames[1] = "1. Default" end
end
UI(WallsTab, "CreateToggle", {
Name = "Filter by wall type",
CurrentValue = CFG.tierFilterOn, Flag = "TierFilter",
Callback = function(v) CFG.tierFilterOn = v end,
})
UI(WallsTab, "CreateDropdown", {
Name = "Minimum wall type",
Options = wallNames,
CurrentOption = { wallNames[CFG.tierFilterMin] or wallNames[1] }, Flag = "TierMin",
Callback = function(opt)
if type(opt) ~= "table" then return end
local n = tonumber(tostring(opt[1]):match("^(%d+)"))
if n then CFG.tierFilterMin = n end
end,
})
local sectionNames = {}
local function refreshSectionNames()
sectionNames = {}
if not STAGES then return end
for i, v in ipairs(STAGES) do
sectionNames[i] = ("%d. %s"):format(i, v.name or ("Section " .. i))
end
end
refreshSectionNames()
local function sectionCenterWorldX(idx)
local v = STAGES and STAGES[idx]
if not v then return nil end
local c0, c1 = tonumber(v.x0) or 0, tonumber(v.x1) or 0
return columnWorldX((c0 + c1) / 2)
end
local function teleportToSection(idx, yMode)
local v = STAGES and STAGES[idx]
if not v then return end
local root = getRoot()
if not root then return end
local refZ = root.Position.Z
if not refZ or refZ == 0 then refZ = defaultZ() end
local wx = sectionCenterWorldX(idx)
if not wx then return end
local targetY
if yMode == "top" then
local c0, c1 = tonumber(v.x0) or 0, tonumber(v.x1) or 0
local minX = columnWorldX(c0) - BLOCK * 0.5
local maxX = columnWorldX(c1) + BLOCK * 0.5
local bestY
for _, part in ipairs(CollectionService:GetTagged("WallGhost")) do
if part:IsA("BasePart") and part.Parent then
local px, py = part.Position.X, part.Position.Y
if px >= minX and px <= maxX and (not bestY or py > bestY) then bestY = py end
end
end
targetY = bestY and (bestY + BLOCK) or (groundYAt(wx, refZ) + 6)
else
targetY = groundYAt(wx, refZ) + 5
end
root.CFrame = CFrame.new(Vector3.new(wx, targetY, refZ))
end
UI(WallsTab, "CreateSection", "Quick Jump")
if #sectionNames > 0 then
local selectedSection = 1
UI(WallsTab, "CreateDropdown", {
Name = "Section",
Options = sectionNames,
CurrentOption = { sectionNames[1] }, Flag = "SectionPick",
Callback = function(opt)
if type(opt) ~= "table" then return end
local n = tonumber(tostring(opt[1]):match("^(%d+)"))
if n then selectedSection = n end
end,
})
UI(WallsTab, "CreateButton", {
Name = "Jump to section at ground level",
Callback = function() teleportToSection(selectedSection, "ground") end,
})
UI(WallsTab, "CreateButton", {
Name = "Jump to section at top of ghosts",
Callback = function() teleportToSection(selectedSection, "top") end,
})
end
UI(WallsTab, "CreateSection", "Section Markers")
UI(WallsTab, "CreateToggle", {
Name = "Show section markers",
CurrentValue = false, Flag = "SectionViz",
Callback = function(v)
SectionViz.enabled = v
if v then buildSectionViz() else destroySectionViz() end
end,
})
UI(WallsTab, "CreateToggle", {
Name = "Only show nearby sections",
CurrentValue = true, Flag = "VizNearOnly",
Callback = function(v) SectionViz.nearOnly = v end,
})
UI(WallsTab, "CreateSlider", {
Name = "Nearby range",
Range = { 1, 8 }, Increment = 1,
CurrentValue = 3, Flag = "VizNearRad",
Callback = function(v) SectionViz.nearRadius = v end,
})
UI(WallsTab, "CreateToggle", {
Name = "Hide finished sections",
CurrentValue = false, Flag = "VizHideDone",
Callback = function(v) SectionViz.hideComplete = v end,
})
UI(WallsTab, "CreateToggle", {
Name = "Shrink far markers",
CurrentValue = true, Flag = "VizScale",
Callback = function(v) SectionViz.scaleByDist = v end,
})
UI(WallsTab, "CreateButton", {
Name = "Hide all markers",
Callback = function() SectionViz.enabled = false; destroySectionViz() end,
})
-- ---- Visuals tab ----
UI(VisualsTab, "CreateSection", "Place Highlights")
UI(VisualsTab, "CreateToggle", {
Name = "Show highlights",
CurrentValue = CFG.showPlaceHighlight, Flag = "PlaceHl",
Callback = function(v)
CFG.showPlaceHighlight = v
if not v then destroyHighlightPool() end
end,
})
UI(VisualsTab, "CreateColorPicker", {
Name = "Highlight color",
Color = CFG.placeHighlightColor, Flag = "PlaceHlColor",
Callback = function(c) CFG.placeHighlightColor = c end,
})
UI(VisualsTab, "CreateSection", "Fire Radius")
UI(VisualsTab, "CreateToggle", {
Name = "Show fire radius ring",
CurrentValue = CFG.showFireRing, Flag = "FireRing",
Callback = function(v)
CFG.showFireRing = v
if not v then destroyFireRing() end
end,
})
UI(VisualsTab, "CreateColorPicker", {
Name = "Ring color",
Color = CFG.fireRingColor, Flag = "FireRingColor",
Callback = function(c) CFG.fireRingColor = c end,
})
-- ============================================================
-- Main loop
-- ============================================================
local lastDt = os.clock()
local lastInfoRefresh = 0
local lastAntiAfk = 0
local function tick()
local now = os.clock()
local dt = now - lastDt
lastDt = now
tickAnalytics()
updateSectionViz()
updateFireRing()
adaptTuning(now, recentAccuracy())
regrowRate(now)
-- periodic anti-afk in addition to Idled hook
if CFG.antiAfk and (now - lastAntiAfk) > 60 then
lastAntiAfk = now
vUserClick()
end
local trainingBusy = false
if CFG.trainStrength or CFG.trainSpeed then
trainingBusy = runTraining()
end
local pausedBySprings = hotSpringsTick(now)
if now - lastInfoRefresh > 1.0 then
lastInfoRefresh = now
if trainingInfoParagraph and type(trainingInfoParagraph.Set) == "function" then
local area = trainState.currentArea or getBestTrainingArea()
local txt
if not area then
txt = "(no training area found)"
else
txt = ("Using: %s\nRequirement: %d Blocks built: %d"):format(
area.Name, getTrainingRequirement(area), getWallAmount())
end
pcall(function()
trainingInfoParagraph:Set({ Title = "Current area", Content = txt })
end)
end
if hsInfoParagraph and type(hsInfoParagraph.Set) == "function" then
local status
if hsState.isOpen then
status = ("Open β %sx boost, ends in %s"):format(
tostring(hsState.multiplier), formatClock(hsState.secondsLeft))
else
status = "Closed β finish the wall to open it"
end
pcall(function()
hsInfoParagraph:Set({ Title = "Current boost", Content = status })
end)
end
if tierInfoParagraph and type(tierInfoParagraph.Set) == "function" then
local i = currentTier()
pcall(function()
tierInfoParagraph:Set({ Title = "Wall info", Content =
("Now building: %s\nGym boost: %.2fx Coin reward: %d"):format(
tierName(i), tierGymMult(i), tierCoins(i)) })
end)
end
if liveParagraph and type(liveParagraph.Set) == "function" then
local list = allLiveAttrs()
local lines = {}
for _, item in ipairs(list) do
local pretty = item.key
:gsub("PlaceRange", "Reach")
:gsub("PlaceCooldown", "Cooldown")
:gsub("Capacity", "Bag capacity")
:gsub("Blocks", "Blocks carried")
lines[#lines + 1] = ("%s: %s"):format(pretty, tostring(item.value))
end
if #lines == 0 then lines[1] = "(no values yet)" end
pcall(function()
liveParagraph:Set({ Title = "Current values", Content = table.concat(lines, "\n") })
end)
end
if progressParagraph and type(progressParagraph.Set) == "function" then
local root = getRoot()
local txt
if not root then
txt = "(character not loaded)"
else
local sec = sectionOf(root.Position.X)
local layer = layerOf(root.Position.Y)
local sName = (STAGES and STAGES[sec] and STAGES[sec].name) or "?"
local statusLine
if trainingBusy then
statusLine = "Training at best area"
elseif phaseBlocksPlacement() then
statusLine = "Paused β event in progress"
elseif refilling then
statusLine = "Refilling at quarry"
elseif CFG.running then
statusLine = ("Building β %d blocks/sec"):format(currentRate)
else
statusLine = "Idle"
end
local clusterLine = ""
if activeCluster then
clusterLine = ("\nCluster progress: %d of %d"):format(
math.min(activeCluster.orderIdx - 1, #activeCluster.order),
#activeCluster.order)
end
txt = ("%s\nSection: %s (layer %d)%s\nBlocks placed: %d"):format(
statusLine, sName, layer, clusterLine, analytics.totalFires)
end
pcall(function()
progressParagraph:Set({ Title = "Status", Content = txt })
end)
end
if positionParagraph and type(positionParagraph.Set) == "function" then
local root = getRoot()
local txt
if not root then
txt = "(character not loaded)"
else
local col = columnOf(root.Position.X)
local sec = sectionOf(root.Position.X)
local layer = layerOf(root.Position.Y)
local sName = (STAGES and STAGES[sec] and STAGES[sec].name) or "?"
txt = ("Section: %d β %s\nColumn: %d Layer: %d"):format(sec, sName, col, layer)
end
pcall(function()
positionParagraph:Set({ Title = "Position", Content = txt })
end)
end
end
if trainingBusy and CFG.trainPauseBuild then return end
if not CFG.running then phase = "idle"; cycleTarget = nil; return end
if pausedBySprings then return end
if not tierPassesFilter() then return end
local blocked = phaseBlocksPlacement()
if blocked then
if now - lastSuspendCheck > 2 then lastSuspendCheck = now end
return
end
refillTokens(dt)
if refilling then return end
if now < pausedUntil then return end
if listDirty then rebuildList() end
local blocks = LocalPlayer:GetAttribute("Blocks") or 0
if CFG.refillEnabled and blocks <= 0 then task.spawn(refillBlocks); return end
if blocks <= 0 then return end
local root = getRoot()
if not root then return end
if phase == "idle" then
if CFG.targetMode == "cluster" then
if not activeCluster then
local ref, filter = modeFilterAndRef(root.Position)
if filter and not anyPasses(filter) then lastPlacedPos = nil; ref, filter = root.Position, nil end
local anchor = pickNearest(ref, filter) or pickNearest(root.Position, nil)
if anchor then
activeCluster = { anchorPos = anchor.Position, order = {}, orderIdx = 1 }
rebuildClusterOrder()
end
end
if not activeCluster then
listDirty = true
else
if activeCluster.orderIdx > #activeCluster.order then
-- cluster ghost set empty. look for unfired ghosts near the anchor
-- (extends the cluster as the wall grows)
local unfired = clusterGhosts(activeCluster.anchorPos)
if #unfired == 0 then
-- nothing in the current anchor; grab nearest unfired within 3x radius
local expand = CFG.clusterRadius * 3
local nearest, bestD = nil, expand
for i = 1, #partList do
local g = partList[i]
if g and g.Parent and not firedSet[g] and not inflight[g] then
local d = (g.Position - activeCluster.anchorPos).Magnitude
if d < bestD then
bestD = d
nearest = g
end
end
end
if nearest then
activeCluster.anchorPos = nearest.Position
unfired = clusterGhosts(activeCluster.anchorPos)
end
end
if #unfired > 0 then
rebuildClusterOrder()
end
end
if activeCluster.orderIdx > #activeCluster.order then
local ifCount = clusterInflightCount(activeCluster.anchorPos)
if ifCount == 0 then
activeCluster = nil
listDirty = true
else
listDirty = true
end
else
local g = activeCluster.order[activeCluster.orderIdx]
if not g or not g.Parent or firedSet[g] or inflight[g] then
activeCluster.orderIdx = activeCluster.orderIdx + 1
else
local sign = (root.Position.Z >= g.Position.Z) and 1 or -1
local target = Vector3.new(
g.Position.X, g.Position.Y + BLOCK,
g.Position.Z + sign * (BLOCK * 0.5 + 2.5))
cycleTarget = target
local dist = (root.Position - target).Magnitude
if dist > 2 then
if CFG.moveMode == "teleport" then
root.CFrame = CFrame.new(target, target + root.CFrame.LookVector)
elseif CFG.moveMode == "step" then
local delta = target - root.Position
local stepDist = math.min(CFG.stepSpeed * dt, delta.Magnitude)
root.CFrame = root.CFrame + delta.Unit * stepDist
end
end
phase = "settling"
settleUntil = now + AUTO.settleTime
end
end
end
else
local ref, filter = modeFilterAndRef(root.Position)
if filter and not anyPasses(filter) then lastPlacedPos = nil; ref, filter = root.Position, nil end
local g = pickNearest(ref, filter) or pickNearest(root.Position, nil)
if not g then
listDirty = true
if now - lastSuspendCheck > CFG.suspendPoll then lastSuspendCheck = now; listDirty = true end
else
local sign = (root.Position.Z >= g.Position.Z) and 1 or -1
local target = Vector3.new(
g.Position.X, g.Position.Y + BLOCK,
g.Position.Z + sign * (BLOCK * 0.5 + 2.5))
cycleTarget = target
local dist = (root.Position - target).Magnitude
if dist > 2 then
if CFG.moveMode == "teleport" then
root.CFrame = CFrame.new(target, target + root.CFrame.LookVector)
elseif CFG.moveMode == "step" then
local delta = target - root.Position
local stepDist = math.min(CFG.stepSpeed * dt, delta.Magnitude)
root.CFrame = root.CFrame + delta.Unit * stepDist
end
end
phase = "settling"
settleUntil = now + AUTO.settleTime
end
end
elseif phase == "settling" then
if now >= settleUntil then phase = "firing" end
elseif phase == "firing" then
if CFG.targetMode == "cluster" and activeCluster then
local fired, blockedRange = fireClusterOrdered()
if activeCluster.orderIdx > #activeCluster.order then
local ifCount = clusterInflightCount(activeCluster.anchorPos)
if ifCount == 0 then
activeCluster = nil
phase = "idle"
cycleTarget = nil
listDirty = true
else
phase = "idle"
end
elseif blockedRange then
phase = "idle"
elseif fired == 0 then
phase = "idle"
end
else
local fired, hasInRange = fireInRange()
if fired == 0 and not hasInRange then
phase = "idle"
cycleTarget = nil
end
end
end
end
RunService.Heartbeat:Connect(tick)
pcall(function()
Rayfield:Notify({
Title = "AutoPlace ready",
Content = "Building and Training tabs. Toggle to start.",
Duration = 6,
})
end)Review the source before using it. Executions count successful copies, capped at 10 per user and network per script in 24 hours.


Your community, in one place