
KeylessUniversal

-- luna.dev | broken bones iv
-- money request method adapted from a russian-language bb4 rejoin farm script; its author was not identified in the provided source.
-- open-source, standalone game extension for the luna.dev universal script hub.
-- settings are stored in luna.dev/scripts/broken_bones_iv.json; shared disclaimer acceptance
-- and main menu preferences are stored in luna.dev/settings.json.
-- bundled releases can be superseded by user-approved updates in the future main hub.
-- no remote loader, forced extension update, or robux purchase is used by this file.
local money_amount = 1e300 -- money to request; change this to a smaller finite amount if needed
local fuel_target = 300 -- regular fuel upgrade cap; values above 300 have caused kicks and are blocked here
local purchase_delay = 0.05 -- optional seconds between purchases; 0 skips this wait, not server response time
local players = game:GetService("Players")
local storage = game:GetService("ReplicatedStorage")
local input = game:GetService("UserInputService")
local runservice = game:GetService("RunService")
local lighting = game:GetService("Lighting")
local teleport = game:GetService("TeleportService")
local virtualuser = game:GetService("VirtualUser")
local player = players.LocalPlayer
local camera = workspace.CurrentCamera
local remotes = storage:FindFirstChild("Functions") or storage:FindFirstChild("RemoteFunctions") or storage
local update = remotes and remotes:FindFirstChild("UpdateStat")
local load = remotes and remotes:FindFirstChild("LoadData")
local upgrades = {
{"breaks", 1000}, {"sprains", 750}, {"dislocations", 500},
{"flight", 800}, {"speed", 600}, {"jump", 400},
{"elasticity", 640}, {"friction", 480}, {"cooldown", 700}, {"fuel", 900}
}
local utilities = {
{"Helium Balloon", {10000, 50000, 100000, 175000, 250000}},
{"Helicopter Hat", {125000, 200000, 300000, 450000, 600000}},
{"Rocket Boots", {400000, 650000, 1000000, 1500000, 2250000}},
{"Jetpack", {1250000, 2500000, 5000000, 10000000, 20000000}},
{"Gravity Bubble", {50000000, 100000000, 250000000, 500000000, 1000000000}}
}
local maps = {
{0, 25000, 100000, 250000, 500000, 850000, 1350000, 2250000, 3500000, 5000000},
{750000, 1100000, 1400000, 1800000, 2200000, 2750000, 4000000, 6500000, 10000000, 15000000},
{1000000, 1500000, 2000000, 3600000, 8000000, 12500000, 20000000, 35000000, 60000000, 150000000}
}
local function create(kind, parent, properties)
local object = Instance.new(kind)
for name, value in pairs(properties) do object[name] = value end
object.Parent = parent
return object
end
local purple = Color3.fromRGB(189, 150, 253)
local pale = Color3.fromRGB(238, 233, 248)
local muted = Color3.fromRGB(177, 167, 195)
local surface = Color3.fromRGB(28, 23, 43)
local function round(object, radius)
create("UICorner", object, {CornerRadius = UDim.new(0, radius or 5)})
return object
end
local function label(parent, text, size, color, height)
return create("TextLabel", parent, {
Size = UDim2.new(1, 0, 0, height or 21), BackgroundTransparency = 1,
Font = Enum.Font.Gotham, Text = text, TextSize = size or 12,
TextColor3 = color or pale, TextXAlignment = Enum.TextXAlignment.Left,
TextWrapped = true,
AutomaticSize = parent:IsA("ScrollingFrame") and Enum.AutomaticSize.Y or Enum.AutomaticSize.None
})
end
local function button(parent, text, callback, height)
local item = round(create("TextButton", parent, {
Size = UDim2.new(1, 0, 0, height or 34), BackgroundColor3 = surface,
BorderSizePixel = 0, Font = Enum.Font.GothamMedium, Text = text,
TextColor3 = pale, TextSize = 12, TextWrapped = true, AutoButtonColor = true
}))
item.MouseButton1Click:Connect(callback)
return item
end
local function gethumanoid()
local character = player.Character
return character and character:FindFirstChildOfClass("Humanoid")
end
local function getroot()
local character = player.Character
return character and character:FindFirstChild("HumanoidRootPart")
end
local function fetch()
if not (load and load:IsA("RemoteFunction")) then return nil end
local ok, result = pcall(load.InvokeServer, load)
if ok and type(result) == "table" then return result end
end
local function request(stat, amount, replace)
if not (update and update:IsA("RemoteFunction")) then
return false, "UpdateStat is unavailable"
end
local ok, accepted = pcall(update.InvokeServer, update, stat, amount, replace)
if not ok then return false, tostring(accepted) end
if accepted == false then return false, "Server explicitly rejected " .. stat end
if accepted == nil then return true, "No confirmation returned" end
return true, "Response received"
end
local function mirror(stat, amount, replace)
if type(getgc) ~= "function" then return false end
local ok, objects = pcall(getgc, true)
if not ok or type(objects) ~= "table" then return false end
local changed = false
for _, object in ipairs(objects) do
if type(object) == "table" and rawget(object, "fuellevel") ~= nil
and rawget(object, "totalbreaks") ~= nil and rawget(object, "money") ~= nil
and rawget(object, "breakslevel") ~= nil then
local old = rawget(object, stat)
if replace or type(old) ~= "number" then
rawset(object, stat, amount)
else
rawset(object, stat, old + amount)
end
changed = true
end
end
return changed
end
local http = game:GetService("HttpService")
local environment = _G
if type(getgenv) == "function" then
local ok, result = pcall(getgenv)
if ok and type(result) == "table" then environment = result end
end
local extension = {
id = "broken_bones_iv",
name = "Broken Bones IV",
version = "5.1.0",
developer = "Luna.dev",
kind = "game_specific",
bundled = true,
standalone = true,
settings_path = "luna.dev/scripts/broken_bones_iv.json",
shared_settings_path = "luna.dev/settings.json",
update_policy = "user_choice"
}
local folder = "luna.dev"
local scriptsfolder = folder .. "/scripts"
local settingsfile = folder .. "/settings.json"
local extensionfile = extension.settings_path
local legacyfile = "luna.dev_settings.json"
local consentkey = "luna_dev_accepted_v1"
local globalinfo = "Shared Luna.dev disclaimer and main menu settings. Deleting this file resets global preferences and disclaimer acceptance; preserved legacy settings may be imported again. Extension preferences remain separate. Do not store passwords here."
local extensioninfo = "Broken Bones IV extension preferences. Deleting this file resets only this extension's saved settings. Luna.dev preserves legacy files during migration."
local file_storage = type(readfile) == "function" and type(writefile) == "function" and type(makefolder) == "function"
local jsonorder = {
_info = 1, scope = 2, version = 3, disclaimer = 4, global = 5,
main_menu = 6, migrations = 7, values = 3, restore = 4, startup = 1, respawn = 2,
accepted = 1
}
local function prettyjson(value, depth)
if type(value) ~= "table" then return http:JSONEncode(value) end
local count, biggest, array = 0, 0, true
for key in pairs(value) do
count += 1
if type(key) ~= "number" or key % 1 ~= 0 or key < 1 then
array = false
else
biggest = math.max(biggest, key)
end
end
if count == 0 then return "{}" end
array = array and biggest == count
local padding, inner = string.rep(" ", depth), string.rep(" ", depth + 1)
local lines = {}
if array then
for index = 1, count do
table.insert(lines, inner .. prettyjson(value[index], depth + 1))
end
return "[\n" .. table.concat(lines, ",\n") .. "\n" .. padding .. "]"
end
local keys = {}
for key in pairs(value) do table.insert(keys, key) end
table.sort(keys, function(a, b)
local left, right = jsonorder[a] or 100, jsonorder[b] or 100
if left ~= right then return left < right end
return tostring(a) < tostring(b)
end)
for _, key in ipairs(keys) do
table.insert(lines, inner .. http:JSONEncode(tostring(key)) .. ": "
.. prettyjson(value[key], depth + 1))
end
return "{\n" .. table.concat(lines, ",\n") .. "\n" .. padding .. "}"
end
local function rawfile(path)
if type(readfile) ~= "function" then return nil end
local ok, content = pcall(readfile, path)
if ok and type(content) == "string" then return content end
end
local function readjson(path)
local raw = rawfile(path)
if raw == nil then return nil end
local ok, data = pcall(http.JSONDecode, http, raw)
if ok and type(data) == "table" then return data end
return nil, "Invalid JSON in " .. path .. "; preserving the existing file."
end
local function makefolders()
if not file_storage then return false, "Executor file storage or folder creation is unavailable" end
for _, path in ipairs({folder, scriptsfolder}) do
local present = false
if type(isfolder) == "function" then
local ok, found = pcall(isfolder, path)
present = ok and found == true
end
if not present then
local ok, err = pcall(makefolder, path)
if not ok then
if type(isfolder) ~= "function" then return false, tostring(err) end
local checked, found = pcall(isfolder, path)
if not checked or not found then return false, tostring(err) end
end
end
end
return true
end
local function writejson(path, data)
local ok, err = makefolders()
if not ok then return false, err end
local encoded, content = pcall(prettyjson, data, 0)
if not encoded then return false, tostring(content) end
local written, reason = pcall(writefile, path, content)
if not written then return false, tostring(reason) end
return true
end
local function defaultglobal()
return {_info = globalinfo, scope = "luna.dev", version = 1,
disclaimer = {accepted = false, version = 1}, global = {}, main_menu = {}, migrations = {}}
end
local function defaultextension()
return {_info = extensioninfo, version = 1, values = {},
restore = {startup = {}, respawn = {}}}
end
local legacy, legacyerror = readjson(legacyfile)
local shareddata, globalerror = readjson(settingsfile)
local had_new_global = shareddata ~= nil
if globalerror then
warn("Luna.dev: " .. globalerror)
return
end
if not shareddata then
shareddata = defaultglobal()
if type(legacy) == "table" and legacy.scope == "luna.dev" then
for key, value in pairs(legacy) do
if key ~= "scripts" and key ~= "_info" then shareddata[key] = value end
end
end
end
shareddata._info = globalinfo
shareddata.scope = "luna.dev"
shareddata.version = 1
if type(shareddata.disclaimer) ~= "table" then shareddata.disclaimer = {} end
if type(shareddata.migrations) ~= "table" then shareddata.migrations = {} end
local extensiondata, extensionerror = readjson(extensionfile)
local had_new_extension = extensiondata ~= nil
if extensionerror then
warn("Luna.dev: " .. extensionerror)
return
end
if not had_new_global and had_new_extension then
shareddata = defaultglobal()
end
local migratedextension = false
if not extensiondata then
extensiondata = defaultextension()
local oldprefs = type(legacy) == "table" and type(legacy.scripts) == "table"
and legacy.scripts.broken_bones_iv or nil
if type(oldprefs) == "table" and shareddata.migrations.broken_bones_iv ~= true then
for key, value in pairs(oldprefs) do
if key == "restore" and type(value) == "table" then
extensiondata.restore = value
else
extensiondata.values[key] = value
end
end
migratedextension = true
end
end
extensiondata._info = extensioninfo
extensiondata.version = 1
if type(extensiondata.values) ~= "table" then extensiondata.values = {} end
if type(extensiondata.restore) ~= "table" then extensiondata.restore = {} end
if type(extensiondata.restore.startup) ~= "table" then extensiondata.restore.startup = {} end
if type(extensiondata.restore.respawn) ~= "table" then extensiondata.restore.respawn = {} end
local savedbb4 = extensiondata.values
local function preferred(key, default)
local value = savedbb4[key]
if type(value) == "string" or type(value) == "number" then return tostring(value) end
return tostring(default)
end
local function saveglobal(change)
local latest, err = readjson(settingsfile)
if err then return false, err end
latest = latest or shareddata or defaultglobal()
if type(latest.disclaimer) ~= "table" then latest.disclaimer = {} end
if type(latest.migrations) ~= "table" then latest.migrations = {} end
latest.scope, latest.version, latest._info = "luna.dev", 1, globalinfo
change(latest)
local ok, reason = writejson(settingsfile, latest)
if ok then shareddata = latest end
return ok, reason
end
local savedfile_raw = rawfile(extensionfile)
if migratedextension then
local ok, err = writejson(extensionfile, extensiondata)
if ok then
savedfile_raw = rawfile(extensionfile)
saveglobal(function(data) data.migrations.broken_bones_iv = true end)
else
warn("Luna.dev: legacy BB4 settings loaded in memory, but migration could not be saved: " .. tostring(err))
end
end
local acceptednotice = shareddata.disclaimer.accepted == true
if not acceptednotice then
local oldjson = readjson("luna.dev_accepted.json")
local oldkey = "luna_dev_bb4_consent_v1_" .. tostring(player.UserId)
local oldtxt = rawfile("luna_dev_bb4_consent_" .. tostring(player.UserId) .. ".txt")
local oldaccept = (type(oldjson) == "table" and oldjson.scope == "luna.dev" and oldjson.accepted == true)
or (type(legacy) == "table" and type(legacy.disclaimer) == "table" and legacy.disclaimer.accepted == true)
or oldtxt == "accepted_v1" or environment[oldkey] == true or environment[consentkey] == true
if oldaccept and not had_new_extension then
acceptednotice = true
local ok, err = saveglobal(function(data)
data.disclaimer.accepted = true
data.disclaimer.version = 1
end)
if not ok then warn("Luna.dev: old disclaimer accepted but new file could not be written: " .. tostring(err)) end
end
end
if acceptednotice then environment[consentkey] = true end
if not acceptednotice then
local consentgui = create("ScreenGui", nil, {
Name = "LunaDevBB4Consent", ResetOnSpawn = false,
DisplayOrder = 1000, ZIndexBehavior = Enum.ZIndexBehavior.Sibling
})
local parentedconsent = pcall(function() consentgui.Parent = game:GetService("CoreGui") end)
if not parentedconsent then consentgui.Parent = player:WaitForChild("PlayerGui") end
local shade = create("Frame", consentgui, {
Size = UDim2.fromScale(1, 1), BackgroundColor3 = Color3.fromRGB(8, 7, 13),
BackgroundTransparency = 0.23, BorderSizePixel = 0, Active = true
})
local viewport = workspace.CurrentCamera.ViewportSize
local panel = round(create("Frame", shade, {
AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5),
Size = UDim2.fromOffset(math.max(210, math.min(440, viewport.X - 24)),
math.max(220, math.min(316, viewport.Y - 24))),
BackgroundColor3 = Color3.fromRGB(19, 17, 29), BorderSizePixel = 0
}), 12)
create("UIStroke", panel, {Color = Color3.fromRGB(128, 76, 216), Thickness = 1.3})
local heading = label(panel, "luna.dev / before you continue", 16, purple, 35)
heading.Position, heading.Size = UDim2.fromOffset(16, 13), UDim2.new(1, -32, 0, 35)
heading.Font = Enum.Font.GothamBold
local warning = create("ScrollingFrame", panel, {
Position = UDim2.fromOffset(16, 57), Size = UDim2.new(1, -32, 1, -164),
BackgroundTransparency = 1, BorderSizePixel = 0,
AutomaticCanvasSize = Enum.AutomaticSize.Y, CanvasSize = UDim2.fromOffset(0, 0),
ScrollBarThickness = 3, ScrollBarImageColor3 = purple
})
local message = label(warning,
"Third-party scripts may violate game rules and lead to kicks, bans, lost progress, or other penalties. Luna.dev cannot guarantee safety and is not responsible for game-issued penalties from using this menu.\n\nBy selecting Accept, you take responsibility for choosing to run it.",
13, pale, 132)
message.Size = UDim2.new(1, -8, 0, 0)
message.AutomaticSize, message.TextYAlignment = Enum.AutomaticSize.Y, Enum.TextYAlignment.Top
local persistence = label(panel,
"Acceptance is shared through luna.dev/settings.json. Extension preferences are saved separately.",
10, muted, 35)
persistence.Position = UDim2.new(0, 16, 1, -102)
persistence.Size = UDim2.new(1, -32, 0, 34)
local accept = button(panel, "ACCEPT & CONTINUE", function() end, 43)
accept.Position = UDim2.new(0, 16, 1, -55)
accept.Size, accept.BackgroundColor3 = UDim2.new(0.5, -21, 0, 43), Color3.fromRGB(116, 65, 202)
accept.Font = Enum.Font.GothamBold
local decline = button(panel, "DECLINE & EXIT", function() end, 43)
decline.Position, decline.Size = UDim2.new(0.5, 5, 1, -55), UDim2.new(0.5, -21, 0, 43)
decline.Font = Enum.Font.GothamBold
local decision, chosen = Instance.new("BindableEvent"), false
accept.MouseButton1Click:Connect(function()
if chosen then return end
chosen = true
decision:Fire(true)
end)
decline.MouseButton1Click:Connect(function()
if chosen then return end
chosen = true
decision:Fire(false)
end)
local allowed = decision.Event:Wait()
consentgui:Destroy()
decision:Destroy()
if not allowed then return end
environment[consentkey] = true
local ok, err = saveglobal(function(data)
data.disclaimer.accepted = true
data.disclaimer.version = 1
end)
if not ok then warn("Luna.dev: disclaimer acceptance could not be saved: " .. tostring(err)) end
end
local existing = game:GetService("CoreGui"):FindFirstChild("LunaDevBrokenBonesIV")
if not existing then
local pg = player:FindFirstChild("PlayerGui")
existing = pg and pg:FindFirstChild("LunaDevBrokenBonesIV")
end
if existing then
existing.Enabled = true
return
end
local gui = create("ScreenGui", nil, {
Name = "LunaDevBrokenBonesIV", ResetOnSpawn = false, IgnoreGuiInset = false,
ZIndexBehavior = Enum.ZIndexBehavior.Sibling
})
local parented = pcall(function() gui.Parent = game:GetService("CoreGui") end)
if not parented then gui.Parent = player:WaitForChild("PlayerGui") end
local function windowdimensions()
local viewport = (workspace.CurrentCamera or camera).ViewportSize
return math.max(200, math.min(520, viewport.X - 24)),
math.max(200, math.min(420, viewport.Y - 24))
end
local initialwidth, initialheight = windowdimensions()
local window = round(create("Frame", gui, {
AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5),
Size = UDim2.fromOffset(initialwidth, initialheight),
BackgroundColor3 = Color3.fromRGB(19, 17, 29),
BorderSizePixel = 0, Active = true
}), 12)
create("UISizeConstraint", window, {MaxSize = Vector2.new(520, 420), MinSize = Vector2.new(200, 44)})
create("UIStroke", window, {Color = Color3.fromRGB(104, 67, 166), Thickness = 1.2})
local header = round(create("Frame", window, {
Size = UDim2.new(1, 0, 0, 44), BackgroundColor3 = Color3.fromRGB(32, 25, 51),
BorderSizePixel = 0, Active = true
}), 12)
local brand = label(header, "luna.dev", 14, purple, 44)
brand.Position, brand.Size = UDim2.fromOffset(12, 0), UDim2.fromOffset(69, 44)
brand.TextWrapped = false
brand.Font = Enum.Font.GothamBold
local unsaveddot = round(create("Frame", header, {
Position = UDim2.fromOffset(81, 19), Size = UDim2.fromOffset(7, 7),
BackgroundColor3 = Color3.fromRGB(192, 132, 252), BorderSizePixel = 0,
Visible = false
}), 7)
local subtitle = label(header, "/ broken bones iv", 12, muted, 44)
subtitle.Position, subtitle.Size = UDim2.fromOffset(100, 0), UDim2.new(1, -194, 1, 0)
subtitle.TextWrapped = false
subtitle.TextTruncate = Enum.TextTruncate.AtEnd
local x = round(create("TextButton", header, {
AnchorPoint = Vector2.new(1, 0), Position = UDim2.new(1, -8, 0, 7),
Size = UDim2.fromOffset(29, 29), BorderSizePixel = 0,
BackgroundColor3 = Color3.fromRGB(65, 36, 64), Text = "×",
TextColor3 = pale, Font = Enum.Font.GothamBold, TextSize = 17
}), 7)
local minimize = round(create("TextButton", header, {
AnchorPoint = Vector2.new(1, 0), Position = UDim2.new(1, -42, 0, 7),
Size = UDim2.fromOffset(29, 29), BorderSizePixel = 0,
BackgroundColor3 = surface, Text = "−", TextColor3 = pale,
Font = Enum.Font.GothamBold, TextSize = 17
}), 7)
local body = create("Frame", window, {
Position = UDim2.fromOffset(12, 50), Size = UDim2.new(1, -24, 1, -59),
BackgroundTransparency = 1
})
local tabs = create("ScrollingFrame", body, {
Size = UDim2.new(1, 0, 0, 35), BackgroundTransparency = 1,
ScrollBarThickness = 0, ScrollingDirection = Enum.ScrollingDirection.X,
CanvasSize = UDim2.fromOffset(0, 0), AutomaticCanvasSize = Enum.AutomaticSize.X
})
create("UIListLayout", tabs, {FillDirection = Enum.FillDirection.Horizontal, Padding = UDim.new(0, 6), SortOrder = Enum.SortOrder.LayoutOrder})
local content = create("Frame", body, {
Position = UDim2.fromOffset(0, 42), Size = UDim2.new(1, 0, 1, -103),
BackgroundTransparency = 1
})
local status = label(body, "Ready. You can keep using other tabs during purchases.", 11, muted, 34)
status.Position = UDim2.new(0, 0, 1, -58)
status.TextYAlignment = Enum.TextYAlignment.Center
local progress = round(create("Frame", body, {
Position = UDim2.new(0, 0, 1, -16), Size = UDim2.new(1, 0, 0, 5),
BackgroundColor3 = surface, BorderSizePixel = 0
}), 4)
local bar = round(create("Frame", progress, {
Size = UDim2.fromScale(0, 1), BackgroundColor3 = purple,
BorderSizePixel = 0
}), 4)
local pages = {}
local nav = {}
local inputfields = {}
local restoring = {startup = {}, respawn = {}}
local restorekeys = {"fly", "noclip", "infinitejump", "speed", "jump", "looplaunch", "gravity", "fullbright", "freecam", "antiafk"}
for _, phase in ipairs({"startup", "respawn"}) do
for _, key in ipairs(restorekeys) do
restoring[phase][key] = extensiondata.restore[phase][key] == true
end
end
local updateunsaved = function() end
local settingbaseline
local forceunsaved = false
local active = true
local running = false
local cancelled = false
local flags = {fly = false, noclip = false, infinitejump = false, fullbright = false,
antiafk = false, freecam = false, gravity = false, speed = false, jump = false, looplaunch = false}
local togglecontrols = {}
local connections = {}
local saved = {gravity = workspace.Gravity, brightness = lighting.Brightness,
ambient = lighting.Ambient, outdoor = lighting.OutdoorAmbient,
clock = lighting.ClockTime, fov = camera.FieldOfView}
local settings = {speed = 80, jump = 100, fly = 65, gravity = 45, launch = 200, launchinterval = 0.25, fov = 90}
for key in pairs(settings) do
local value = tonumber(savedbb4[key])
if value and value == value and value ~= math.huge and value ~= -math.huge
and (key ~= "launchinterval" or value > 0) then
settings[key] = value
end
end
local saved_delay = tonumber(savedbb4.purchase_delay)
if saved_delay and saved_delay == saved_delay and saved_delay ~= math.huge and saved_delay >= 0 then
purchase_delay = saved_delay
end
local lastlaunch = 0
local originals = setmetatable({}, {__mode = "k"})
local function notice(message) if active then status.Text = tostring(message) end end
local function addconnection(connection) table.insert(connections, connection) end
local function switch(name)
for title, page in pairs(pages) do page.Visible = title == name end
for title, tab in pairs(nav) do
tab.BackgroundColor3 = title == name and Color3.fromRGB(88, 53, 146) or surface
end
end
local function page(name)
local view = create("ScrollingFrame", content, {
Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, Visible = false,
BorderSizePixel = 0, ScrollBarThickness = 3, ScrollBarImageColor3 = purple,
CanvasSize = UDim2.fromOffset(0, 0), AutomaticCanvasSize = Enum.AutomaticSize.Y
})
create("UIPadding", view, {PaddingRight = UDim.new(0, 7), PaddingBottom = UDim.new(0, 6)})
create("UIListLayout", view, {Padding = UDim.new(0, 7), SortOrder = Enum.SortOrder.LayoutOrder})
local tab = round(create("TextButton", tabs, {
Size = UDim2.fromOffset(math.max(72, #name * 8 + 23), 31),
BackgroundColor3 = surface, BorderSizePixel = 0, Text = name,
TextColor3 = pale, TextSize = 12, Font = Enum.Font.GothamMedium
}))
tab.MouseButton1Click:Connect(function() switch(name) end)
pages[name] = view
nav[name] = tab
return view
end
local function field(parent, caption, initial)
label(parent, caption, 12, muted)
local box = round(create("TextBox", parent, {
Size = UDim2.new(1, 0, 0, 34), BackgroundColor3 = surface,
BorderSizePixel = 0, Text = tostring(initial), ClearTextOnFocus = false,
PlaceholderText = tostring(initial), Font = Enum.Font.Code, TextSize = 13,
TextColor3 = pale, TextXAlignment = Enum.TextXAlignment.Center
}))
table.insert(inputfields, box)
box:GetPropertyChangedSignal("Text"):Connect(function() updateunsaved() end)
return box
end
local function finite(text)
local value = tonumber(text)
if not value or value ~= value or value == math.huge or value == -math.huge then return nil end
return value
end
local function number(text, low, high)
local value = finite(text)
if not value then return nil end
return math.clamp(value, low, high)
end
local function setflag(key, enabled)
flags[key] = enabled
local item = togglecontrols[key]
if item then
item.control.Text = item.title .. (enabled and " ON" or " OFF")
item.control.BackgroundColor3 = enabled and Color3.fromRGB(72, 47, 115) or surface
end
end
local function toggle(parent, title, key, callback)
local control = button(parent, title .. " OFF", function()
local enabled = not flags[key]
setflag(key, enabled)
if callback then callback(enabled) end
end)
togglecontrols[key] = {control = control, title = title, callback = callback}
return control
end
local function refreshdata()
local data = fetch()
if data then
notice("Money: " .. tostring(data.money) .. " | Fuel: " .. tostring(data.fuellevel) .. " | Breaks: " .. tostring(data.totalbreaks))
else
notice("Couldn't load server stats.")
end
return data
end
local economy = page("Economy")
label(economy, "Money, upgrades, utilities and maps", 14, pale, 25).Font = Enum.Font.GothamBold
label(economy, "Give Money is optional. Auto-buy works separately. After the second money request, rejoin, inject Luna.dev again and start background auto-buy.", 11, muted, 58)
local cashbox = field(economy, "Money request", preferred("money_amount", money_amount))
local delaybox = field(economy, "Auto-buy delay (seconds; default 0.05)", purchase_delay)
local upgrade_status = label(economy, "Upgrade levels: checking...", 11, muted, 156)
upgrade_status.TextYAlignment = Enum.TextYAlignment.Top
local function showlevels(data)
if not active or not data then return end
local rows = {"UPGRADE LEVELS — LOADDATA"}
local complete, unknown = 0, 0
for _, item in ipairs(upgrades) do
local current = tonumber(data[item[1] .. "level"])
local name = item[1]:sub(1, 1):upper() .. item[1]:sub(2)
if current then
local level = math.floor(current)
if level >= 300 then complete += 1 end
table.insert(rows, string.format("%s: %d/300%s", name, level,
level >= 300 and " MAX" or " (" .. tostring(300 - level) .. " remaining)"))
else
unknown += 1
table.insert(rows, name .. ": unknown — not assumed to be level 0")
end
end
local humanoid = gethumanoid()
if humanoid then
table.insert(rows, string.format("Current character: WalkSpeed %.2f · JumpPower %.2f",
humanoid.WalkSpeed, humanoid.JumpPower))
end
table.insert(rows, string.format("Maxed: %d/10 · Unknown: %d", complete, unknown))
upgrade_status.Text = table.concat(rows, "\n")
return complete, unknown
end
local scanbusy = false
local function scanupgrades()
if scanbusy or not active then return end
scanbusy = true
task.spawn(function()
local data = fetch()
scanbusy = false
if not active then return end
if data then
local complete, unknown = showlevels(data)
if not running then
notice(string.format("Upgrade check: %d/10 at 300, %d unknown. Speed and Jump are listed separately above.",
complete, unknown))
end
elseif not running then
upgrade_status.Text = "Unable to read upgrade levels from LoadData. No purchase requests sent."
notice("Could not read upgrade levels. Check the game connection, then retry.")
end
end)
end
button(economy, "CHECK ALL UPGRADE LEVELS", scanupgrades, 38)
local money_busy = false
local money_presses = 0
local moneybutton
local start
local function updateeconomybuttons()
if not active then return end
if moneybutton then
moneybutton.Text = money_busy and "SENDING MONEY REQUEST..."
or (money_presses == 0 and "GIVE MONEY · FIRST PRESS"
or "GIVE MONEY AGAIN · DISCONNECT")
moneybutton.Active = not money_busy and not running and money_presses < 2
moneybutton.AutoButtonColor = moneybutton.Active
moneybutton.BackgroundColor3 = moneybutton.Active and Color3.fromRGB(116, 65, 202)
or Color3.fromRGB(65, 49, 89)
end
if start then
start.Text = running and "STOP AUTO-BUY" or "START AUTO-BUY IN BACKGROUND"
start.Active = not money_busy
start.AutoButtonColor = start.Active
start.BackgroundColor3 = running and Color3.fromRGB(91, 47, 108)
or (start.Active and Color3.fromRGB(66, 47, 110) or surface)
start.TextColor3 = start.Active and pale or muted
end
end
moneybutton = button(economy, "GIVE MONEY · FIRST PRESS", function()
if money_busy or running or money_presses >= 2 then return end
local amount = finite(cashbox.Text)
if not amount or amount <= 0 then
notice("Enter a positive, finite money amount.")
return
end
money_busy = true
money_presses += 1
updateeconomybuttons()
notice("Sending money request " .. money_presses .. "/2...")
task.spawn(function()
local ok, response = request("money", amount, false)
if not active then return end
if money_presses == 2 then
notice(ok and "Money request sent. Disconnecting; rejoin manually."
or "Money request failed: " .. tostring(response) .. ". Disconnecting; rejoin manually.")
task.wait(0.3)
if active then
player:Kick("Luna.dev | Money requests sent. Rejoin Broken Bones IV, inject Luna.dev again, then open Economy and press START AUTO-BUY IN BACKGROUND to purchase upgrades, utilities and maps.")
end
return
end
money_busy = false
updateeconomybuttons()
if not ok then
notice("Money request failed: " .. tostring(response) .. ". Auto-buy is still available.")
elseif response == "No confirmation returned" then
notice("Request sent with no confirmation returned. Rejoin to check whether the balance saved.")
else
notice("Money request returned a response. You can request again or start auto-buy.")
end
end)
end, 58)
moneybutton.Font = Enum.Font.GothamBold
moneybutton.TextSize = 15
create("UIStroke", moneybutton, {Color = Color3.fromRGB(183, 133, 255), Thickness = 1.4})
local function applydelay()
local delay = finite(delaybox.Text)
if not delay or delay < 0 then
notice("Enter a non-negative, finite delay in seconds (example: 0.05).")
return false
end
purchase_delay = delay
notice(purchase_delay == 0
and "No added timer. Each purchase still waits for two server responses."
or "Auto-buy delay: " .. tostring(purchase_delay) .. "s between purchases.")
return true
end
button(economy, "APPLY AUTO-BUY DELAY", applydelay)
label(economy, "0 removes the extra wait, but each purchase still needs two server responses. The game may also throttle requests; this cannot be made instant by changing a timer.", 11, muted, 56)
start = button(economy, "START AUTO-BUY IN BACKGROUND", function()
if running then
cancelled = true
notice("Stopping after the current request...")
return
end
if money_busy then
notice("Wait until the current money request finishes.")
return
end
if not applydelay() then return end
running = true
cancelled = false
updateeconomybuttons()
bar.Size = UDim2.fromScale(0, 1)
task.spawn(function()
local ok, message = pcall(function()
notice("Reading current upgrade levels before purchasing...")
local data = fetch()
if not data then error("Unable to read current upgrade levels; no purchases started", 0) end
local complete, unknown = showlevels(data)
if unknown > 0 then
error("Some upgrade levels are missing from LoadData. No purchases started; check the upgrade reader.", 0)
end
if complete == #upgrades then
notice("All regular upgrades already report 300/300. Checking utilities and maps...")
else
notice(string.format("Starting with %d/10 upgrades maxed. Speed %s/300 · Jump %s/300.",
complete, tostring(data.speedlevel), tostring(data.jumplevel)))
end
local total, done = 0, 0
local remoteelapsed = 0
local incomplete = {}
for _, item in ipairs(upgrades) do
total += 300 - math.clamp(math.floor(tonumber(data[item[1] .. "level"]) or 0), 0, 300)
end
for _, item in ipairs(utilities) do
total += 5 - math.clamp(math.floor(tonumber(data[item[1]]) or 0), 0, 5)
end
for index, costs in ipairs(maps) do
for stage in ipairs(costs) do
if not data["map" .. index .. "stage" .. stage] then total += 1 end
end
end
local function check()
if cancelled or not active then error("Auto-buy stopped", 0) end
end
local function purchase(stat, amount, replace, cost)
check()
if cost > 0 then
local started = os.clock()
local paid, err = request("money", -cost, false)
remoteelapsed += os.clock() - started
if not paid then error(err, 0) end
end
check()
local started = os.clock()
local accepted, err = request(stat, amount, replace)
remoteelapsed += os.clock() - started
if not accepted then error(err, 0) end
done += 1
if active then
bar.Size = UDim2.fromScale(total > 0 and done / total or 1, 1)
if done % 15 == 0 or done == total then
notice(string.format("Sent %s · %d/%d · remote wait %.2fs/item", stat, done, total, remoteelapsed / done))
end
if done % 30 == 0 then
local latest = fetch()
if latest then showlevels(latest) end
end
end
if purchase_delay > 0 then
task.wait(purchase_delay)
end
end
for _, item in ipairs(upgrades) do
check()
local stat = item[1] .. "level"
local snapshot = fetch()
if snapshot then
data = snapshot
showlevels(data)
end
local reported = tonumber(data[stat])
if not reported then error("Cannot read " .. stat .. "; stopping rather than guessing its level", 0) end
local level = math.clamp(math.floor(reported), 0, 300)
while level < 300 do
local nextlevel = level + 1
local cost = level < 100 and item[2] * nextlevel ^ 2
or math.floor(10 ^ (nextlevel / 100) * 1000 * item[2])
purchase(stat, 1, false, cost)
level = nextlevel
end
local latest = fetch()
if latest then
showlevels(latest)
local actual = tonumber(latest[stat])
if not actual or actual < 300 then
table.insert(incomplete, stat .. " " .. tostring(actual or "unknown") .. "/300")
end
else
table.insert(incomplete, stat .. " (could not verify)")
end
end
for _, item in ipairs(utilities) do
local level = math.clamp(math.floor(tonumber(data[item[1]]) or 0), 0, 5)
while level < 5 do
local nextlevel = level + 1
purchase(item[1], nextlevel, true, item[2][nextlevel])
level = nextlevel
end
end
for index, costs in ipairs(maps) do
for stage, cost in ipairs(costs) do
local stat = "map" .. index .. "stage" .. stage
if not data[stat] then purchase(stat, true, true, cost) end
end
end
check()
local final = fetch()
if final then
showlevels(final)
table.clear(incomplete)
for _, item in ipairs(upgrades) do
local stat = item[1] .. "level"
local actual = tonumber(final[stat])
if not actual or actual < 300 then
local itemtext = stat .. " " .. tostring(actual or "unknown") .. "/300"
if not table.find(incomplete, itemtext) then
table.insert(incomplete, itemtext)
end
end
end
else
table.insert(incomplete, "final server stats unavailable")
end
if #incomplete > 0 then
notice("Not yet 300: " .. table.concat(incomplete, ", ") .. ". Recheck the shop; you can rerun auto-buy from reported levels.")
else
notice("All regular upgrades reported 300/300. Rejoin only if the original game shop is still showing cached levels.")
end
end)
if active and not ok then notice(message) end
running = false
updateeconomybuttons()
end)
end, 46)
updateeconomybuttons()
scanupgrades()
button(economy, "REFRESH SERVER STATS", function()
task.spawn(function()
local data = refreshdata()
if data then showlevels(data) end
end)
end)
local utilitystatus = label(economy, "Already-owned level 5 utilities are skipped.", 11, muted, 28)
button(economy, "CHECK UTILITY LEVELS", function()
task.spawn(function()
local data = fetch()
if not data then notice("Couldn't load utility levels.") return end
local summary = {}
for _, item in ipairs(utilities) do
local current = math.clamp(math.floor(tonumber(data[item[1]]) or 0), 0, 5)
table.insert(summary, item[1] .. " " .. current .. "/5")
end
if active then utilitystatus.Text = table.concat(summary, " · ") end
end)
end)
local abilities = page("Abilities")
label(abilities, "Fuel affects all five utilities", 14, pale, 25).Font = Enum.Font.GothamBold
label(abilities, "Helium Balloon · Helicopter Hat · Rocket Boots · Jetpack · Gravity Bubble", 12, muted, 33)
local fuelbox = field(abilities, "Fuel level (maximum 300)", preferred("fuel_target", fuel_target))
label(abilities, "Above 300 may trigger a kick. This menu blocks any value over 300.", 11, Color3.fromRGB(255, 172, 126), 37)
button(abilities, "APPLY FUEL LEVEL (MAX 300)", function()
task.spawn(function()
local target = tonumber(fuelbox.Text)
if not target or target ~= target or target == math.huge or target == -math.huge
or target % 1 ~= 0 or target < 0 or target > fuel_target then
notice("Fuel must be a whole number from 0 to 300. Higher values are blocked.")
return
end
local data = fetch()
if not data then notice("Couldn't confirm your current fuel level.") return end
if (tonumber(data.fuellevel) or 0) >= target then
notice("Fuel is already level " .. tostring(data.fuellevel) .. ". No request needed.")
return
end
local ok, err = request("fuellevel", target, true)
if not ok then notice(err .. ". No further request sent.") return end
local verified = fetch()
if not verified or (tonumber(verified.fuellevel) or 0) < target then
notice("Requested fuel level wasn't confirmed; check server stats.")
return
end
local synced = mirror("fuellevel", target, true)
notice(synced and "Fuel level updated. Start a new run to refill the green bar."
or "Fuel updated. Rejoin if the UI is stale, then start a new run.")
end)
end, 40)
label(abilities, "Level 300 is the normal currency upgrade ceiling, not infinite fuel. All utilities share this bonus; a run that's already active keeps its original countdown.", 11, muted, 62)
local experience = page("XP helper")
label(experience, "Levels are calculated from total breaks", 14, pale, 25).Font = Enum.Font.GothamBold
label(experience, "Direct totalbreaks requests caused kicks. XP helper is read-only: earn breaks through regular runs, then refresh progress here.", 11, Color3.fromRGB(255, 172, 126), 61)
local levelbox = field(experience, "Target level (1–1395)", preferred("xp_target", 500))
local estimate = label(experience, "Enter a target to calculate your remaining breaks.", 12, muted, 67)
local function breaksforlevel(level)
if level <= 1 then return 0 end
if level < 100 then
return math.ceil(((level - 1) * 6.141934860599844) ^ 2.1552185512626503)
end
return math.ceil(10 ^ (level / 100) * 100000)
end
local function levelforbreaks(total)
if total <= 0 then return 1 end
if total < 1000000 then
return math.floor(total ^ 0.463990067 / 6.141934860599844 + 1)
end
return math.floor(math.log10(total / 100000) * 100)
end
local function formatvalue(value)
if value < 1e9 then return string.format("%.0f", value) end
return string.format("%.5g", value)
end
local xploading = false
button(experience, "REFRESH XP PROGRESS", function()
if xploading then return end
local target = finite(levelbox.Text)
if not target or target % 1 ~= 0 or target < 1 or target > 1395 then
notice("Enter a whole target level from 1 to 1395")
return
end
xploading = true
task.spawn(function()
local data = fetch()
xploading = false
if not active then return end
if not data or type(data.totalbreaks) ~= "number" then
notice("Couldn't read current total breaks")
return
end
local current = math.max(0, data.totalbreaks)
local required = breaksforlevel(target)
estimate.Text = "Current level: " .. formatvalue(levelforbreaks(current))
.. " • Total breaks: " .. formatvalue(current)
.. "\nTarget level: " .. tostring(target)
.. " • Needed: " .. formatvalue(math.max(0, required - current))
notice(current >= required and "Target level reached." or "XP progress refreshed. No stat changes sent.")
end)
end, 40)
local movement = page("Movement")
label(movement, "Local movement controls", 14, pale, 25).Font = Enum.Font.GothamBold
label(movement, "Suggested starting values (not safe/anti-kick guarantees): walk 24–80, jump 50–120, fly 30–100, gravity 50–196.2, launch 100–500. No script-imposed upper limits; extreme values can kick you or destabilize physics.", 11, Color3.fromRGB(255, 172, 126), 98)
local speedbox = field(movement, "Walk speed · suggested 24–80", settings.speed)
local jumpbox = field(movement, "Jump power · suggested 50–120", settings.jump)
local flybox = field(movement, "Fly speed · suggested 30–100", settings.fly)
local gravitybox = field(movement, "Local gravity · suggested 50–196.2", settings.gravity)
local launchbox = field(movement, "Launch strength · suggested 100–500", settings.launch)
local intervalbox = field(movement, "Loop launch interval (seconds) · suggested 0.25–1", settings.launchinterval)
local function applymovement()
local values = {
speed = finite(speedbox.Text), jump = finite(jumpbox.Text),
fly = finite(flybox.Text), gravity = finite(gravitybox.Text),
launch = finite(launchbox.Text), launchinterval = finite(intervalbox.Text)
}
for key, value in pairs(values) do
if not value then return false, "Invalid " .. key .. ": use a finite number" end
end
if values.launchinterval <= 0 then return false, "Loop interval must be greater than zero" end
for key, value in pairs(values) do settings[key] = value end
return true
end
local function applyandnotice()
local ok, err = applymovement()
if not ok then notice(err) return false end
if flags.gravity then
local applied, reason = pcall(function() workspace.Gravity = settings.gravity end)
if not applied then setflag("gravity", false) notice("Gravity rejected: " .. tostring(reason)) return false end
end
notice("Movement values updated. Extreme values may fail or trigger a game kick.")
return true
end
button(movement, "APPLY MOVEMENT VALUES", applyandnotice)
toggle(movement, "Walk speed override", "speed", function(on)
if on then
if not applymovement() then setflag("speed", false) notice("Enter finite movement values first") return end
local humanoid = gethumanoid()
if humanoid and not originals[humanoid] then
originals[humanoid] = {speed = humanoid.WalkSpeed, jump = humanoid.JumpPower,
usejump = humanoid.UseJumpPower}
end
else
local humanoid = gethumanoid()
if humanoid and originals[humanoid] then humanoid.WalkSpeed = originals[humanoid].speed end
end
end)
toggle(movement, "Jump power override", "jump", function(on)
if on then
if not applymovement() then setflag("jump", false) notice("Enter finite movement values first") return end
local humanoid = gethumanoid()
if humanoid and not originals[humanoid] then
originals[humanoid] = {speed = humanoid.WalkSpeed, jump = humanoid.JumpPower,
usejump = humanoid.UseJumpPower}
end
else
local humanoid = gethumanoid()
if humanoid and originals[humanoid] then
humanoid.JumpPower = originals[humanoid].jump
humanoid.UseJumpPower = originals[humanoid].usejump
end
end
end)
toggle(movement, "Infinite jump", "infinitejump")
toggle(movement, "Fly (WASD / Space / Ctrl)", "fly", function(on)
if on and not applymovement() then
setflag("fly", false)
notice("Enter finite movement values first")
return
end
notice(on and "Fly enabled; use movement keys." or "Fly disabled.")
end)
toggle(movement, "Noclip", "noclip")
toggle(movement, "Gravity override", "gravity", function(on)
if on and not applymovement() then
setflag("gravity", false)
notice("Enter finite movement values first")
return
end
local ok, err = pcall(function() workspace.Gravity = on and settings.gravity or saved.gravity end)
if not ok then setflag("gravity", false) notice("Gravity rejected: " .. tostring(err)) end
end)
local function launchonce()
local root = getroot()
if not root then return false, "Character isn't ready" end
if root.Anchored then return false, "Character is anchored" end
local ok, err = pcall(function()
root.AssemblyLinearVelocity += workspace.CurrentCamera.CFrame.LookVector * settings.launch
end)
if not ok then return false, "Launch rejected: " .. tostring(err) end
return true
end
button(movement, "LAUNCH IN LOOK DIRECTION", function()
local ok, err = applymovement()
if not ok then notice(err) return end
local launched, reason = launchonce()
notice(launched and "Launched in camera look direction." or reason)
end)
toggle(movement, "LOOP LAUNCH IN LOOK DIRECTION", "looplaunch", function(on)
if on then
local ok, err = applymovement()
if not ok then setflag("looplaunch", false) notice(err) return end
lastlaunch = 0
notice("Loop launch enabled. Toggle OFF to stop; fly may overwrite velocity.")
else
notice("Loop launch stopped.")
end
end)
local mapbox = field(movement, "Map (1–3) / stage (1–10), e.g. 3,10", preferred("map_stage", "1,1"))
button(movement, "TELEPORT TO MAP STAGE", function()
local a, b = mapbox.Text:match("^%s*(%d+)%s*,%s*(%d+)%s*$")
local map, stage = tonumber(a), tonumber(b)
if not map or not stage or map < 1 or map > 3 or stage < 1 or stage > 10 then
notice("Use map,stage (example: 3,10)") return
end
local folder = storage:FindFirstChild("Map" .. map .. "Spawns")
local spot = folder and folder:FindFirstChild("Stage" .. stage)
local root = getroot()
if root and spot and spot:IsA("Vector3Value") then
root.CFrame = CFrame.new(spot.Value + Vector3.new(0, 5, 0))
notice("Local teleport sent to map " .. map .. ", stage " .. stage)
else
notice("Spawn or character wasn't found")
end
end)
local gameplay = page("Gameplay")
label(gameplay, "Physics and session tools", 14, pale, 25).Font = Enum.Font.GothamBold
button(gameplay, "LOCAL RAGDOLL", function()
local humanoid = gethumanoid()
if humanoid then humanoid:ChangeState(Enum.HumanoidStateType.Physics) end
end)
button(gameplay, "STAND UP", function()
local humanoid = gethumanoid()
if humanoid then humanoid:ChangeState(Enum.HumanoidStateType.GettingUp) end
end)
toggle(gameplay, "Anti-AFK", "antiafk")
label(gameplay, "The game controls the normal crash/ragdoll cycle. Local physics may be overridden while a run is active.", 11, muted, 48)
local visuals = page("Visuals")
label(visuals, "Local visual controls", 14, pale, 25).Font = Enum.Font.GothamBold
local fovbox = field(visuals, "Field of view", settings.fov)
toggle(visuals, "Fullbright", "fullbright", function(on)
lighting.Brightness = on and 4 or saved.brightness
lighting.Ambient = on and Color3.new(1, 1, 1) or saved.ambient
lighting.OutdoorAmbient = on and Color3.new(1, 1, 1) or saved.outdoor
lighting.ClockTime = on and 12 or saved.clock
end)
button(visuals, "APPLY FOV", function()
settings.fov = number(fovbox.Text, 40, 120) or 90
camera.FieldOfView = settings.fov
end)
button(visuals, "RESET FOV", function() camera.FieldOfView = saved.fov end)
toggle(visuals, "Freecam (RMB + WASD)", "freecam", function(on)
if on then
camera.CameraType = Enum.CameraType.Scriptable
else
camera.CameraType = Enum.CameraType.Custom
camera.CameraSubject = gethumanoid()
end
end)
local restorepage = page("Restore")
label(restorepage, "Feature restoration", 14, pale, 25).Font = Enum.Font.GothamBold
label(restorepage, "Choose each feature's startup and respawn behaviour. Respawn only restores features that were enabled before death. Changes save only when you press Save Settings.", 11, muted, 76)
local restorelabels = {
fly = "Fly", noclip = "Noclip", infinitejump = "Infinite jump",
speed = "WalkSpeed", jump = "JumpPower", looplaunch = "Loop launch",
gravity = "Gravity", fullbright = "Fullbright", freecam = "Freecam", antiafk = "Anti-AFK"
}
for _, key in ipairs(restorekeys) do
label(restorepage, restorelabels[key], 12, pale, 22).Font = Enum.Font.GothamMedium
for _, phase in ipairs({"startup", "respawn"}) do
local control
local function repaint()
control.Text = (phase == "startup" and "On injection" or "After respawn")
.. (restoring[phase][key] and " · ON" or " · OFF")
control.BackgroundColor3 = restoring[phase][key]
and Color3.fromRGB(72, 47, 115) or surface
end
control = button(restorepage, "", function()
restoring[phase][key] = not restoring[phase][key]
repaint()
updateunsaved()
end, 30)
repaint()
end
end
local function settingssignature()
local pieces = {}
for _, entry in ipairs(inputfields) do table.insert(pieces, entry.Text) end
for _, phase in ipairs({"startup", "respawn"}) do
for _, key in ipairs(restorekeys) do
table.insert(pieces, restoring[phase][key] and "1" or "0")
end
end
return http:JSONEncode(pieces)
end
local function isdirty()
return forceunsaved or (settingbaseline ~= nil and settingssignature() ~= settingbaseline)
end
updateunsaved = function()
if not active then return end
unsaveddot.Visible = isdirty()
end
local function confirmation(title, description, yes, no)
local overlay = create("Frame", gui, {
Size = UDim2.fromScale(1, 1), BackgroundColor3 = Color3.fromRGB(5, 5, 9),
BackgroundTransparency = 0.25, BorderSizePixel = 0, Active = true,
ZIndex = 90
})
local panel = round(create("Frame", overlay, {
AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5),
Size = UDim2.fromOffset(math.max(215, math.min(405, camera.ViewportSize.X - 22)), 205),
BackgroundColor3 = Color3.fromRGB(24, 20, 36), BorderSizePixel = 0,
ZIndex = 91
}), 10)
create("UIStroke", panel, {Color = purple, Thickness = 1.2})
local headline = label(panel, title, 15, pale, 31)
headline.Position, headline.Size, headline.ZIndex = UDim2.fromOffset(14, 13), UDim2.new(1, -28, 0, 31), 92
headline.Font = Enum.Font.GothamBold
local explanation = label(panel, description, 12, muted, 78)
explanation.Position, explanation.Size, explanation.ZIndex = UDim2.fromOffset(14, 51), UDim2.new(1, -28, 0, 78), 92
explanation.TextYAlignment = Enum.TextYAlignment.Top
local done = Instance.new("BindableEvent")
local decision
local left = button(panel, no or "KEEP EDITING", function()
if decision ~= nil then return end
decision = false
done:Fire(false)
end, 42)
left.Position, left.Size, left.ZIndex = UDim2.new(0, 14, 1, -55), UDim2.new(0.5, -21, 0, 42), 92
local right = button(panel, yes or "CONTINUE", function()
if decision ~= nil then return end
decision = true
done:Fire(true)
end, 42)
right.Position, right.Size, right.ZIndex = UDim2.new(0.5, 7, 1, -55), UDim2.new(0.5, -21, 0, 42), 92
right.BackgroundColor3 = Color3.fromRGB(116, 65, 202)
local result = done.Event:Wait()
overlay:Destroy()
done:Destroy()
return result
end
local function requestclose()
if isdirty() and not confirmation("Unsaved settings",
"Closing Luna.dev will discard changes made since the last Save Settings.",
"CLOSE WITHOUT SAVING", "KEEP EDITING") then
return
end
gui:Destroy()
end
local options = page("Settings")
label(options, "Menu and session", 14, pale, 25).Font = Enum.Font.GothamBold
label(options, "Universal acceptance: luna.dev/settings.json. This extension: luna.dev/scripts/broken_bones_iv.json. All files are readable, indented JSON. Older settings files are retained.", 11, muted, 80)
local settingsstatus = label(options, "Changes are not saved automatically. The dot beside luna.dev means your settings have unsaved changes.", 11, muted, 58)
local function extensionprefs()
local amount, delay = finite(cashbox.Text), finite(delaybox.Text)
local fuel, xp, view = finite(fuelbox.Text), finite(levelbox.Text), finite(fovbox.Text)
local move = {
speed = finite(speedbox.Text), jump = finite(jumpbox.Text),
fly = finite(flybox.Text), gravity = finite(gravitybox.Text),
launch = finite(launchbox.Text), launchinterval = finite(intervalbox.Text)
}
if not amount or amount <= 0 or not delay or delay < 0 then
return nil, "Enter a positive money amount and non-negative auto-buy delay."
end
if not fuel or fuel % 1 ~= 0 or fuel < 0 or fuel > 300 then
return nil, "Fuel target must be a whole number between 0 and 300."
end
if not xp or xp % 1 ~= 0 or xp < 1 or xp > 1395 then
return nil, "XP target must be a whole number between 1 and 1395."
end
if not view or view < 40 or view > 120 then
return nil, "FOV must be between 40 and 120."
end
for key, value in pairs(move) do
if not value then return nil, "Invalid movement value: " .. key end
end
if move.launchinterval <= 0 then return nil, "Launch interval must be positive." end
local map, stage = mapbox.Text:match("^%s*(%d+)%s*,%s*(%d+)%s*$")
if not map or not stage or tonumber(map) > 3 or tonumber(map) < 1
or tonumber(stage) > 10 or tonumber(stage) < 1 then
return nil, "Map/stage must be 1,1 through 3,10."
end
local values = {
money_amount = cashbox.Text, purchase_delay = delaybox.Text,
fuel_target = fuelbox.Text, xp_target = levelbox.Text,
fov = fovbox.Text, map_stage = mapbox.Text,
speed = speedbox.Text, jump = jumpbox.Text, fly = flybox.Text,
gravity = gravitybox.Text, launch = launchbox.Text,
launchinterval = intervalbox.Text
}
local restore = {startup = {}, respawn = {}}
for _, phase in ipairs({"startup", "respawn"}) do
for _, key in ipairs(restorekeys) do
restore[phase][key] = restoring[phase][key] == true
end
end
return {values = values, restore = restore}, {delay = delay, move = move, fov = view}
end
local function resetextensionfile()
local default = defaultextension()
local ok, err = writejson(extensionfile, default)
if ok then
savedfile_raw = rawfile(extensionfile)
forceunsaved = true
updateunsaved()
settingsstatus.Text = "Saved BB4 preferences reset. Current inputs remain unchanged. Reinject to use defaults."
notice("Only BB4 settings were reset; your current session remains unchanged.")
end
return ok, err
end
local function savesettings()
local prefs, values = extensionprefs()
if not prefs then notice(values) return end
local currentraw = rawfile(extensionfile)
if currentraw ~= savedfile_raw then
if not confirmation("Saved settings changed",
"This extension's saved file was reset or changed elsewhere. Saving now will overwrite that change.",
"SAVE ANYWAY", "CANCEL") then return end
end
local data, err = readjson(extensionfile)
if err then notice(err) return end
data = data or defaultextension()
data._info, data.version = extensioninfo, 1
data.values, data.restore = prefs.values, prefs.restore
local ok, reason = writejson(extensionfile, data)
if not ok then
settingsstatus.Text = "Couldn't save: " .. tostring(reason)
notice("Save Settings failed: " .. tostring(reason))
return
end
savedfile_raw = rawfile(extensionfile)
purchase_delay = values.delay
for key, value in pairs(values.move) do settings[key] = value end
settings.fov = values.fov
settingbaseline, forceunsaved = settingssignature(), false
updateunsaved()
settingsstatus.Text = "Saved to luna.dev/scripts/broken_bones_iv.json. Other extensions are unchanged."
notice("BB4 settings saved.")
end
button(options, "SAVE SETTINGS", savesettings, 42)
button(options, "RESET SAVED BB4 SETTINGS", function()
if not confirmation("Reset Broken Bones IV?",
"Reset only this extension's saved JSON. Your current session remains unchanged. Older files are preserved.",
"RESET BB4", "CANCEL") then return end
local ok, err = resetextensionfile()
if not ok then notice("Couldn't reset BB4 settings: " .. tostring(err)) end
end, 36)
button(options, "REJOIN TO REFRESH GAME DATA", function()
cancelled = true
notice("Rejoining...")
if #players:GetPlayers() > 1 then
teleport:TeleportToPlaceInstance(game.PlaceId, game.JobId, player)
else
teleport:Teleport(game.PlaceId, player)
end
end)
button(options, "DESTROY LUNA.DEV MENU", requestclose)
label(options, "Money method credit: original Russian-language BB4 ReJoin Farm script (author unknown; no credit was listed in the supplied code).", 11, muted, 58)
label(options, "RightShift hides the window without discarding changes. Minimize keeps purchases active; closing warns about unsaved settings and cancels purchases. The second Give Money press disconnects you.", 11, muted, 64)
local dragging, origin, initial = false, nil, nil
addconnection(header.InputBegan:Connect(function(pointer)
if pointer.UserInputType ~= Enum.UserInputType.MouseButton1 and pointer.UserInputType ~= Enum.UserInputType.Touch then return end
dragging = true
origin, initial = pointer.Position, window.Position
addconnection(pointer.Changed:Connect(function()
if pointer.UserInputState == Enum.UserInputState.End then dragging = false end
end))
end))
addconnection(input.InputChanged:Connect(function(pointer)
if not dragging then return end
if pointer.UserInputType ~= Enum.UserInputType.MouseMovement and pointer.UserInputType ~= Enum.UserInputType.Touch then return end
local delta = pointer.Position - origin
window.Position = UDim2.new(initial.X.Scale, initial.X.Offset + delta.X,
initial.Y.Scale, initial.Y.Offset + delta.Y)
end))
local function fitwindow()
local width, height = windowdimensions()
window.Size = UDim2.fromOffset(width, body.Visible and height or 44)
end
minimize.MouseButton1Click:Connect(function()
body.Visible = not body.Visible
fitwindow()
minimize.Text = body.Visible and "−" or "+"
end)
addconnection(camera:GetPropertyChangedSignal("ViewportSize"):Connect(fitwindow))
x.MouseButton1Click:Connect(requestclose)
addconnection(input.InputBegan:Connect(function(key, processed)
if key.KeyCode == Enum.KeyCode.RightShift and not processed then
window.Visible = not window.Visible
end
if flags.infinitejump and not processed and key.KeyCode == Enum.KeyCode.Space then
local humanoid = gethumanoid()
if humanoid then humanoid:ChangeState(Enum.HumanoidStateType.Jumping) end
end
end))
addconnection(player.Idled:Connect(function()
if not flags.antiafk then return end
pcall(function()
virtualuser:CaptureController()
virtualuser:ClickButton2(Vector2.new())
end)
end))
local lastparts = {}
addconnection(runservice.Heartbeat:Connect(function(delta)
if not active then return end
local humanoid = gethumanoid()
local root = getroot()
if humanoid and (flags.speed or flags.jump) and not originals[humanoid] then
originals[humanoid] = {speed = humanoid.WalkSpeed, jump = humanoid.JumpPower,
usejump = humanoid.UseJumpPower}
end
if flags.speed and humanoid then
local ok, err = pcall(function() humanoid.WalkSpeed = settings.speed end)
if not ok then setflag("speed", false) notice("Walk speed rejected: " .. tostring(err)) end
end
if flags.jump and humanoid then
local ok, err = pcall(function()
humanoid.UseJumpPower = true
humanoid.JumpPower = settings.jump
end)
if not ok then setflag("jump", false) notice("Jump power rejected: " .. tostring(err)) end
end
if flags.gravity then
local ok, err = pcall(function() workspace.Gravity = settings.gravity end)
if not ok then setflag("gravity", false) notice("Gravity rejected: " .. tostring(err)) end
end
if flags.noclip and player.Character then
for _, part in ipairs(player.Character:GetDescendants()) do
if part:IsA("BasePart") then
if lastparts[part] == nil then lastparts[part] = part.CanCollide end
part.CanCollide = false
end
end
elseif next(lastparts) then
for part, collide in pairs(lastparts) do
if part.Parent then part.CanCollide = collide end
lastparts[part] = nil
end
end
if flags.fly and root and not root.Anchored then
local direction = Vector3.zero
local cf = camera.CFrame
if input:IsKeyDown(Enum.KeyCode.W) then direction += cf.LookVector end
if input:IsKeyDown(Enum.KeyCode.S) then direction -= cf.LookVector end
if input:IsKeyDown(Enum.KeyCode.D) then direction += cf.RightVector end
if input:IsKeyDown(Enum.KeyCode.A) then direction -= cf.RightVector end
if input:IsKeyDown(Enum.KeyCode.Space) then direction += Vector3.yAxis end
if input:IsKeyDown(Enum.KeyCode.LeftControl) then direction -= Vector3.yAxis end
local ok, err = pcall(function()
root.AssemblyLinearVelocity = direction.Magnitude > 0 and direction.Unit * settings.fly or Vector3.zero
end)
if not ok then setflag("fly", false) notice("Fly rejected: " .. tostring(err)) end
end
if flags.looplaunch then
lastlaunch += delta
if lastlaunch >= settings.launchinterval then
lastlaunch = 0
local ok, err = launchonce()
if not ok then
setflag("looplaunch", false)
notice(err .. ". Loop launch stopped.")
end
end
end
if flags.freecam and camera then
local direction = Vector3.zero
local cf = camera.CFrame
if input:IsKeyDown(Enum.KeyCode.W) then direction += cf.LookVector end
if input:IsKeyDown(Enum.KeyCode.S) then direction -= cf.LookVector end
if input:IsKeyDown(Enum.KeyCode.D) then direction += cf.RightVector end
if input:IsKeyDown(Enum.KeyCode.A) then direction -= cf.RightVector end
if input:IsMouseButtonPressed(Enum.UserInputType.MouseButton2) then
local mouse = input:GetMouseDelta()
cf *= CFrame.Angles(0, -mouse.X * 0.002, 0)
cf *= CFrame.Angles(-mouse.Y * 0.002, 0, 0)
end
camera.CFrame = cf + (direction.Magnitude > 0 and direction.Unit * delta * 80 or Vector3.zero)
end
end))
local characterkeys = {fly = true, noclip = true, infinitejump = true,
speed = true, jump = true, looplaunch = true}
local restoreonrespawn = nil
local function activatefeature(key)
if flags[key] then return end
local item = togglecontrols[key]
if not item then return end
setflag(key, true)
if item.callback then item.callback(true) end
end
addconnection(player.CharacterRemoving:Connect(function()
local snapshot = {}
for _, key in ipairs(restorekeys) do
snapshot[key] = restoring.respawn[key] and flags[key] == true
if characterkeys[key] then setflag(key, false) end
end
restoreonrespawn = snapshot
end))
addconnection(player.CharacterAdded:Connect(function(character)
local snapshot = restoreonrespawn
restoreonrespawn = nil
if not snapshot then return end
task.spawn(function()
local humanoid = character:WaitForChild("Humanoid", 15)
local root = character:WaitForChild("HumanoidRootPart", 15)
if not active or player.Character ~= character or not humanoid or not root then return end
for _, key in ipairs(restorekeys) do
if snapshot[key] then activatefeature(key) end
end
end)
end))
local registry = environment.luna_dev_extensions
if type(registry) ~= "table" then
registry = {}
environment.luna_dev_extensions = registry
end
registry[extension.id] = {
metadata = extension,
gui = gui,
isDirty = isdirty,
resetSaved = resetextensionfile,
close = requestclose,
settingsPath = extensionfile
}
settingbaseline = settingssignature()
updateunsaved()
task.spawn(function()
local character = player.Character or player.CharacterAdded:Wait()
if not active then return end
if not character:WaitForChild("Humanoid", 15) or not character:WaitForChild("HumanoidRootPart", 15) then return end
if not active or player.Character ~= character then return end
for _, key in ipairs(restorekeys) do
if restoring.startup[key] then activatefeature(key) end
end
end)
gui.Destroying:Connect(function()
if registry[extension.id] and registry[extension.id].gui == gui then
registry[extension.id] = nil
end
active = false
cancelled = true
for _, connection in ipairs(connections) do connection:Disconnect() end
for part, collide in pairs(lastparts) do
if part.Parent then part.CanCollide = collide end
end
workspace.Gravity = saved.gravity
lighting.Brightness = saved.brightness
lighting.Ambient = saved.ambient
lighting.OutdoorAmbient = saved.outdoor
lighting.ClockTime = saved.clock
camera.FieldOfView = saved.fov
if flags.freecam then
camera.CameraType = Enum.CameraType.Custom
camera.CameraSubject = gethumanoid()
end
local humanoid = gethumanoid()
if humanoid and originals[humanoid] then
if flags.speed then pcall(function() humanoid.WalkSpeed = originals[humanoid].speed end) end
if flags.jump then
pcall(function()
humanoid.JumpPower = originals[humanoid].jump
humanoid.UseJumpPower = originals[humanoid].usejump
end)
end
end
end)
switch("Economy")No comments yet for this script.



