-- ORBITAL - Mine a Planet [BETA 8]
if game.PlaceId ~= 121125129560252 then return end
local BOOTOK, BOOTERR = pcall(function()
local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")
local RS = game:GetService("ReplicatedStorage")
local LP = Players.LocalPlayer
local Net = RS:WaitForChild("Net")
local HOST = (gethui and gethui()) or game:GetService("CoreGui")
for _, root in ipairs({ HOST, LP:FindFirstChild("PlayerGui") }) do
if root then for _, n in ipairs({ "Orbital", "OrbitalBoot" }) do
local o = root:FindFirstChild(n); if o then pcall(function() o:Destroy() end) end
end end
end
local DEF = { autoCollect=false, autoSell=false, autoUpgrade=false, autoEvent=false, autoRoll=false,
rarities = { Divine=true, Exotic=true, Prismatic=true, Transcendent=true, Void=true },
returnHome=true, notify=true, collectMs=140 }
local S = getgenv().OrbitalX
if not S then S = {}; getgenv().OrbitalX = S; for k,v in pairs(DEF) do S[k]=v end end
for k,v in pairs(DEF) do if S[k]==nil then S[k]=v end end
if S._cleanup then pcall(S._cleanup); task.wait(0.1) end
S.run = true
local GEN = (S._gen or 0)+1; S._gen = GEN
local function live() return S.run==true and S._gen==GEN end
-- stats can survive a hot reload from an older build, so backfill every field
local stats = S._stats or {}
stats.collected = stats.collected or 0
stats.upgrades = stats.upgrades or 0
stats.rolls = stats.rolls or 0
stats.errors = stats.errors or 0
S._stats = stats
local conns = {}
if getgenv()._OrbitalConns then for _,c in ipairs(getgenv()._OrbitalConns) do pcall(function() c:Disconnect() end) end end
getgenv()._OrbitalConns = {}
local function track(c) if c then conns[#conns+1]=c; table.insert(getgenv()._OrbitalConns,c) end return c end
-- theme
local C = { bg=Color3.fromRGB(12,14,20), surf=Color3.fromRGB(19,22,31), surf2=Color3.fromRGB(27,31,43),
line=Color3.fromRGB(60,70,96), txt=Color3.fromRGB(236,240,250), sub=Color3.fromRGB(132,144,172),
acc=Color3.fromRGB(96,170,255), good=Color3.fromRGB(80,214,140), bad=Color3.fromRGB(240,96,110) }
local FB,FM,FR = Enum.Font.GothamBold, Enum.Font.GothamMedium, Enum.Font.Gotham
local function mk(cls,p,k) local i=Instance.new(cls) for a,b in pairs(p) do i[a]=b end if k then for _,c in ipairs(k) do c.Parent=i end end return i end
local function corner(r) return mk("UICorner",{CornerRadius=UDim.new(0,r or 8)}) end
local function stroke(t,c) return mk("UIStroke",{Transparency=t or 0.8,Color=c or C.line,Thickness=1}) end
local function pad(n) return mk("UIPadding",{PaddingTop=UDim.new(0,n),PaddingBottom=UDim.new(0,n),PaddingLeft=UDim.new(0,n),PaddingRight=UDim.new(0,n)}) end
local function tw(o,p,t,st) TweenService:Create(o,TweenInfo.new(t or 0.18,st or Enum.EasingStyle.Quad,Enum.EasingDirection.Out),p):Play() end
-- ======================================================= root gui (hidden) =
local gui = mk("ScreenGui",{Name="Orbital",ResetOnSpawn=false,ZIndexBehavior=Enum.ZIndexBehavior.Sibling,DisplayOrder=999,Enabled=false,Parent=HOST})
local toasts = mk("Frame",{AnchorPoint=Vector2.new(1,0),Position=UDim2.new(1,-18,0,18),Size=UDim2.new(0,300,1,-36),
BackgroundTransparency=1,Parent=gui},{mk("UIListLayout",{Padding=UDim.new(0,8),HorizontalAlignment=Enum.HorizontalAlignment.Right,SortOrder=Enum.SortOrder.LayoutOrder})})
local function notify(title,msg,kind)
if not S.notify then return end
local col=(kind=="bad" and C.bad) or (kind=="good" and C.good) or C.acc
local card=mk("Frame",{Size=UDim2.new(1,0,0,0),AutomaticSize=Enum.AutomaticSize.Y,BackgroundColor3=C.surf,
Position=UDim2.new(1,40,0,0),Parent=toasts},{corner(10),stroke(0.72)})
mk("Frame",{Size=UDim2.new(0,3,1,0),BackgroundColor3=col,BorderSizePixel=0,Parent=card})
local body=mk("Frame",{Position=UDim2.new(0,14,0,0),Size=UDim2.new(1,-24,0,0),AutomaticSize=Enum.AutomaticSize.Y,
BackgroundTransparency=1,Parent=card},{mk("UIPadding",{PaddingTop=UDim.new(0,10),PaddingBottom=UDim.new(0,11)}),
mk("UIListLayout",{Padding=UDim.new(0,2),SortOrder=Enum.SortOrder.LayoutOrder})})
mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(1,0,0,15),Text=title,TextColor3=C.txt,
TextXAlignment=Enum.TextXAlignment.Left,Font=FM,TextSize=13,LayoutOrder=1,Parent=body})
mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(1,0,0,0),AutomaticSize=Enum.AutomaticSize.Y,Text=msg,
TextColor3=C.sub,TextWrapped=true,TextXAlignment=Enum.TextXAlignment.Left,Font=FR,TextSize=11,LayoutOrder=2,Parent=body})
tw(card,{Position=UDim2.new(0,0,0,0)},0.28)
task.delay(5,function() if card.Parent then tw(card,{Position=UDim2.new(1,40,0,0)},0.25) task.wait(0.3) pcall(function() card:Destroy() end) end end)
end
S._notify=notify
local errLast, errLog = {}, {}
S._errLog=errLog
local function report(f,e)
stats.errors=stats.errors+1
local m=tostring(e):gsub("^.-:%d+: ",""):sub(1,110)
table.insert(errLog,1,{f=f,e=m,t=os.date("%H:%M:%S")}); if #errLog>30 then table.remove(errLog) end
local now=os.clock()
if not errLast[f] or now-errLast[f]>20 then errLast[f]=now; notify("Error: "..f,m,"bad") end
end
local function loopify(fn,delay,name)
task.spawn(function() while live() do
local ok,err=xpcall(fn,function(e) return tostring(e).." | "..debug.traceback("",2):gsub("%s+"," "):sub(1,160) end)
if not ok then report(name,err) end
task.wait((type(delay)=="function" and delay()) or delay)
end end)
end
-- ======================================================= game bindings ====
local function R(n) return Net:FindFirstChild(n) end
local MYID = LP.UserId
local function ownerOf(i) local a=i.Parent for _=1,6 do if not a then return end local v=a:GetAttribute("FS_UserId") if v then return v end a=a.Parent end end
local myDocks = {}
local function reindexDocks()
table.clear(myDocks)
for _,d in ipairs(workspace:GetDescendants()) do
if d:IsA("BasePart") and d:GetAttribute("FS_OreCount")~=nil and ownerOf(d)==MYID then
local pr=d:FindFirstChildWhichIsA("ProximityPrompt"); if pr then myDocks[d]=pr end
end
end
end
pcall(reindexDocks)
local function myOre() local t,n=0,0 for p in pairs(myDocks) do local ok,v=pcall(function() return p.Parent and p:GetAttribute("FS_OreCount") or nil end) if ok and v then t=t+v n=n+1 end end return t,n end
-- native perk toggles (player already owns these; this is the game's own path)
local function setPerk(key,val)
local r=R("SetAutoPerk"); if not r then return false end
local ok=pcall(function() r:FireServer({key=key,value=val}) end)
return ok
end
-- upgrade state cache
local UP = {
{k="luck", req="RequestUpgradeLuck", sync="LuckSync", label="Luck"},
{k="dockStorage", req="RequestUpgradeDockStorage", sync="DockStorageSync", label="Dock Storage"},
{k="laser", req="RequestUpgradeLaserPower", sync="LaserSync", label="Laser"},
{k="droneRolls", req="RequestUpgradeDroneRolls", sync="DroneRollSync", label="Drone Rolls"},
{k="planet", req="RequestUpgradePlanet", sync="PlanetSync", label="Planet"},
{k="cargo", req="RequestUpgradeCargo", sync="CargoSync", label="Cargo"},
}
local upState = {}
S._upState = upState
for _,u in ipairs(UP) do
local r=R(u.sync)
if r then track(r.OnClientEvent:Connect(function(payload)
local st=payload
if type(st)=="table" and st.nextCost==nil and st.level==nil then
for _,v in pairs(st) do if type(v)=="table" and (v.nextCost~=nil or v.level~=nil) then st=v break end end
end
upState[u.k]=st
end)) end
end
local cash=0
do local r=R("CurrencySync")
if r then track(r.OnClientEvent:Connect(function(p)
local s=p; if type(s)=="table" and s.m==nil then for _,v in pairs(s) do if type(v)=="table" and v.m then s=v break end end end
if type(s)=="table" and s.m then cash=(s.m or 0)*10^(s.e or 0) end
end)) end
end
local function pullState() pcall(function() local r=R("RequestStateSync") if r then r:FireServer() end end) end
pullState()
-- forward declared: the automation loops below are defined before the movement helpers
local hrpOf, flyTo, myEventDrops, mySellPad
-- ======================================================= event drops =====
-- Mirrors GlobalDropController's protocol exactly:
-- sync {campaignId, scope="server"|"global", drops={{dropId,position,expiresAt}}}
-- {clear=true, scope} -> wipe that scope
-- {claimResult={dropId,status,retryAfter}}
-- claim RequestGlobalDropClaim:FireServer{dropId, campaignId, scope}
-- The game gates claims client-side on distance (<=8 studs); we skip that and claim by id,
-- but we DO honour its 2s per-drop throttle, the retry backoff, and the server's
-- EventDropConfig.maxClaimsPerMinute = 90 cap so we never trip rate limiting.
local drops, claimTimes = {}, {}
local ev = S._ev or { claimed=0, rejected=0, attempts=0 }
S._ev = ev; S._drops = drops
local MAX_CLAIMS_PER_MIN = 85 -- config says 90; stay just under
do
local r = R("GlobalDropSync")
if r then track(r.OnClientEvent:Connect(function(p)
if type(p) ~= "table" then return end
if p.clear == true and (p.scope == "server" or p.scope == "global") then
for id, d in pairs(drops) do if d.scope == p.scope then drops[id] = nil end end
return
end
local cr = p.claimResult
if type(cr) == "table" and type(cr.dropId) == "string" then
if cr.status == "claimed" then drops[cr.dropId] = nil; ev.claimed = ev.claimed + 1
elseif cr.status == "terminal" then drops[cr.dropId] = nil; ev.rejected = ev.rejected + 1
elseif cr.status == "retry" then
local d = drops[cr.dropId]
if d then d.nextClaimAt = os.clock() + math.clamp(tonumber(cr.retryAfter) or 1, 0.5, 2) end
end
return
end
if type(p.campaignId) == "string" and (p.scope == "server" or p.scope == "global") and type(p.drops) == "table" then
for _, v in ipairs(p.drops) do
if type(v) == "table" and type(v.dropId) == "string" and typeof(v.position) == "Vector3" and drops[v.dropId] == nil then
drops[v.dropId] = { position = v.position, expiresAt = v.expiresAt,
campaignId = p.campaignId, scope = p.scope, nextClaimAt = 0 }
end
end
end
end)) end
end
local function dropCount() local n = 0 for _ in pairs(drops) do n = n + 1 end return n end
local function shards(kind) return LP:GetAttribute("FS_EventShards_" .. kind) or 0 end
-- Auto roll. The game's native SetAutoRollActive is gamepass gated (FS_GP_autoRoll), and the
-- server rejects it if unowned, so we drive the roll action itself instead. Targets are still
-- pushed so the native panel stays in sync if the pass is ever owned.
loopify(function()
if not S.autoRoll then return end
local r = R("RequestRollDrones"); if not r then return end
pcall(function() r:FireServer({}) end)
stats.rolls = (stats.rolls or 0) + 1
end, 1.1, "Auto roll")
-- Incident shard pickup is validated SERVER-SIDE (EventDropConfig.pickupRadiusStuds = 8) and
-- there is no client claim remote for it, so it cannot be done from range. What we can do is
-- park at the sky beam where drops spawn (radius 110 around FS_SkyBeamAnchor) - which is exactly
-- what any player farming the event does, so it reads as completely normal.
local function eventActive() return #myEventDrops() > 0 end
S._eventActive = eventActive
-- Fly over each of OUR drops. The server grants the shard on proximity (8 studs), which is
-- exactly the manual "fly over it" action, just driven automatically. Returns home after.
loopify(function()
if not S.autoEvent then return end
if not (S._myDrops and S._flyTo and S._hrpOf) then return end
local list = S._myDrops()
ev.pending = #list
if #list == 0 then return end
local hrp = S._hrpOf(); if not hrp then return end
local home = hrp.Position
for _, drop in ipairs(list) do
if not live() or not S.autoEvent then break end
if drop.Parent then
-- must close to ~3 studs: the server only grants the pickup on true contact range
local ok = S._flyTo(drop.Position, 3)
ev.moves = (ev.moves or 0) + 1
if ok then
task.wait(0.8) -- let the server register the pickup
if not drop.Parent then ev.claimed = ev.claimed + 1 end
end
end
end
if S.returnHome ~= false then S._flyTo(home, 6) end
end, 2.5, "Event shards")
-- Auto sell. The native autoSell perk is gamepass gated (FS_GP_autoSell) and the server refuses
-- to enable it when unowned, so we do it the manual way: fly to OUR sell pad (prompt range is 16)
-- fire "Sell All", then fly back. Only runs when the backpack actually holds ore.
loopify(function()
if not S.autoSell then return end
if not (S._mySellPad and S._flyTo and S._hrpOf) then return end
local ore = LP:GetAttribute("FS_BackpackOre") or 0
if ore <= 0 then return end
local anchor, prompt = S._mySellPad()
if not anchor then return end
local hrp = S._hrpOf(); if not hrp then return end
local home = hrp.Position
local dist = (anchor.Position - hrp.Position).Magnitude
if dist > 12 then
if not S._flyTo(anchor.Position + Vector3.new(0, 3, 0), 8) then return end
end
if prompt and fireproximityprompt then
for _ = 1, 3 do
pcall(fireproximityprompt, prompt)
task.wait(0.35)
if (LP:GetAttribute("FS_BackpackOre") or 0) <= 0 then break end
end
end
stats.sells = (stats.sells or 0) + 1
if S.returnHome ~= false and dist > 12 then S._flyTo(home, 6) end
end, 6, "Auto sell")
-- ======================================================= movement ========
-- Both shard pickup and selling are validated SERVER-SIDE by proximity, so they genuinely
-- require being there. We glide in steps (~160 studs/s, close to the game's own jetpack) so it
-- reads as normal flying rather than a snap teleport, and we return to where we started.
hrpOf = function() local ch=LP.Character; return ch and ch:FindFirstChild("HumanoidRootPart") end
S._hrpOf = hrpOf
-- Travel at the character's REAL jetpack speed. Moving faster than FS_JetpackFlightSpeed trips
-- the server's movement correction (FS_MovementCorrectionToken climbs and you get yanked back),
-- which is exactly why the first version never arrived. At legit speed it is just normal flight.
flyTo = function(pos, arrive, timeout)
local hrp=hrpOf(); if not hrp then return false end
arrive = arrive or 6
local speed = (LP:GetAttribute("FS_JetpackFlightSpeed") or 80) * 0.9
local dt = 0.06
local perStep = math.max(2, speed * dt)
local t0 = os.clock()
timeout = timeout or math.clamp((pos - hrp.Position).Magnitude / math.max(speed, 1) + 6, 8, 30)
while live() and os.clock()-t0 < timeout do
local h = hrpOf(); if not h then return false end
local cur = h.Position
local d = pos - cur
if d.Magnitude <= arrive then return true end
local ok = pcall(function() h.CFrame = CFrame.new(cur + d.Unit * math.min(d.Magnitude, perStep)) end)
if not ok then return false end
task.wait(dt)
end
local h = hrpOf()
return (h ~= nil) and (h.Position - pos).Magnitude <= arrive + 4
end
S._flyTo = flyTo
-- our own event drops only. Other players' drops live in sibling folders and are not ours to take.
-- Two kinds of drops live under FarmStar.EventDrops:
-- Shared/ -> the actual EVENT SHARDS, FS_DropOwner == 0, free for anyone to fly over
-- <userId>/ -> private drops (consumables/batteries) owned by that player
-- We take the shared shards plus our OWN private drops, never another player's.
myEventDrops = function()
local out={}
local fs=workspace:FindFirstChild("FarmStar"); if not fs then return out end
local eds=fs:FindFirstChild("EventDrops"); if not eds then return out end
local now=os.time()
local hrp=hrpOf()
local here=hrp and hrp.Position or Vector3.new()
local function scan(folder)
if not folder then return end
for _,d in ipairs(folder:GetDescendants()) do
if d:IsA("BasePart") and d.Name=="FS_EventDrop" then
local exp=d:GetAttribute("FS_DropExpireAt")
local owner=d:GetAttribute("FS_DropOwner")
if (owner==0 or owner==LP.UserId) and (type(exp)~="number" or exp>now) then
out[#out+1]=d
end
end
end
end
scan(eds:FindFirstChild("Shared"))
scan(eds:FindFirstChild(tostring(LP.UserId)))
-- shards first, then nearest first, so the valuable pickups are never missed on a short event
table.sort(out, function(a,b)
local sa = (a:GetAttribute("FS_DropKind")=="shard") and 1 or 0
local sb = (b:GetAttribute("FS_DropKind")=="shard") and 1 or 0
if sa ~= sb then return sa > sb end
return (a.Position-here).Magnitude < (b.Position-here).Magnitude
end)
return out
end
S._myDrops=myEventDrops
-- our own sell pad (each plot has one under Platforms.<userId>.Shops)
mySellPad = function()
local fs=workspace:FindFirstChild("FarmStar"); if not fs then return nil end
local plats=fs:FindFirstChild("Platforms"); if not plats then return nil end
local mine=plats:FindFirstChild(tostring(LP.UserId)); if not mine then return nil end
local anchor=mine:FindFirstChild("FS_SellPromptAnchor", true)
if not anchor then return nil end
local prompt=anchor:FindFirstChildWhichIsA("ProximityPrompt")
return anchor, prompt
end
S._mySellPad=mySellPad
-- ======================================================= drones ==========
-- Rarity ladder + catalog come straight from the game's own config, so the target
-- list we hand to SetAutoRollTargets is exactly what its own panel would send.
local DroneCatalog, RarityConfig
pcall(function()
local Cfg = RS:WaitForChild("Config")
DroneCatalog = require(Cfg:WaitForChild("DroneCatalog"))
RarityConfig = require(Cfg:WaitForChild("RarityConfig"))
end)
local RARITY_ORDER = (RarityConfig and RarityConfig.order) or
{ "Common","Uncommon","Rare","Epic","Legendary","Mythic","Secret","Divine","Exotic","Prismatic","Transcendent","Void" }
local function rankOf(rarity)
if RarityConfig and RarityConfig.rank then local ok,v = pcall(RarityConfig.rank, rarity); if ok and type(v)=="number" then return v end end
for i,r in ipairs(RARITY_ORDER) do if r == rarity then return i end end
return 0
end
local function rarityOfDrone(key)
if DroneCatalog and DroneCatalog.rarity then local ok,v = pcall(DroneCatalog.rarity, key); if ok then return v end end
return nil
end
local function targetsForSelection()
local out, seen = {}, {}
if not (DroneCatalog and DroneCatalog.obtainableDronesOfRarity) then return out end
for _, r in ipairs(RARITY_ORDER) do
if S.rarities and S.rarities[r] then
local ok, list = pcall(DroneCatalog.obtainableDronesOfRarity, r)
if ok and type(list) == "table" then
for _, k in ipairs(list) do if type(k)=="string" and not seen[k] then seen[k]=true; out[#out+1]=k end end
end
end
end
return out
end
local function pushRollTargets()
local t = targetsForSelection()
pcall(function() local r=R("SetAutoRollTargets"); if r then r:FireServer({ targets = t }) end end)
return #t
end
local function setRollActive(v)
pcall(function() local r=R("SetAutoRollActive"); if r then r:FireServer({ active = v and true or false }) end end)
end
-- drone inventory mirror (InventorySync items: {uid, kind, droneType, location, level, favorite})
local invItems = {}
S._inv = invItems
do
local r = R("InventorySync")
if r then track(r.OnClientEvent:Connect(function(p)
local src = p
if type(src) == "table" and src.items == nil then
for _, v in pairs(src) do if type(v)=="table" and v.items then src = v break end end
end
local items = type(src)=="table" and src.items or nil
if type(items) ~= "table" then return end
table.clear(invItems)
for _, it in pairs(items) do if type(it)=="table" and it.kind=="drone" then invItems[#invItems+1]=it end end
end)) end
end
-- returns: placedCount, storedCount, list of upgrade suggestions {dock, from, to, gain}
local function droneAnalysis()
local placed, stored = {}, {}
for _, it in ipairs(invItems) do
local loc = tostring(it.location or "")
local d = { uid=it.uid, key=it.droneType, fav=it.favorite,
rank=rankOf(rarityOfDrone(it.droneType)), rarity=rarityOfDrone(it.droneType) }
local dock = loc:match("^dock:(%d+)")
if dock then d.dock = tonumber(dock); placed[#placed+1]=d else stored[#stored+1]=d end
end
table.sort(placed, function(a,b) return a.rank < b.rank end) -- weakest first
table.sort(stored, function(a,b) return a.rank > b.rank end) -- strongest first
local sugg, si = {}, 1
for _, weak in ipairs(placed) do
local best = stored[si]
if best and best.rank > weak.rank and not weak.fav then
sugg[#sugg+1] = { dock=weak.dock, from=weak.key, fromR=weak.rarity, to=best.key, toR=best.rarity }
si = si + 1
end
end
return placed, stored, sugg
end
S._droneAnalysis = droneAnalysis
-- ======================================================= loading screen ===
local bootDone = false
do
local bg = mk("ScreenGui",{Name="OrbitalBoot",ResetOnSpawn=false,IgnoreGuiInset=true,DisplayOrder=1000,Parent=HOST})
local card = mk("Frame",{AnchorPoint=Vector2.new(0.5,0.5),Position=UDim2.new(0.5,0,0.5,0),Size=UDim2.new(0,380,0,140),
BackgroundColor3=C.surf,BackgroundTransparency=1,Parent=bg},{corner(16),stroke(0.55)})
mk("UIGradient",{Rotation=90,Color=ColorSequence.new(Color3.fromRGB(255,255,255),Color3.fromRGB(170,170,170)),Parent=card})
local dot = mk("Frame",{Position=UDim2.new(0,26,0,28),Size=UDim2.new(0,20,0,20),BackgroundColor3=C.acc,
BackgroundTransparency=1,Parent=card},{corner(6)})
local t1 = mk("TextLabel",{BackgroundTransparency=1,Position=UDim2.new(0,58,0,26),Size=UDim2.new(1,-80,0,22),Text="ORBITAL",
TextColor3=C.txt,TextTransparency=1,TextXAlignment=Enum.TextXAlignment.Left,Font=FB,TextSize=22,Parent=card})
local t2 = mk("TextLabel",{BackgroundTransparency=1,Position=UDim2.new(0,58,0,50),Size=UDim2.new(1,-80,0,16),Text="mine a planet",
TextColor3=C.sub,TextTransparency=1,TextXAlignment=Enum.TextXAlignment.Left,Font=FR,TextSize=12,Parent=card})
local step = mk("TextLabel",{BackgroundTransparency=1,Position=UDim2.new(0,26,1,-46),Size=UDim2.new(1,-52,0,14),Text="",
TextColor3=C.sub,TextTransparency=1,TextXAlignment=Enum.TextXAlignment.Left,Font=FR,TextSize=11,Parent=card})
local barBg = mk("Frame",{Position=UDim2.new(0,26,1,-28),Size=UDim2.new(1,-52,0,5),BackgroundColor3=C.surf2,
BackgroundTransparency=1,BorderSizePixel=0,Parent=card},{corner(3)})
local bar = mk("Frame",{Size=UDim2.new(0,0,1,0),BackgroundColor3=C.acc,BorderSizePixel=0,Parent=barBg},{corner(3)})
tw(card,{BackgroundTransparency=0.02},0.3) tw(dot,{BackgroundTransparency=0},0.3)
tw(t1,{TextTransparency=0},0.35) tw(t2,{TextTransparency=0},0.4)
tw(step,{TextTransparency=0},0.4) tw(barBg,{BackgroundTransparency=0},0.4)
task.spawn(function()
local steps={"reading plot","indexing docks","binding remotes","syncing state","ready"}
for i,s in ipairs(steps) do
if step.Parent then step.Text=s end
tw(bar,{Size=UDim2.new(i/#steps,0,1,0)},0.26)
task.wait(0.26)
end
task.wait(0.2)
tw(card,{BackgroundTransparency=1},0.3) tw(t1,{TextTransparency=1},0.28) tw(t2,{TextTransparency=1},0.28)
tw(step,{TextTransparency=1},0.28) tw(dot,{BackgroundTransparency=1},0.28) tw(barBg,{BackgroundTransparency=1},0.28)
task.wait(0.34)
pcall(function() bg:Destroy() end)
bootDone = true
gui.Enabled = true -- window only appears AFTER the loader
end)
end
-- ======================================================= window ===========
local W,H = 560, 400
local win = mk("Frame",{Name="Win",AnchorPoint=Vector2.new(0.5,0.5),Position=UDim2.new(0.5,0,0.5,0),
Size=UDim2.new(0,W,0,H),BackgroundColor3=C.bg,Active=true,Draggable=true,Parent=gui},{corner(16),stroke(0.5)})
mk("UIGradient",{Rotation=90,Color=ColorSequence.new(Color3.fromRGB(255,255,255),Color3.fromRGB(184,184,184)),Parent=win})
local bar = mk("Frame",{Size=UDim2.new(1,0,0,56),BackgroundTransparency=1,Parent=win})
mk("Frame",{Position=UDim2.new(0,20,0.5,-10),Size=UDim2.new(0,20,0,20),BackgroundColor3=C.acc,Parent=bar},{corner(6)})
mk("TextLabel",{BackgroundTransparency=1,Position=UDim2.new(0,52,0,12),Size=UDim2.new(1,-150,0,19),Text="ORBITAL",
TextColor3=C.txt,TextXAlignment=Enum.TextXAlignment.Left,Font=FB,TextSize=18,Parent=bar})
mk("TextLabel",{BackgroundTransparency=1,Position=UDim2.new(0,52,0,33),Size=UDim2.new(1,-150,0,14),Text="mine a planet",
TextColor3=C.sub,TextXAlignment=Enum.TextXAlignment.Left,Font=FR,TextSize=11,Parent=bar})
local cashLbl = mk("TextLabel",{AnchorPoint=Vector2.new(1,0.5),Position=UDim2.new(1,-58,0.5,0),Size=UDim2.new(0,150,0,18),
Text="",TextColor3=C.good,TextXAlignment=Enum.TextXAlignment.Right,Font=FM,TextSize=13,BackgroundTransparency=1,Parent=bar})
local hideB = mk("TextButton",{AnchorPoint=Vector2.new(1,0.5),Position=UDim2.new(1,-18,0.5,0),Size=UDim2.new(0,28,0,28),
BackgroundColor3=C.surf2,Text="-",TextColor3=C.txt,Font=FB,TextSize=16,AutoButtonColor=false,Parent=bar},{corner(8)})
mk("Frame",{Position=UDim2.new(0,20,0,56),Size=UDim2.new(1,-40,0,1),BackgroundColor3=C.line,
BackgroundTransparency=0.6,BorderSizePixel=0,Parent=win})
local side = mk("Frame",{Position=UDim2.new(0,16,0,68),Size=UDim2.new(0,136,1,-84),BackgroundColor3=C.surf,Parent=win},
{corner(12),mk("UIListLayout",{Padding=UDim.new(0,5),SortOrder=Enum.SortOrder.LayoutOrder}),pad(8)})
local content = mk("Frame",{Position=UDim2.new(0,166,0,68),Size=UDim2.new(1,-182,1,-84),BackgroundTransparency=1,ClipsDescendants=true,Parent=win})
local pages,tabs = {},{}
local function selectTab(id)
for k,pg in pairs(pages) do pg.Visible=(k==id) end
for k,b in pairs(tabs) do
tw(b,{BackgroundTransparency=(k==id) and 0 or 1},0.15)
local l=b:FindFirstChildOfClass("TextLabel"); if l then l.TextColor3=(k==id) and C.txt or C.sub end
end
end
local tOrd=0
local function addTab(id,label)
tOrd=tOrd+1
local b=mk("TextButton",{Size=UDim2.new(1,0,0,34),BackgroundColor3=C.surf2,BackgroundTransparency=1,Text="",
AutoButtonColor=false,LayoutOrder=tOrd,Parent=side},{corner(9)})
mk("TextLabel",{BackgroundTransparency=1,Position=UDim2.new(0,12,0,0),Size=UDim2.new(1,-16,1,0),Text=label,
TextColor3=C.sub,TextXAlignment=Enum.TextXAlignment.Left,Font=FM,TextSize=13,Parent=b})
local pg=mk("ScrollingFrame",{Size=UDim2.new(1,0,1,0),BackgroundTransparency=1,BorderSizePixel=0,Visible=false,
CanvasSize=UDim2.new(),AutomaticCanvasSize=Enum.AutomaticSize.Y,ScrollBarThickness=3,ScrollBarImageColor3=C.acc,Parent=content},
{mk("UIListLayout",{Padding=UDim.new(0,9),SortOrder=Enum.SortOrder.LayoutOrder})})
pages[id]=pg tabs[id]=b
track(b.MouseButton1Click:Connect(function() selectTab(id) end))
return pg
end
local ord=0
local function O() ord=ord+1 return ord end
local function sect(p,t) mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(1,0,0,14),Text=string.upper(t),
TextColor3=C.sub,TextXAlignment=Enum.TextXAlignment.Left,Font=FB,TextSize=10,LayoutOrder=O(),Parent=p}) end
local function Toggle(p,label,key,cb)
local row=mk("Frame",{Size=UDim2.new(1,0,0,42),BackgroundColor3=C.surf,LayoutOrder=O(),Parent=p},{corner(10),stroke(0.85)})
mk("TextLabel",{BackgroundTransparency=1,Position=UDim2.new(0,14,0,0),Size=UDim2.new(1,-70,1,0),Text=label,
TextColor3=C.txt,TextXAlignment=Enum.TextXAlignment.Left,Font=FM,TextSize=13,Parent=row})
local knob=mk("Frame",{AnchorPoint=Vector2.new(1,0.5),Position=UDim2.new(1,-12,0.5,0),Size=UDim2.new(0,42,0,22),
BackgroundColor3=S[key] and C.acc or C.surf2,Parent=row},{corner(11)})
local dot=mk("Frame",{AnchorPoint=Vector2.new(0,0.5),Position=UDim2.new(0,S[key] and 22 or 2,0.5,0),Size=UDim2.new(0,18,0,18),
BackgroundColor3=C.txt,Parent=knob},{corner(9)})
local btn=mk("TextButton",{BackgroundTransparency=1,Size=UDim2.new(1,0,1,0),Text="",Parent=row})
track(btn.MouseButton1Click:Connect(function()
S[key]=not S[key]
tw(knob,{BackgroundColor3=S[key] and C.acc or C.surf2},0.15)
tw(dot,{Position=UDim2.new(0,S[key] and 22 or 2,0.5,0)},0.15)
if cb then pcall(cb,S[key]) end
end))
end
local function card(p,h) return mk("Frame",{Size=UDim2.new(1,0,0,h),BackgroundColor3=C.surf,LayoutOrder=O(),Parent=p},{corner(10),stroke(0.85),pad(13)}) end
local U={}
do local p=addTab("auto","Automation")
sect(p,"Ore")
Toggle(p,"Auto collect","autoCollect",function(v) setPerk("autoCollect",v) end)
Toggle(p,"Auto sell","autoSell",function(v) setPerk("autoSell",v) end)
sect(p,"Status")
local c=card(p,58)
U.s1=mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(1,0,0,17),Text="",TextColor3=C.txt,
TextXAlignment=Enum.TextXAlignment.Left,Font=FM,TextSize=13,Parent=c})
U.s2=mk("TextLabel",{BackgroundTransparency=1,Position=UDim2.new(0,0,0,20),Size=UDim2.new(1,0,0,16),Text="",
TextColor3=C.sub,TextXAlignment=Enum.TextXAlignment.Left,Font=FR,TextSize=11,Parent=c})
end
do local p=addTab("upg","Upgrades")
sect(p,"Auto")
Toggle(p,"Auto upgrade all","autoUpgrade")
sect(p,"Tracks")
U.upRows={}
for _,u in ipairs(UP) do
local c=card(p,38)
mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(0.5,0,1,0),Text=u.label,TextColor3=C.txt,
TextXAlignment=Enum.TextXAlignment.Left,Font=FM,TextSize=12,Parent=c})
U.upRows[u.k]=mk("TextLabel",{BackgroundTransparency=1,Position=UDim2.new(0.5,0,0,0),Size=UDim2.new(0.5,0,1,0),Text="-",
TextColor3=C.sub,TextXAlignment=Enum.TextXAlignment.Right,Font=FR,TextSize=11,Parent=c})
end
end
do local p=addTab("drn","Drones")
sect(p,"Auto roll")
Toggle(p,"Auto roll drones","autoRoll",function(v)
local n=pushRollTargets(); setRollActive(v)
if v then notify("Auto roll on", n.." drone types targeted","good") end
end)
sect(p,"Target rarities")
local grid=mk("Frame",{Size=UDim2.new(1,0,0,0),AutomaticSize=Enum.AutomaticSize.Y,BackgroundTransparency=1,LayoutOrder=O(),Parent=p},
{mk("UIGridLayout",{CellSize=UDim2.new(0.5,-4,0,30),CellPadding=UDim2.new(0,8,0,8),SortOrder=Enum.SortOrder.LayoutOrder})})
U.rarChips={}
for _,r in ipairs(RARITY_ORDER) do
local on=(S.rarities and S.rarities[r]) and true or false
local chip=mk("TextButton",{BackgroundColor3=on and C.acc or C.surf,Text="",AutoButtonColor=false,Parent=grid},{corner(8),stroke(0.85)})
local lb=mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(1,0,1,0),Text=r,
TextColor3=on and Color3.fromRGB(12,16,26) or C.sub,Font=FM,TextSize=11,Parent=chip})
U.rarChips[r]={chip=chip,lb=lb}
track(chip.MouseButton1Click:Connect(function()
S.rarities=S.rarities or {}
S.rarities[r]=not S.rarities[r]
local o=S.rarities[r] and true or false
tw(chip,{BackgroundColor3=o and C.acc or C.surf},0.14)
lb.TextColor3 = o and Color3.fromRGB(12,16,26) or C.sub
local n=pushRollTargets()
if U.rollInfo then U.rollInfo.Text=n.." drone types targeted" end
end))
end
U.rollInfo=mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(1,0,0,16),Text="",TextColor3=C.sub,
TextXAlignment=Enum.TextXAlignment.Left,Font=FR,TextSize=11,LayoutOrder=O(),Parent=p})
sect(p,"Loadout")
local dc=card(p,58)
U.d1=mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(1,0,0,17),Text="",TextColor3=C.txt,
TextXAlignment=Enum.TextXAlignment.Left,Font=FM,TextSize=13,Parent=dc})
U.d2=mk("TextLabel",{BackgroundTransparency=1,Position=UDim2.new(0,0,0,20),Size=UDim2.new(1,0,0,16),Text="",
TextColor3=C.sub,TextXAlignment=Enum.TextXAlignment.Left,Font=FR,TextSize=11,Parent=dc})
sect(p,"Suggested swaps")
U.swapBox=mk("Frame",{Size=UDim2.new(1,0,0,116),BackgroundColor3=C.surf,LayoutOrder=O(),Parent=p},
{corner(10),stroke(0.85),pad(11),mk("UIListLayout",{Padding=UDim.new(0,4),SortOrder=Enum.SortOrder.LayoutOrder})})
U.swapEmpty=mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(1,0,0,15),Text="Loadout is optimal",
TextColor3=C.sub,TextXAlignment=Enum.TextXAlignment.Left,Font=FR,TextSize=11,Parent=U.swapBox})
end
do local p=addTab("evt","Events")
sect(p,"Shard farming")
Toggle(p,"Auto collect shards","autoEvent")
Toggle(p,"Return to start after","returnHome")
sect(p,"Progress")
local c1=card(p,44)
mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(0.45,0,1,0),Text="UFO",TextColor3=C.txt,
TextXAlignment=Enum.TextXAlignment.Left,Font=FM,TextSize=13,Parent=c1})
U.ufo=mk("TextLabel",{BackgroundTransparency=1,Position=UDim2.new(0.45,0,0,0),Size=UDim2.new(0.55,0,1,0),Text="-",
TextColor3=C.sub,TextXAlignment=Enum.TextXAlignment.Right,Font=FR,TextSize=12,Parent=c1})
local c2=card(p,44)
mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(0.45,0,1,0),Text="JellyFish",TextColor3=C.txt,
TextXAlignment=Enum.TextXAlignment.Left,Font=FM,TextSize=13,Parent=c2})
U.jelly=mk("TextLabel",{BackgroundTransparency=1,Position=UDim2.new(0.45,0,0,0),Size=UDim2.new(0.55,0,1,0),Text="-",
TextColor3=C.sub,TextXAlignment=Enum.TextXAlignment.Right,Font=FR,TextSize=12,Parent=c2})
sect(p,"Live")
local c3=card(p,58)
U.e1=mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(1,0,0,17),Text="",TextColor3=C.txt,
TextXAlignment=Enum.TextXAlignment.Left,Font=FM,TextSize=13,Parent=c3})
U.e2=mk("TextLabel",{BackgroundTransparency=1,Position=UDim2.new(0,0,0,20),Size=UDim2.new(1,0,0,16),Text="",
TextColor3=C.sub,TextXAlignment=Enum.TextXAlignment.Left,Font=FR,TextSize=11,Parent=c3})
end
do local p=addTab("diag","Diagnostics")
sect(p,"Health")
local c=card(p,40)
U.health=mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(1,0,1,0),Text="Nominal",TextColor3=C.good,
TextXAlignment=Enum.TextXAlignment.Left,Font=FM,TextSize=13,Parent=c})
sect(p,"Errors")
U.errBox=mk("Frame",{Size=UDim2.new(1,0,0,120),BackgroundColor3=C.surf,LayoutOrder=O(),Parent=p},
{corner(10),stroke(0.85),pad(11),mk("UIListLayout",{Padding=UDim.new(0,4),SortOrder=Enum.SortOrder.LayoutOrder})})
U.errEmpty=mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(1,0,0,15),Text="None",TextColor3=C.sub,
TextXAlignment=Enum.TextXAlignment.Left,Font=FR,TextSize=11,Parent=U.errBox})
Toggle(p,"Notifications","notify")
end
selectTab("auto")
-- ======================================================= loops ============
loopify(function()
if not S.autoCollect then return end
local n=0
for part,pr in pairs(myDocks) do
if not live() then break end
local ok,ore=pcall(function() return part.Parent and (part:GetAttribute("FS_OreCount") or 0) or nil end)
if not ok or ore==nil then myDocks[part]=nil
elseif ore>0 and pr.Parent then pcall(fireproximityprompt,pr) n=n+1 task.wait(0.03) end
end
if n>0 then stats.collected=stats.collected+n end
end, function() return (S.collectMs or 140)/1000 end, "Auto collect")
loopify(function() reindexDocks() end, 12, "Dock reindex")
loopify(function() pullState() end, 6, "State sync")
-- auto upgrade: buy any track that is not maxed and is affordable
loopify(function()
if not S.autoUpgrade then return end
for _,u in ipairs(UP) do
if not live() then break end
local st=upState[u.k]
if type(st)=="table" and type(st.nextCost)=="number" and cash>=st.nextCost then
local r=R(u.req)
if r then
pcall(function() r:FireServer({}) end)
stats.upgrades=stats.upgrades+1
task.wait(0.4)
end
end
end
end, 3, "Auto upgrade")
loopify(function()
local ore,docks=myOre()
if U.s1 then U.s1.Text=string.format("Docks %d Ore %d", docks, ore) end
if U.s2 then U.s2.Text=string.format("Collected %d Upgrades %d", stats.collected, stats.upgrades) end
if cashLbl then
local v=cash
local s = v>=1e12 and string.format("%.2fT",v/1e12) or v>=1e9 and string.format("%.2fB",v/1e9)
or v>=1e6 and string.format("%.2fM",v/1e6) or v>=1e3 and string.format("%.1fk",v/1e3) or string.format("%d",v)
cashLbl.Text=s
end
for _,u in ipairs(UP) do
local lb=U.upRows and U.upRows[u.k]
if lb then
local st=upState[u.k]
if type(st)~="table" then lb.Text="-" lb.TextColor3=C.sub
elseif st.nextCost==nil then lb.Text="MAX" lb.TextColor3=C.good
else
local c=st.nextCost
local s = c>=1e12 and string.format("%.2fT",c/1e12) or c>=1e9 and string.format("%.2fB",c/1e9)
or c>=1e6 and string.format("%.2fM",c/1e6) or c>=1e3 and string.format("%.1fk",c/1e3) or tostring(c)
lb.Text=(st.level and ("L"..tostring(st.level).." ") or "")..s
lb.TextColor3=(cash>=c) and C.acc or C.sub
end
end
end
do
local u,j = shards("UFO"), shards("JellyFish")
if U.ufo then U.ufo.Text=string.format("%d/10 shards %d token%s", u%10, math.floor(u/10), (math.floor(u/10)==1) and "" or "s")
U.ufo.TextColor3=(u>0) and C.acc or C.sub end
if U.jelly then U.jelly.Text=string.format("%d/10 shards %d token%s", j%10, math.floor(j/10), (math.floor(j/10)==1) and "" or "s")
U.jelly.TextColor3=(j>0) and C.acc or C.sub end
local pend = ev.pending or 0
if U.e1 then U.e1.Text = (pend > 0) and (pend .. " drop" .. ((pend == 1) and "" or "s") .. " waiting") or "No drops right now" end
if U.e2 then U.e2.Text = string.format("Picked up %d Trips %d Backpack %s",
ev.claimed or 0, ev.moves or 0, tostring(LP:GetAttribute("FS_BackpackOre") or 0)) end
end
do
local placed, stored, sugg = droneAnalysis()
if U.d1 then U.d1.Text=string.format("Placed %d Stored %d", #placed, #stored) end
if U.d2 then
local bestP = placed[#placed]; local bestS = stored[1]
U.d2.Text = string.format("Best placed %s Best stored %s",
(bestP and (bestP.rarity or "?")) or "-", (bestS and (bestS.rarity or "?")) or "-")
end
if U.swapBox then
for _,ch in ipairs(U.swapBox:GetChildren()) do if ch:IsA("TextLabel") and ch~=U.swapEmpty then ch:Destroy() end end
U.swapEmpty.Visible=(#sugg==0)
for i=1,math.min(#sugg,5) do local s=sugg[i]
mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(1,0,0,15),
Text=string.format("Dock %s %s -> %s", tostring(s.dock), tostring(s.fromR or s.from), tostring(s.toR or s.to)),
TextColor3=C.acc,TextTruncate=Enum.TextTruncate.AtEnd,TextXAlignment=Enum.TextXAlignment.Left,
Font=FR,TextSize=10,LayoutOrder=i,Parent=U.swapBox})
end
end
end
if U.health then
U.health.Text=(stats.errors==0) and "Nominal" or (stats.errors.." error(s)")
U.health.TextColor3=(stats.errors==0) and C.good or C.bad
end
if U.errBox then
for _,ch in ipairs(U.errBox:GetChildren()) do if ch:IsA("TextLabel") and ch~=U.errEmpty then ch:Destroy() end end
U.errEmpty.Visible=(#errLog==0)
for i=1,math.min(#errLog,5) do local e=errLog[i]
mk("TextLabel",{BackgroundTransparency=1,Size=UDim2.new(1,0,0,15),Text=e.t.." "..e.f..": "..e.e,TextColor3=C.bad,
TextTruncate=Enum.TextTruncate.AtEnd,TextXAlignment=Enum.TextXAlignment.Left,Font=FR,TextSize=10,LayoutOrder=i,Parent=U.errBox})
end
end
end, 1, "UI")
-- chrome
local shown=true
track(hideB.MouseButton1Click:Connect(function()
shown=not shown
tw(win,{Size=shown and UDim2.new(0,W,0,H) or UDim2.new(0,W,0,56)},0.22)
side.Visible=shown content.Visible=shown
end))
track(UserInputService.InputBegan:Connect(function(i,gp)
if gp then return end
if i.KeyCode==Enum.KeyCode.RightControl and bootDone then gui.Enabled=not gui.Enabled end
end))
S._cleanup=function() S.run=false for _,c in ipairs(conns) do pcall(function() c:Disconnect() end) end pcall(function() gui:Destroy() end) end
task.delay(2.2,function() if live() then notify("Ready","Wave 2 online.","good") end end)
end)
if not BOOTOK then warn("[ORBITAL] "..tostring(BOOTERR)) end