Roblox Larping Legends 2 Script (October 2026)

PUBLISHEDUPDATED
1 Available ScriptFree

Grinding strength in Larping Legends 2 script can take forever if you just click away all day without any help.

You spend your time fighting other players, lifting weights to get stronger, and working your way up through rebirths and ascensions.

Using the right tools makes the whole process much faster, so let us check out what works right now.

Larping Legends 2 Script – Auto Ascend | Not Working & More

Auto Ascend | Not Working & MoreFree
Auto Ascend | Not WorkingCandy Corn Tween | WorkingAuto Lift | Working (Laggy)Walk Speed | WorkingGym Teleports | WorkingSettings | Working
local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")

local player = Players.LocalPlayer

--------------------------------------------------------------------
-- THEME PRESETS
--------------------------------------------------------------------
local Presets = {
	["Pumpkin"] = {
		Background = Color3.fromRGB(24, 24, 30), TitleBar = Color3.fromRGB(32, 32, 40),
		Panel = Color3.fromRGB(36, 36, 46), Item = Color3.fromRGB(46, 46, 58),
		Accent = Color3.fromRGB(255, 140, 0), On = Color3.fromRGB(60, 180, 75),
		Off = Color3.fromRGB(200, 60, 60), Text = Color3.fromRGB(235, 235, 240),
		SubText = Color3.fromRGB(150, 150, 165),
	},
	["Haunted Purple"] = {
		Background = Color3.fromRGB(28, 18, 40), TitleBar = Color3.fromRGB(38, 24, 54),
		Panel = Color3.fromRGB(44, 30, 62), Item = Color3.fromRGB(58, 40, 80),
		Accent = Color3.fromRGB(255, 140, 0), On = Color3.fromRGB(120, 200, 60),
		Off = Color3.fromRGB(200, 60, 60), Text = Color3.fromRGB(240, 235, 245),
		SubText = Color3.fromRGB(160, 145, 180),
	},
	["Midnight"] = {
		Background = Color3.fromRGB(15, 20, 35), TitleBar = Color3.fromRGB(20, 28, 48),
		Panel = Color3.fromRGB(26, 36, 60), Item = Color3.fromRGB(36, 48, 78),
		Accent = Color3.fromRGB(80, 160, 255), On = Color3.fromRGB(60, 180, 120),
		Off = Color3.fromRGB(210, 70, 80), Text = Color3.fromRGB(230, 236, 250),
		SubText = Color3.fromRGB(140, 155, 185),
	},
	["Blood Moon"] = {
		Background = Color3.fromRGB(25, 10, 12), TitleBar = Color3.fromRGB(40, 14, 18),
		Panel = Color3.fromRGB(48, 20, 24), Item = Color3.fromRGB(66, 28, 32),
		Accent = Color3.fromRGB(220, 40, 50), On = Color3.fromRGB(90, 190, 90),
		Off = Color3.fromRGB(150, 40, 40), Text = Color3.fromRGB(245, 230, 230),
		SubText = Color3.fromRGB(175, 140, 140),
	},
	["Toxic Green"] = {
		Background = Color3.fromRGB(14, 22, 16), TitleBar = Color3.fromRGB(20, 34, 24),
		Panel = Color3.fromRGB(26, 44, 30), Item = Color3.fromRGB(36, 60, 40),
		Accent = Color3.fromRGB(120, 230, 60), On = Color3.fromRGB(60, 200, 90),
		Off = Color3.fromRGB(200, 60, 60), Text = Color3.fromRGB(230, 245, 230),
		SubText = Color3.fromRGB(140, 175, 145),
	},
	["Light"] = {
		Background = Color3.fromRGB(240, 240, 245), TitleBar = Color3.fromRGB(225, 225, 235),
		Panel = Color3.fromRGB(250, 250, 255), Item = Color3.fromRGB(215, 215, 228),
		Accent = Color3.fromRGB(255, 140, 0), On = Color3.fromRGB(50, 170, 70),
		Off = Color3.fromRGB(210, 60, 60), Text = Color3.fromRGB(30, 30, 40),
		SubText = Color3.fromRGB(110, 110, 125),
	},
}
local PresetNames = { "Pumpkin", "Haunted Purple", "Midnight", "Blood Moon", "Toxic Green", "Light" }

local FontSets = {
	["Gotham"]      = { Regular = Enum.Font.Gotham,     Bold = Enum.Font.GothamBold },
	["Fredoka"]     = { Regular = Enum.Font.FredokaOne, Bold = Enum.Font.FredokaOne },
	["Source Sans"] = { Regular = Enum.Font.SourceSans, Bold = Enum.Font.SourceSansBold },
	["Arcade"]      = { Regular = Enum.Font.Arcade,     Bold = Enum.Font.Arcade },
}
local FontNames = { "Gotham", "Fredoka", "Source Sans", "Arcade" }

-- Live theme state (mutated by the Settings tab)
local Theme = {}
for k, v in pairs(Presets["Pumpkin"]) do
	Theme[k] = v
end
local Fonts = FontSets["Gotham"]
local Appearance = { Corner = 1, Transparency = 0 }

--------------------------------------------------------------------
-- HELPERS
--------------------------------------------------------------------
local connections = {}
local allModules = {}
local themeBinds = {}

local function connect(signal, fn)
	local c = signal:Connect(fn)
	table.insert(connections, c)
	return c
end

local function make(class, props, parent)
	local inst = Instance.new(class)
	for k, v in pairs(props) do
		inst[k] = v
	end
	inst.Parent = parent
	return inst
end

-- Runs fn now and again every time the theme changes
local function bind(fn)
	table.insert(themeBinds, fn)
	fn()
end

-- Runs at most once per frame, so dragging color sliders doesn't lag
local themePending = false
local function applyTheme()
	if themePending then
		return
	end
	themePending = true
	task.defer(function()
		themePending = false
		for _, fn in ipairs(themeBinds) do
			pcall(fn)
		end
	end)
end

-- skin(inst, { BackgroundColor3 = "Panel", TextColor3 = "Text" })
local function skin(inst, map)
	bind(function()
		for prop, key in pairs(map) do
			inst[prop] = Theme[key]
		end
	end)
	return inst
end

local function font(inst, bold)
	bind(function()
		inst.Font = bold and Fonts.Bold or Fonts.Regular
	end)
end

local function corner(inst, radius, fixed)
	radius = radius or 6
	local c = make("UICorner", { CornerRadius = UDim.new(0, radius) }, inst)
	if not fixed then
		bind(function()
			c.CornerRadius = UDim.new(0, math.floor(radius * Appearance.Corner + 0.5))
		end)
	end
end

local function windowTransparency(inst)
	bind(function()
		inst.BackgroundTransparency = Appearance.Transparency
	end)
end

local function label(parent, props, bold, colorKey)
	props.BackgroundTransparency = 1
	local l = make("TextLabel", props, parent)
	skin(l, { TextColor3 = colorKey or "Text" })
	font(l, bold)
	return l
end

local function isPress(input)
	return input.UserInputType == Enum.UserInputType.MouseButton1
		or input.UserInputType == Enum.UserInputType.Touch
end

local function isMove(input)
	return input.UserInputType == Enum.UserInputType.MouseMovement
		or input.UserInputType == Enum.UserInputType.Touch
end

local function notify(module, key)
	if module.OnChange then
		local ok, err = pcall(module.OnChange, module, key)
		if not ok then
			warn("[" .. module.Name .. "] OnChange error:", err)
		end
	end
end

--------------------------------------------------------------------
-- MAIN WINDOW
--------------------------------------------------------------------
local screenGui = make("ScreenGui", {
	Name = "ModuleHub",
	ResetOnSpawn = false,
	ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
}, player:WaitForChild("PlayerGui"))

local main = make("Frame", {
	Size = UDim2.fromOffset(500, 360),
	AnchorPoint = Vector2.new(0.5, 0.5),
	Position = UDim2.new(0.5, 0, 0.5, 0),
	BorderSizePixel = 0,
}, screenGui)
skin(main, { BackgroundColor3 = "Background" })
windowTransparency(main)
corner(main, 10)

local uiScale = make("UIScale", { Scale = 1 }, main)

local titleBar = make("Frame", {
	Size = UDim2.new(1, 0, 0, 36),
	BorderSizePixel = 0,
}, main)
skin(titleBar, { BackgroundColor3 = "TitleBar" })
windowTransparency(titleBar)
corner(titleBar, 10)

-- square off the bottom corners of the title bar
local titleFix = make("Frame", {
	Size = UDim2.new(1, 0, 0, 10),
	Position = UDim2.new(0, 0, 1, -10),
	BorderSizePixel = 0,
}, titleBar)
skin(titleFix, { BackgroundColor3 = "TitleBar" })
windowTransparency(titleFix)

local titleLabel = label(titleBar, {
	Size = UDim2.new(1, -60, 1, 0),
	Position = UDim2.new(0, 14, 0, 0),
	Text = "🎃 Module Hub",
	TextSize = 16,
	TextXAlignment = Enum.TextXAlignment.Left,
}, true, "Accent")

local closeButton = make("TextButton", {
	Size = UDim2.fromOffset(28, 28),
	Position = UDim2.new(1, -34, 0, 4),
	Text = "X",
	TextColor3 = Color3.new(1, 1, 1),
	TextSize = 14,
	AutoButtonColor = true,
}, titleBar)
skin(closeButton, { BackgroundColor3 = "Off" })
font(closeButton, true)
corner(closeButton, 6)

-- Dragging
do
	local dragging, dragStart, startPos = false, nil, nil

	connect(titleBar.InputBegan, function(input)
		if isPress(input) then
			dragging = true
			dragStart = input.Position
			startPos = main.Position
		end
	end)

	connect(UserInputService.InputChanged, function(input)
		if dragging and isMove(input) then
			local delta = input.Position - dragStart
			main.Position = UDim2.new(
				startPos.X.Scale, startPos.X.Offset + delta.X,
				startPos.Y.Scale, startPos.Y.Offset + delta.Y
			)
		end
	end)

	connect(UserInputService.InputEnded, function(input)
		if isPress(input) then
			dragging = false
		end
	end)
end

-- Tab bar (left) and content area (right)
local tabBar = make("Frame", {
	Size = UDim2.new(0, 120, 1, -36),
	Position = UDim2.new(0, 0, 0, 36),
	BorderSizePixel = 0,
}, main)
skin(tabBar, { BackgroundColor3 = "Panel" })
windowTransparency(tabBar)

make("UIListLayout", {
	Padding = UDim.new(0, 4),
	SortOrder = Enum.SortOrder.LayoutOrder,
}, tabBar)
make("UIPadding", {
	PaddingTop = UDim.new(0, 8),
	PaddingLeft = UDim.new(0, 8),
	PaddingRight = UDim.new(0, 8),
}, tabBar)

local content = make("Frame", {
	Size = UDim2.new(1, -120, 1, -36),
	Position = UDim2.new(0, 120, 0, 36),
	BackgroundTransparency = 1,
}, main)

--------------------------------------------------------------------
-- WIDGETS
--------------------------------------------------------------------
local function createSlider(parent, setting, module)
	local min, max = setting.Min, setting.Max
	local step = setting.Step or 1
	local suffix = setting.Suffix or ""

	local frame = make("Frame", {
		Size = UDim2.new(1, 0, 0, 42),
		BackgroundTransparency = 1,
	}, parent)

	label(frame, {
		Size = UDim2.new(0.6, 0, 0, 18),
		Text = setting.Name,
		TextSize = 13,
		TextXAlignment = Enum.TextXAlignment.Left,
	}, false)

	local valueLabel = label(frame, {
		Size = UDim2.new(0.4, 0, 0, 18),
		Position = UDim2.new(0.6, 0, 0, 0),
		TextSize = 13,
		TextXAlignment = Enum.TextXAlignment.Right,
	}, true, "Accent")

	local track = make("Frame", {
		Size = UDim2.new(1, 0, 0, 8),
		Position = UDim2.new(0, 0, 0, 26),
		BorderSizePixel = 0,
	}, frame)
	skin(track, { BackgroundColor3 = "Item" })
	corner(track, 4, true)

	local fill = make("Frame", {
		Size = UDim2.new(0, 0, 1, 0),
		BorderSizePixel = 0,
	}, track)
	skin(fill, { BackgroundColor3 = "Accent" })
	corner(fill, 4, true)

	local knob = make("Frame", {
		Size = UDim2.fromOffset(14, 14),
		AnchorPoint = Vector2.new(0.5, 0.5),
		Position = UDim2.new(0, 0, 0.5, 0),
		BackgroundColor3 = Color3.new(1, 1, 1),
		BorderSizePixel = 0,
	}, track)
	corner(knob, 7, true)

	local function setValue(v, silent)
		v = math.clamp(v, min, max)
		v = min + math.floor((v - min) / step + 0.5) * step
		v = math.clamp(tonumber(string.format("%.4f", v)), min, max)
		module.Values[setting.Key] = v

		local alpha = (v - min) / (max - min)
		fill.Size = UDim2.new(alpha, 0, 1, 0)
		knob.Position = UDim2.new(alpha, 0, 0.5, 0)

		if step < 1 then
			valueLabel.Text = string.format("%.2f", v) .. suffix
		else
			valueLabel.Text = string.format("%d", math.floor(v + 0.5)) .. suffix
		end

		if not silent then
			notify(module, setting.Key)
		end
	end

	local function updateFromInput(input)
		local width = track.AbsoluteSize.X
		if width <= 0 then
			return
		end
		local alpha = math.clamp((input.Position.X - track.AbsolutePosition.X) / width, 0, 1)
		setValue(min + (max - min) * alpha)
	end

	-- only listens for mouse movement while you're actually dragging
	local moveConn, endConn
	local function stopDrag()
		if moveConn then moveConn:Disconnect() moveConn = nil end
		if endConn then endConn:Disconnect() endConn = nil end
	end

	connect(track.InputBegan, function(input)
		if isPress(input) then
			updateFromInput(input)
			stopDrag()
			moveConn = UserInputService.InputChanged:Connect(function(i)
				if isMove(i) then
					updateFromInput(i)
				end
			end)
			endConn = UserInputService.InputEnded:Connect(function(i)
				if isPress(i) then
					stopDrag()
				end
			end)
		end
	end)

	-- make sure a drag listener can never outlive the GUI
	connect(frame.Destroying, stopDrag)

	module.Setters[setting.Key] = setValue
	setValue(module.Values[setting.Key], true)
end

local function createToggle(parent, setting, module)
	local frame = make("Frame", {
		Size = UDim2.new(1, 0, 0, 28),
		BackgroundTransparency = 1,
	}, parent)

	label(frame, {
		Size = UDim2.new(1, -60, 1, 0),
		Text = setting.Name,
		TextSize = 13,
		TextXAlignment = Enum.TextXAlignment.Left,
	}, false)

	local btn = make("TextButton", {
		Size = UDim2.fromOffset(48, 22),
		Position = UDim2.new(1, -48, 0.5, -11),
		TextSize = 12,
		TextColor3 = Color3.new(1, 1, 1),
		AutoButtonColor = true,
	}, frame)
	font(btn, true)
	corner(btn, 6)

	local function refresh()
		local on = module.Values[setting.Key]
		btn.Text = on and "ON" or "OFF"
		btn.BackgroundColor3 = on and Theme.On or Theme.Off
	end
	bind(refresh)

	connect(btn.MouseButton1Click, function()
		module.Values[setting.Key] = not module.Values[setting.Key]
		refresh()
		notify(module, setting.Key)
	end)

	module.Setters[setting.Key] = function(v, silent)
		module.Values[setting.Key] = v
		refresh()
		if not silent then
			notify(module, setting.Key)
		end
	end
end

local function createChoice(parent, setting, module)
	local frame = make("Frame", {
		Size = UDim2.new(1, 0, 0, 28),
		BackgroundTransparency = 1,
	}, parent)

	label(frame, {
		Size = UDim2.new(0.5, 0, 1, 0),
		Text = setting.Name,
		TextSize = 13,
		TextXAlignment = Enum.TextXAlignment.Left,
	}, false)

	local btn = make("TextButton", {
		Size = UDim2.new(0.5, 0, 0, 24),
		Position = UDim2.new(0.5, 0, 0.5, -12),
		TextSize = 12,
		AutoButtonColor = true,
	}, frame)
	skin(btn, { BackgroundColor3 = "Item", TextColor3 = "Accent" })
	font(btn, true)
	corner(btn, 6)

	local function refresh()
		btn.Text = tostring(module.Values[setting.Key]) .. "  â–¸"
	end

	connect(btn.MouseButton1Click, function()
		local options = setting.Options
		local index = table.find(options, module.Values[setting.Key]) or 0
		index = index % #options + 1
		module.Values[setting.Key] = options[index]
		refresh()
		notify(module, setting.Key)
	end)

	module.Setters[setting.Key] = function(v, silent)
		module.Values[setting.Key] = v
		refresh()
		if not silent then
			notify(module, setting.Key)
		end
	end

	refresh()
end

local function createText(parent, setting, module)
	local frame = make("Frame", {
		Size = UDim2.new(1, 0, 0, 28),
		BackgroundTransparency = 1,
	}, parent)

	label(frame, {
		Size = UDim2.new(0.35, 0, 1, 0),
		Text = setting.Name,
		TextSize = 13,
		TextXAlignment = Enum.TextXAlignment.Left,
	}, false)

	local box = make("TextBox", {
		Size = UDim2.new(0.65, 0, 0, 24),
		Position = UDim2.new(0.35, 0, 0.5, -12),
		Text = module.Values[setting.Key],
		ClearTextOnFocus = false,
		TextSize = 12,
		TextXAlignment = Enum.TextXAlignment.Left,
		ClipsDescendants = true,
	}, frame)
	skin(box, { BackgroundColor3 = "Item", TextColor3 = "Text" })
	font(box, false)
	corner(box, 6)
	make("UIPadding", { PaddingLeft = UDim.new(0, 8), PaddingRight = UDim.new(0, 8) }, box)

	connect(box.FocusLost, function()
		module.Values[setting.Key] = box.Text
		notify(module, setting.Key)
	end)

	module.Setters[setting.Key] = function(v, silent)
		module.Values[setting.Key] = v
		box.Text = v
		if not silent then
			notify(module, setting.Key)
		end
	end
end

local function createButton(parent, setting, module)
	local frame = make("Frame", {
		Size = UDim2.new(1, 0, 0, 28),
		BackgroundTransparency = 1,
	}, parent)

	label(frame, {
		Size = UDim2.new(0.5, 0, 1, 0),
		Text = setting.Name,
		TextSize = 13,
		TextXAlignment = Enum.TextXAlignment.Left,
	}, false)

	local btn = make("TextButton", {
		Size = UDim2.new(0.5, 0, 0, 24),
		Position = UDim2.new(0.5, 0, 0.5, -12),
		Text = setting.Text or "Run",
		TextSize = 12,
		AutoButtonColor = true,
	}, frame)
	skin(btn, { BackgroundColor3 = "Accent" })
	btn.TextColor3 = Color3.new(0, 0, 0)
	font(btn, true)
	corner(btn, 6)

	connect(btn.MouseButton1Click, function()
		if setting.Action then
			local ok, err = pcall(setting.Action, module)
			if not ok then
				warn("[" .. module.Name .. "] button error:", err)
			end
		end
	end)
end

local function createDropdown(parent, setting, module)
	local frame = make("Frame", {
		Size = UDim2.new(1, 0, 0, 0),
		AutomaticSize = Enum.AutomaticSize.Y,
		BackgroundTransparency = 1,
	}, parent)
	make("UIListLayout", {
		Padding = UDim.new(0, 4),
		SortOrder = Enum.SortOrder.LayoutOrder,
	}, frame)

	local row = make("Frame", {
		Size = UDim2.new(1, 0, 0, 28),
		BackgroundTransparency = 1,
		LayoutOrder = 1,
	}, frame)

	label(row, {
		Size = UDim2.new(0.3, 0, 1, 0),
		Text = setting.Name,
		TextSize = 13,
		TextXAlignment = Enum.TextXAlignment.Left,
	}, false)

	local btn = make("TextButton", {
		Size = UDim2.new(0.7, 0, 0, 24),
		Position = UDim2.new(0.3, 0, 0.5, -12),
		TextSize = 12,
		AutoButtonColor = true,
	}, row)
	skin(btn, { BackgroundColor3 = "Item", TextColor3 = "Accent" })
	font(btn, true)
	corner(btn, 6)

	local list = make("ScrollingFrame", {
		Size = UDim2.new(1, 0, 0, 130),
		BorderSizePixel = 0,
		ScrollBarThickness = 4,
		CanvasSize = UDim2.new(),
		AutomaticCanvasSize = Enum.AutomaticSize.Y,
		Visible = false,
		LayoutOrder = 2,
	}, frame)
	skin(list, { BackgroundColor3 = "Item", ScrollBarImageColor3 = "Accent" })
	corner(list, 6)
	make("UIListLayout", {
		Padding = UDim.new(0, 2),
		SortOrder = Enum.SortOrder.LayoutOrder,
	}, list)
	make("UIPadding", {
		PaddingTop = UDim.new(0, 4),
		PaddingLeft = UDim.new(0, 4),
		PaddingRight = UDim.new(0, 4),
		PaddingBottom = UDim.new(0, 4),
	}, list)

	local optionButtons = {}

	local function refresh()
		btn.Text = tostring(module.Values[setting.Key]) .. (list.Visible and "  â–¾" or "  â–¸")
		for name, b in pairs(optionButtons) do
			local selected = (name == module.Values[setting.Key])
			b.BackgroundColor3 = selected and Theme.Accent or Theme.Panel
			b.TextColor3 = selected and Color3.new(0, 0, 0) or Theme.Text
		end
	end

	for i, name in ipairs(setting.Options) do
		local ob = make("TextButton", {
			Size = UDim2.new(1, -6, 0, 24),
			Text = name,
			TextSize = 12,
			LayoutOrder = i,
			AutoButtonColor = false,
			BorderSizePixel = 0,
		}, list)
		font(ob, false)
		corner(ob, 4)
		optionButtons[name] = ob

		connect(ob.MouseButton1Click, function()
			module.Values[setting.Key] = name
			list.Visible = false
			refresh()
			notify(module, setting.Key)
		end)
	end

	connect(btn.MouseButton1Click, function()
		list.Visible = not list.Visible
		refresh()
	end)

	module.Setters[setting.Key] = function(v, silent)
		module.Values[setting.Key] = v
		refresh()
		if not silent then
			notify(module, setting.Key)
		end
	end

	bind(refresh)
end

--------------------------------------------------------------------
-- ERROR POPUPS (fade out slowly)
--------------------------------------------------------------------
local toastHolder = make("Frame", {
	Size = UDim2.new(0, 380, 0, 0),
	AutomaticSize = Enum.AutomaticSize.Y,
	AnchorPoint = Vector2.new(0.5, 0),
	Position = UDim2.new(0.5, 0, 0, 16),
	BackgroundTransparency = 1,
}, screenGui)
make("UIListLayout", {
	Padding = UDim.new(0, 6),
	SortOrder = Enum.SortOrder.LayoutOrder,
	HorizontalAlignment = Enum.HorizontalAlignment.Center,
}, toastHolder)

local lastToast, lastToastTime = "", 0

-- notifyError("simple explanation", "technical details (optional)")
local function notifyError(message, detail)
	-- don't spam the same message over and over
	if message == lastToast and os.clock() - lastToastTime < 4 then
		return
	end
	lastToast, lastToastTime = message, os.clock()

	-- keep at most 4 popups on screen
	local existing = {}
	for _, child in ipairs(toastHolder:GetChildren()) do
		if child:IsA("Frame") then
			table.insert(existing, child)
		end
	end
	if #existing >= 4 then
		existing[1]:Destroy()
	end

	local box = make("Frame", {
		Size = UDim2.new(1, 0, 0, 0),
		AutomaticSize = Enum.AutomaticSize.Y,
		BackgroundColor3 = Color3.fromRGB(40, 16, 16),
		BackgroundTransparency = 0.1,
		BorderSizePixel = 0,
	}, toastHolder)
	corner(box, 8, true)
	make("UIPadding", {
		PaddingTop = UDim.new(0, 8), PaddingBottom = UDim.new(0, 8),
		PaddingLeft = UDim.new(0, 12), PaddingRight = UDim.new(0, 12),
	}, box)
	make("UIListLayout", {
		Padding = UDim.new(0, 3),
		SortOrder = Enum.SortOrder.LayoutOrder,
	}, box)
	local stroke = make("UIStroke", {
		Color = Color3.fromRGB(255, 90, 80),
		Thickness = 1.5,
	}, box)

	local labels = {}
	table.insert(labels, make("TextLabel", {
		Size = UDim2.new(1, 0, 0, 0),
		AutomaticSize = Enum.AutomaticSize.Y,
		BackgroundTransparency = 1,
		Text = "âš  " .. tostring(message),
		TextWrapped = true,
		TextColor3 = Color3.fromRGB(255, 130, 120),
		Font = Enum.Font.GothamBold,
		TextSize = 14,
		TextXAlignment = Enum.TextXAlignment.Left,
		LayoutOrder = 1,
	}, box))

	if detail then
		table.insert(labels, make("TextLabel", {
			Size = UDim2.new(1, 0, 0, 0),
			AutomaticSize = Enum.AutomaticSize.Y,
			BackgroundTransparency = 1,
			Text = "Details: " .. tostring(detail),
			TextWrapped = true,
			TextColor3 = Color3.fromRGB(200, 170, 170),
			Font = Enum.Font.Gotham,
			TextSize = 11,
			TextXAlignment = Enum.TextXAlignment.Left,
			LayoutOrder = 2,
		}, box))
	end

	-- hold for a moment, then fade away slowly
	task.spawn(function()
		task.wait(3)
		if not box.Parent then
			return
		end
		local info = TweenInfo.new(2.5, Enum.EasingStyle.Quad, Enum.EasingDirection.In)
		TweenService:Create(box, info, { BackgroundTransparency = 1 }):Play()
		TweenService:Create(stroke, info, { Transparency = 1 }):Play()
		for _, l in ipairs(labels) do
			TweenService:Create(l, info, { TextTransparency = 1 }):Play()
		end
		task.wait(2.6)
		if box.Parent then
			box:Destroy()
		end
	end)
end

--------------------------------------------------------------------
-- TABS + MODULES
--------------------------------------------------------------------
local tabs = {}
local selectedTab = nil

local function selectTab(tab)
	selectedTab = tab
	for _, t in ipairs(tabs) do
		local active = (t == tab)
		t.Page.Visible = active
		t.Button.BackgroundColor3 = active and Theme.Accent or Theme.Item
		t.Button.TextColor3 = active and Color3.new(0, 0, 0) or Theme.Text
	end
end

-- keep the tab buttons in sync with theme changes
bind(function()
	if selectedTab then
		selectTab(selectedTab)
	end
end)

local function addTab(name)
	local tab = { Name = name }

	tab.Button = make("TextButton", {
		Size = UDim2.new(1, 0, 0, 30),
		Text = name,
		TextSize = 13,
		LayoutOrder = #tabs + 1,
		AutoButtonColor = false,
	}, tabBar)
	font(tab.Button, true)
	corner(tab.Button, 6)

	tab.Page = make("ScrollingFrame", {
		Size = UDim2.new(1, 0, 1, 0),
		BackgroundTransparency = 1,
		BorderSizePixel = 0,
		ScrollBarThickness = 4,
		CanvasSize = UDim2.new(),
		AutomaticCanvasSize = Enum.AutomaticSize.Y,
		Visible = false,
	}, content)
	skin(tab.Page, { ScrollBarImageColor3 = "Accent" })

	make("UIListLayout", {
		Padding = UDim.new(0, 8),
		SortOrder = Enum.SortOrder.LayoutOrder,
	}, tab.Page)
	make("UIPadding", {
		PaddingTop = UDim.new(0, 8),
		PaddingLeft = UDim.new(0, 8),
		PaddingRight = UDim.new(0, 12),
		PaddingBottom = UDim.new(0, 8),
	}, tab.Page)

	connect(tab.Button.MouseButton1Click, function()
		selectTab(tab)
	end)

	table.insert(tabs, tab)
	if not selectedTab then
		selectTab(tab)
	else
		selectTab(selectedTab)
	end
	return tab
end

--[[
	addModule(tab, {
		Name = "My Module",
		Description = "What it does",
		NoToggle = false,      -- true = no ON/OFF button (used for settings-only modules)
		Open = false,          -- true = settings panel starts expanded
		Settings = {
			{ Key = "Speed", Name = "Speed", Type = "Slider", Min = 1, Max = 100, Default = 50, Step = 1, Suffix = " studs/s" },
			{ Key = "Flag",  Name = "Option", Type = "Toggle", Default = true },
			{ Key = "Mode",  Name = "Mode", Type = "Choice", Options = { "A", "B" }, Default = "A" },
			{ Key = "Pick",  Name = "Pick", Type = "Dropdown", Options = { "A", "B", "C" }, Default = "A" },
			{ Key = "Label", Name = "Label", Type = "Text", Default = "hello" },
			{ Name = "Do thing", Type = "Button", Text = "Run", Action = function(module) end },
		},
		OnEnable  = function(module) end,
		OnDisable = function(module) end,
		OnChange  = function(module, key) end,   -- fires when any setting changes
	})
]]
local function addModule(tab, cfg)
	local module = {
		Name = cfg.Name,
		Enabled = false,
		Values = {},
		Setters = {},
		Config = cfg,
		OnChange = cfg.OnChange,
	}
	for _, s in ipairs(cfg.Settings or {}) do
		if s.Key then
			module.Values[s.Key] = s.Default
		end
	end
	table.insert(allModules, module)

	local container = make("Frame", {
		Size = UDim2.new(1, 0, 0, 0),
		AutomaticSize = Enum.AutomaticSize.Y,
		BorderSizePixel = 0,
		LayoutOrder = #allModules,
	}, tab.Page)
	skin(container, { BackgroundColor3 = "Panel" })
	corner(container, 8)
	make("UIListLayout", {
		SortOrder = Enum.SortOrder.LayoutOrder,
	}, container)

	-- Header row
	local header = make("Frame", {
		Size = UDim2.new(1, 0, 0, 48),
		BackgroundTransparency = 1,
		LayoutOrder = 1,
	}, container)

	label(header, {
		Size = UDim2.new(1, -130, 0, 22),
		Position = UDim2.new(0, 12, 0, 6),
		Text = cfg.Name,
		TextSize = 14,
		TextXAlignment = Enum.TextXAlignment.Left,
	}, true)

	label(header, {
		Size = UDim2.new(1, -130, 0, 16),
		Position = UDim2.new(0, 12, 0, 26),
		Text = cfg.Description or "",
		TextSize = 11,
		TextXAlignment = Enum.TextXAlignment.Left,
		TextTruncate = Enum.TextTruncate.AtEnd,
	}, false, "SubText")

	local toggleButton = make("TextButton", {
		Size = UDim2.fromOffset(56, 28),
		Position = UDim2.new(1, -104, 0.5, -14),
		TextSize = 13,
		TextColor3 = Color3.new(1, 1, 1),
		AutoButtonColor = true,
		Visible = not cfg.NoToggle,
	}, header)
	font(toggleButton, true)
	corner(toggleButton, 6)

	local function refreshToggle()
		toggleButton.Text = module.Enabled and "ON" or "OFF"
		toggleButton.BackgroundColor3 = module.Enabled and Theme.On or Theme.Off
	end
	bind(refreshToggle)

	function module.SetEnabled(state)
		if cfg.NoToggle or module.Enabled == state then
			return
		end
		module.Enabled = state
		refreshToggle()

		local callback = state and cfg.OnEnable or cfg.OnDisable
		if callback then
			local ok, err = pcall(callback, module)
			if not ok then
				warn("[" .. cfg.Name .. "] error:", err)
			end
		end
	end

	connect(toggleButton.MouseButton1Click, function()
		module.SetEnabled(not module.Enabled)
	end)

	-- Settings panel + gear button
	if cfg.Settings and #cfg.Settings > 0 then
		local gear = make("TextButton", {
			Size = UDim2.fromOffset(32, 28),
			Position = UDim2.new(1, -40, 0.5, -14),
			Text = "âš™",
			TextSize = 18,
			AutoButtonColor = true,
		}, header)
		font(gear, true)
		corner(gear, 6)

		local settingsFrame = make("Frame", {
			Size = UDim2.new(1, 0, 0, 0),
			AutomaticSize = Enum.AutomaticSize.Y,
			BackgroundTransparency = 1,
			Visible = cfg.Open == true,
			LayoutOrder = 2,
		}, container)
		make("UIListLayout", {
			Padding = UDim.new(0, 6),
			SortOrder = Enum.SortOrder.LayoutOrder,
		}, settingsFrame)
		make("UIPadding", {
			PaddingLeft = UDim.new(0, 12),
			PaddingRight = UDim.new(0, 12),
			PaddingBottom = UDim.new(0, 10),
			PaddingTop = UDim.new(0, 2),
		}, settingsFrame)

		for _, s in ipairs(cfg.Settings) do
			if s.Type == "Slider" then
				createSlider(settingsFrame, s, module)
			elseif s.Type == "Toggle" then
				createToggle(settingsFrame, s, module)
			elseif s.Type == "Choice" then
				createChoice(settingsFrame, s, module)
			elseif s.Type == "Dropdown" then
				createDropdown(settingsFrame, s, module)
			elseif s.Type == "Text" then
				createText(settingsFrame, s, module)
			elseif s.Type == "Button" then
				createButton(settingsFrame, s, module)
			end
		end

		local function refreshGear()
			local open = settingsFrame.Visible
			gear.BackgroundColor3 = open and Theme.Accent or Theme.Item
			gear.TextColor3 = open and Color3.new(0, 0, 0) or Theme.Text
		end
		bind(refreshGear)

		connect(gear.MouseButton1Click, function()
			settingsFrame.Visible = not settingsFrame.Visible
			refreshGear()
		end)
	end

	return module
end

-- Resets every setting in a module back to its default
local function resetModule(module)
	for _, s in ipairs(module.Config.Settings) do
		if s.Key and module.Setters[s.Key] then
			module.Setters[s.Key](s.Default, true)
		end
	end
	notify(module, nil)
end

--------------------------------------------------------------------
-- CLOSE BUTTON: stop everything, then destroy the GUI
--------------------------------------------------------------------
connect(closeButton.MouseButton1Click, function()
	for _, module in ipairs(allModules) do
		module.SetEnabled(false)
	end
	for _, c in ipairs(connections) do
		c:Disconnect()
	end
	screenGui:Destroy()
end)

-- Keybind that hides/shows the window without stopping anything
local appearanceModule, windowModule

connect(UserInputService.InputBegan, function(input, processed)
	if processed then
		return
	end
	local key = windowModule and windowModule.Values.Keybind or "RightShift"
	if input.KeyCode.Name == key then
		main.Visible = not main.Visible
	end
end)

--------------------------------------------------------------------
-- TAB: AUTO FARM
--------------------------------------------------------------------
local autoTab = addTab("Auto Farm")

--------------------------------------------------------------------
-- Auto Ascend
--------------------------------------------------------------------
do
	local GuiService = game:GetService("GuiService")
	local session = 0
	local lastPress = 0

	local Suffixes = {
		K = 1e3, THOUSAND = 1e3,
		M = 1e6, MILLION = 1e6,
		B = 1e9, BILLION = 1e9,
		T = 1e12, TRILLION = 1e12,
		QA = 1e15, QUADRILLION = 1e15,
		QI = 1e18, QUINTILLION = 1e18,
		SX = 1e21, SEXTILLION = 1e21,
		SP = 1e24, SEPTILLION = 1e24,
		OC = 1e27, OCTILLION = 1e27,
		NO = 1e30, NONILLION = 1e30,
		DC = 1e33, DECILLION = 1e33,
	}

	local function parseAmount(text)
		text = tostring(text)
		text = (text:gsub("<[^>]+>", ""))
		text = (text:gsub(",", ""))
		local num, suffix = text:match("(%d[%d%.]*)%s*(%a*)")
		num = tonumber(num)
		if not num then
			return nil
		end
		local mult = Suffixes[string.upper(suffix or "")] or 1
		return num * mult
	end

	local function listStats()
		local stats = player:FindFirstChild("leaderstats")
		local names = {}
		if stats then
			for _, c in ipairs(stats:GetChildren()) do
				table.insert(names, c.Name)
			end
		end
		return table.concat(names, ", ")
	end

	local function getStat(statName)
		local stats = player:FindFirstChild("leaderstats")
		if not stats then
			return nil, "leaderstats folder not found"
		end
		local stat
		for _, child in ipairs(stats:GetChildren()) do
			if string.lower(child.Name) == string.lower(statName) then
				stat = child
				break
			end
		end
		if not stat then
			return nil, "No leaderstat named '" .. statName .. "'. Available: " .. listStats()
		end
		local v = stat.Value
		if type(v) == "number" then
			return v
		end
		local parsed = parseAmount(v)
		if not parsed then
			return nil, "Couldn't read a number from '" .. tostring(v) .. "'"
		end
		return parsed
	end

	local function getParts()
		local pg = player:FindFirstChildOfClass("PlayerGui")
		if not pg then
			return nil, "PlayerGui not found"
		end
		local gameGui = pg:FindFirstChild("gameGui") or pg:FindFirstChild("gameGui", true)
		if not gameGui then
			return nil, "gameGui not found"
		end
		local menu = gameGui:FindFirstChild("ascendMenu", true)
		if not menu then
			return nil, "ascendMenu not found inside gameGui"
		end
		local neededLabel = menu:FindFirstChild("neededLabel", true)
		local amount = (neededLabel and neededLabel:FindFirstChild("amountLabel", true))
			or menu:FindFirstChild("amountLabel", true)
		if not amount then
			return nil, "amountLabel not found inside ascendMenu"
		end
		local confirm = menu:FindFirstChild("confirmButton", true)
		if not confirm then
			return nil, "confirmButton not found inside ascendMenu"
		end
		local autoBtn = gameGui:FindFirstChild("autoRebirthButton", true)
		return { amount = amount, confirm = confirm, autoBtn = autoBtn }
	end

	-- all text found on a button and its children
	local function dumpTexts(btn)
		local texts = {}
		if btn:IsA("TextButton") or btn:IsA("TextLabel") then
			table.insert(texts, btn.Text)
		end
		for _, d in ipairs(btn:GetDescendants()) do
			if d:IsA("TextLabel") or d:IsA("TextButton") then
				table.insert(texts, d.Text)
			end
		end
		return table.concat(texts, " / ")
	end

	-- true (ON) / false (OFF) / nil (can't tell) from the button's text
	local function readAutoState(btn)
		local joined = string.lower(dumpTexts(btn))
		if joined:find("%f[%a]on%f[%A]") then
			return true
		end
		if joined:find("%f[%a]off%f[%A]") then
			return false
		end
		return nil
	end

	-- fingerprint of how the button looks, so we can tell if a click changed it
	local function snapshot(btn)
		local parts = {}
		local function add(i)
			local s = i.Name .. ":"
			if i:IsA("GuiObject") then
				s = s .. tostring(i.Visible) .. tostring(i.BackgroundColor3) .. tostring(i.BackgroundTransparency)
			end
			if i:IsA("TextLabel") or i:IsA("TextButton") then
				s = s .. i.Text .. tostring(i.TextColor3)
			end
			if i:IsA("ImageLabel") or i:IsA("ImageButton") then
				s = s .. tostring(i.ImageColor3) .. i.Image
			end
			table.insert(parts, s)
		end
		add(btn)
		for _, d in ipairs(btn:GetDescendants()) do
			add(d)
		end
		return table.concat(parts, "|")
	end

	----------------------------------------------------------------
	-- Button pressing: tries several methods, one at a time
	----------------------------------------------------------------
	local function getGlobal(name)
		local ok, v = pcall(function()
			return getfenv()[name]
		end)
		if ok and type(v) == "function" then
			return v
		end
		local ok2, v2 = pcall(function()
			return (name == "firesignal" and firesignal) or (name == "getconnections" and getconnections) or nil
		end)
		if ok2 and type(v2) == "function" then
			return v2
		end
		return nil
	end

	-- the button itself plus any clickable buttons inside it
	local function clickTargets(btn)
		local list = { btn }
		for _, d in ipairs(btn:GetDescendants()) do
			if d:IsA("GuiButton") then
				table.insert(list, d)
			end
		end
		return list
	end

	local function methodsFor(btn)
		local list = {}
		local fire = getGlobal("firesignal")
		local conns = getGlobal("getconnections")

		for _, target in ipairs(clickTargets(btn)) do
			if target:IsA("GuiButton") then
				if fire then
					for _, n in ipairs({ "MouseButton1Click", "Activated", "MouseButton1Down" }) do
						table.insert(list, {
							name = "firesignal " .. n .. " on " .. target.Name,
							run = function()
								fire(target[n])
								return true
							end,
						})
					end
				end

				if conns then
					for _, n in ipairs({ "MouseButton1Click", "Activated" }) do
						table.insert(list, {
							name = "connections " .. n .. " on " .. target.Name,
							run = function()
								local any = false
								for _, c in ipairs(conns(target[n])) do
									if pcall(function() c:Fire() end) or pcall(function() c.Function() end) then
										any = true
									end
								end
								return any
							end,
						})
					end
				end
			end
		end

		-- real mouse click, with the Module Hub hidden so it can't block the button
		table.insert(list, {
			name = "mouse click",
			run = function()
				if btn.AbsoluteSize.X <= 0 or btn.AbsoluteSize.Y <= 0 then
					return false
				end
				local vim = game:GetService("VirtualInputManager")
				local sg = btn:FindFirstAncestorOfClass("ScreenGui")
				local inset = Vector2.zero
				if sg and not sg.IgnoreGuiInset then
					inset = GuiService:GetGuiInset()
				end
				local pos = btn.AbsolutePosition + btn.AbsoluteSize / 2 + inset

				local wasVisible = main.Visible
				main.Visible = false
				task.wait(0.05)

				local ok, err = pcall(function()
					vim:SendMouseMoveEvent(pos.X, pos.Y, game)
					task.wait(0.05)
					vim:SendMouseButtonEvent(pos.X, pos.Y, 0, true, game, 0)
					task.wait(0.08)
					vim:SendMouseButtonEvent(pos.X, pos.Y, 0, false, game, 0)
				end)

				main.Visible = wasVisible
				if not ok then
					error(err)
				end
				return true
			end,
		})

		return list
	end

	-- waits up to `seconds` for verify() to become true
	local function waitFor(verify, seconds)
		local t = 0
		while t < seconds do
			if verify() then
				return true
			end
			task.wait(0.1)
			t += 0.1
		end
		return verify()
	end

	-- verify (optional): returns true once the click had an effect.
	-- returns: true, methodName   OR   false, reason
	local function pressButton(btn, verify)
		local ranAny = false
		for _, m in ipairs(methodsFor(btn)) do
			local ok, res = pcall(m.run)
			if ok and res then
				ranAny = true
				if not verify then
					return true, m.name
				end
				-- give the game time to react before trying anything else
				if waitFor(verify, 1) then
					return true, m.name
				end
			end
		end
		if ranAny then
			return false, "clicked, but nothing on the button changed"
		end
		return false, "your script runner has no way to click buttons (no firesignal, getconnections or virtual mouse)"
	end

	----------------------------------------------------------------
	-- Remote recorder: learns what the game sends when you click Auto Rebirth
	----------------------------------------------------------------
	local recordedCall = nil
	local recording = false
	local hookInstalled = false
	local callLog = {}

	local function envFunc(name)
		local f = getGlobal(name)
		if f then
			return f
		end
		local ok, v = pcall(function()
			return getgenv()[name]
		end)
		if ok and type(v) == "function" then
			return v
		end
		return nil
	end

	local function installHook()
		if hookInstalled then
			return true
		end
		local hm = envFunc("hookmetamethod")
		local gnm = envFunc("getnamecallmethod")
		if not (hm and gnm) then
			return false
		end
		local wrap = envFunc("newcclosure") or function(f)
			return f
		end

		local old
		old = hm(game, "__namecall", wrap(function(self, ...)
			if recording then
				local method = gnm()
				if method == "FireServer" or method == "InvokeServer" then
					local okc, isRemote = pcall(function()
						return typeof(self) == "Instance" and (self:IsA("RemoteEvent") or self:IsA("RemoteFunction"))
					end)
					if okc and isRemote then
						table.insert(callLog, { remote = self, method = method, args = table.pack(...) })
					end
				end
			end
			return old(self, ...)
		end))

		hookInstalled = true
		return true
	end

	local function describeArgs(args)
		local out = {}
		for i = 1, args.n do
			table.insert(out, tostring(args[i]))
		end
		return table.concat(out, ", ")
	end

	local function fireRecorded()
		local c = recordedCall
		if not c or not c.remote.Parent then
			return false
		end
		if c.method == "FireServer" then
			c.remote:FireServer(table.unpack(c.args, 1, c.args.n))
		else
			task.spawn(function()
				c.remote:InvokeServer(table.unpack(c.args, 1, c.args.n))
			end)
		end
		return true
	end

	local function recordRemote()
		if not installHook() then
			notifyError(
				"Your script runner can't watch remotes (no hookmetamethod).",
				"Use a remote spy to find the Auto Rebirth remote and send me its name"
			)
			return
		end
		callLog = {}
		recording = true
		notifyError("Recording for 6 seconds. Click Auto Rebirth by hand now to turn it OFF.")

		task.delay(6, function()
			recording = false

			-- ignore remotes that fire constantly (they're not the button)
			local counts = {}
			for _, c in ipairs(callLog) do
				counts[c.remote] = (counts[c.remote] or 0) + 1
			end

			local pick
			for _, c in ipairs(callLog) do
				if counts[c.remote] <= 2 then
					pick = c
					break
				end
			end

			if pick then
				recordedCall = pick
				notifyError(
					"Recorded: " .. pick.remote.Name .. ". Auto Ascend will use it to turn Auto Rebirth off.",
					pick.remote:GetFullName() .. " | args: " .. describeArgs(pick.args)
				)
			else
				notifyError(
					"Didn't catch anything. Press Record again and click Auto Rebirth within 6 seconds.",
					#callLog .. " remote calls seen, all too frequent to be the button"
				)
			end
		end)
	end

	local function testRecorded()
		if not recordedCall then
			notifyError("Nothing recorded yet. Press 'Record' first.")
			return
		end
		fireRecorded()
		notifyError("Fired " .. recordedCall.remote.Name .. ". Check if Auto Rebirth changed.")
	end

	----------------------------------------------------------------
	-- Diagnostics
	----------------------------------------------------------------
	local function checkNow(module)
		local parts, missing = getParts()
		if not parts then
			notifyError("Can't find the Ascend menu parts.", missing)
			return
		end
		local needed = parseAmount(parts.amount.Text)
		local current, statErr = getStat(module.Values.StatName)

		local autoText = "button not found"
		if parts.autoBtn then
			local s = readAutoState(parts.autoBtn)
			autoText = (s == true and "ON" or s == false and "OFF" or "can't tell")
				.. " (text: '" .. dumpTexts(parts.autoBtn) .. "')"
		end

		notifyError(
			"Needed: " .. tostring(needed) .. "  |  You have: " .. tostring(current or statErr),
			"amountLabel: '" .. parts.amount.Text .. "' | leaderstats: " .. listStats() .. " | Auto Rebirth: " .. autoText
		)
	end

	-- clicks Auto Rebirth once so you can see which method works
	local function testAutoClick(module)
		local parts, missing = getParts()
		if not (parts and parts.autoBtn) then
			notifyError("Can't find the Auto Rebirth button.", missing or "autoRebirthButton not found")
			return
		end
		local before = snapshot(parts.autoBtn)
		local ok, info = pressButton(parts.autoBtn, function()
			return snapshot(parts.autoBtn) ~= before
		end)
		if ok then
			notifyError("Click worked using: " .. info .. ". Auto Rebirth just changed, so click it again to put it back.")
		else
			notifyError("Auto Rebirth didn't change: " .. info .. ".", "text: '" .. dumpTexts(parts.autoBtn) .. "'")
		end
	end

	----------------------------------------------------------------
	-- Module
	----------------------------------------------------------------
	addModule(autoTab, {
		Name = "Auto Ascend",
		Description = "Presses Ascend when your stat reaches the needed amount",
		Settings = {
			{ Key = "StatName",    Name = "Stat Name",      Type = "Text", Default = "Strength" },
			{ Key = "Interval",    Name = "Check Every",    Type = "Slider", Min = 0.25, Max = 5, Default = 1, Step = 0.25, Suffix = "s" },
			{ Key = "Cooldown",    Name = "Cooldown After", Type = "Slider", Min = 2, Max = 30, Default = 5, Step = 1, Suffix = "s" },
			{ Key = "DisableAuto", Name = "Turn Off Auto Rebirth First", Type = "Toggle", Default = true },
			{ Name = "Show what it reads", Type = "Button", Text = "Check Now", Action = checkNow },
			{ Name = "Test Auto Rebirth click", Type = "Button", Text = "Test Click", Action = testAutoClick },
			{ Name = "Learn Auto Rebirth remote", Type = "Button", Text = "Record", Action = recordRemote },
			{ Name = "Fire recorded remote", Type = "Button", Text = "Test Remote", Action = testRecorded },
		},
		OnEnable = function(module)
			session += 1
			local mySession = session

			task.spawn(function()
				while session == mySession do
					local ok, err = pcall(function()
						local values = module.Values

						if os.clock() - lastPress < values.Cooldown then
							return
						end

						local parts, missing = getParts()
						if not parts then
							notifyError("Can't find the Ascend menu parts. The game may have renamed them.", missing)
							task.wait(2)
							return
						end

						local needed = parseAmount(parts.amount.Text)
						if not needed then
							notifyError(
								"Couldn't read the needed amount from the Ascend menu.",
								"amountLabel text was '" .. parts.amount.Text .. "'"
							)
							task.wait(2)
							return
						end

						local current, statErr = getStat(values.StatName)
						if not current then
							notifyError(
								"Couldn't read your '" .. values.StatName .. "' stat. Check the Stat Name in the âš™ settings.",
								statErr
							)
							task.wait(2)
							return
						end

						if current < needed then
							return
						end

						-- 1) turn auto rebirth off
						if values.DisableAuto then
							if recordedCall then
								fireRecorded()
								task.wait(0.4)
							elseif parts.autoBtn then
								local state = readAutoState(parts.autoBtn)
								if state == true then
									local before = snapshot(parts.autoBtn)
									local pressed, info = pressButton(parts.autoBtn, function()
										return snapshot(parts.autoBtn) ~= before
									end)
									if not pressed then
										notifyError(
											"Couldn't turn Auto Rebirth off (" .. tostring(info) .. "). Press 'Record' in the âš™ so I can learn how. Pressing Ascend anyway.",
											"text: '" .. dumpTexts(parts.autoBtn) .. "'"
										)
									end
									task.wait(0.3)
								end
							end
						end

						-- 2) press Ascend
						local pressed, info = pressButton(parts.confirm)
						if pressed then
							lastPress = os.clock()
						else
							notifyError("Couldn't press Ascend: " .. tostring(info) .. ".")
						end
					end)

					if not ok then
						notifyError("Something unexpected went wrong in Auto Ascend.", err)
						task.wait(1)
					end

					task.wait(math.max(module.Values.Interval, 0.25))
				end
			end)
		end,
		OnDisable = function()
			session += 1
		end,
	})
end

--------------------------------------------------------------------
-- Candy Corn Tween
--------------------------------------------------------------------
do
	local session = 0
	local currentTween = nil
	local lastCandy = nil
	local anchoredRoot = nil

	local function getRoot()
		local character = player.Character
		return character and character:FindFirstChild("HumanoidRootPart")
	end

	local function getHumanoid()
		local character = player.Character
		return character and character:FindFirstChildOfClass("Humanoid")
	end

	local function getPosition(obj)
		if obj:IsA("BasePart") then
			return obj.Position
		elseif obj:IsA("Model") then
			return obj:GetPivot().Position
		end
		return nil
	end

	-- Looks inside sub-folders too, and doesn't hang if the folder is missing
	local function collectCandy()
		local event = workspace:FindFirstChild("HalloweenEvent")
		local folder = event and event:FindFirstChild("ActiveCandyCorns")
		if not folder then
			return nil
		end

		local list = {}
		local function scan(parent)
			for _, child in ipairs(parent:GetChildren()) do
				if child:IsA("BasePart") or child:IsA("Model") then
					table.insert(list, child)
				elseif child:IsA("Folder") then
					scan(child)
				end
			end
		end
		scan(folder)
		return list
	end

	-- returns: candy  OR  nil, simpleMessage, detailMessage
	local function getTarget(values)
		local root = getRoot()
		if not root then
			return nil, "Your character isn't loaded yet.", "HumanoidRootPart not found"
		end

		local list = collectCandy()
		if not list then
			return nil,
				"Can't find the candy corn folder. The Halloween event may be over, or the game changed it.",
				"workspace.HalloweenEvent.ActiveCandyCorns is missing"
		end
		if #list == 0 then
			return nil,
				"There are no candy corns right now. Waiting for new ones to spawn.",
				"ActiveCandyCorns is empty"
		end

		local candidates = {}
		local closestTooFar = math.huge

		for _, candy in ipairs(list) do
			if not (values.SkipLast and candy == lastCandy) then
				local pos = getPosition(candy)
				if pos then
					local distance = (root.Position - pos).Magnitude
					if distance <= values.MaxRange then
						table.insert(candidates, { candy = candy, distance = distance })
					else
						closestTooFar = math.min(closestTooFar, distance)
					end
				end
			end
		end

		if #candidates == 0 then
			if closestTooFar < math.huge then
				return nil,
					"The nearest candy corn is too far away (" .. math.floor(closestTooFar)
						.. " studs). Raise 'Max Range' in the âš™ settings.",
					"Max Range is " .. values.MaxRange .. " studs"
			end
			return nil,
				"The only candy corn left is the one you just visited. Waiting for a new one.",
				"'Skip Last Candy' is on"
		end

		if values.Mode == "Random" then
			return candidates[math.random(1, #candidates)].candy
		end

		local best = candidates[1]
		for _, entry in ipairs(candidates) do
			if values.Mode == "Closest" and entry.distance < best.distance then
				best = entry
			elseif values.Mode == "Farthest" and entry.distance > best.distance then
				best = entry
			end
		end
		return best.candy
	end

	local function releaseAnchor()
		if anchoredRoot then
			anchoredRoot.Anchored = false
			anchoredRoot = nil
		end
	end

	-- returns: true  OR  false, simpleMessage, detailMessage
	local function tweenToCandy(candy, values)
		local root = getRoot()
		local targetPos = getPosition(candy)
		if not root then
			return false, "Your character isn't loaded yet.", "HumanoidRootPart not found"
		end
		if not targetPos then
			return false, "That candy corn disappeared before you could reach it.", "Target has no position"
		end

		local finalPos = targetPos + Vector3.new(0, values.HeightOffset, 0)
		local distance = (root.Position - targetPos).Magnitude
		local duration = math.max(distance / values.TweenSpeed, 0.05)

		if values.Anchor then
			root.Anchored = true
			anchoredRoot = root
		end

		-- cancel gravity/momentum every frame so physics doesn't fight the tween
		local hb = RunService.Heartbeat:Connect(function()
			if root.Parent then
				root.AssemblyLinearVelocity = Vector3.zero
				root.AssemblyAngularVelocity = Vector3.zero
			end
		end)

		local state
		local ok, err = pcall(function()
			local tween = TweenService:Create(root, TweenInfo.new(duration, Enum.EasingStyle.Linear), {
				CFrame = CFrame.new(finalPos),
			})
			currentTween = tween

			-- watchdog: if the tween never finishes (respawn, etc.), cancel it
			task.delay(duration + 3, function()
				if tween.PlaybackState == Enum.PlaybackState.Playing then
					tween:Cancel()
				end
			end)

			tween:Play()
			state = tween.Completed:Wait()
		end)

		currentTween = nil
		hb:Disconnect()
		releaseAnchor()

		if not ok then
			return false, "The tween failed to run.", tostring(err)
		end

		-- finished normally, but we're nowhere near the target = the game moved us
		if state == Enum.PlaybackState.Completed and root.Parent then
			local off = (root.Position - finalPos).Magnitude
			if off > 20 then
				return false,
					"The game pushed you back, so it may be blocking fast movement. Try lowering 'Tween Speed'.",
					"Ended " .. math.floor(off) .. " studs away from the target"
			end
		end

		return true
	end

	addModule(autoTab, {
		Name = "Candy Corn Tween",
		Description = "Tweens you to candy corns one after another",
		Settings = {
			{ Key = "TweenSpeed",   Name = "Tween Speed",   Type = "Slider", Min = 10, Max = 300, Default = 80, Step = 1, Suffix = " studs/s" },
			{ Key = "HeightOffset", Name = "Height Offset", Type = "Slider", Min = 0, Max = 10, Default = 3, Step = 0.5, Suffix = " studs" },
			{ Key = "Pause",        Name = "Pause Between", Type = "Slider", Min = 0, Max = 3, Default = 0.25, Step = 0.05, Suffix = "s" },
			{ Key = "MaxRange",     Name = "Max Range",     Type = "Slider", Min = 50, Max = 5000, Default = 2000, Step = 50, Suffix = " studs" },
			{ Key = "Mode",         Name = "Target Mode",   Type = "Choice", Options = { "Closest", "Random", "Farthest" }, Default = "Closest" },
			{ Key = "SkipLast",     Name = "Skip Last Candy", Type = "Toggle", Default = true },
			{ Key = "Anchor",       Name = "Anchor While Tweening", Type = "Toggle", Default = false },
		},
		OnEnable = function(module)
			session += 1
			local mySession = session

			task.spawn(function()
				while session == mySession do
					local ok, err = pcall(function()
						local hum = getHumanoid()
						if hum and hum.Health <= 0 then
							notifyError("You're dead right now. Waiting for you to respawn.")
							task.wait(1)
							return
						end

						local candy, simple, detail = getTarget(module.Values)
						if not candy then
							notifyError(simple, detail)
							task.wait(1)
							return
						end

						lastCandy = candy
						local success, s, d = tweenToCandy(candy, module.Values)
						if not success then
							notifyError(s, d)
							task.wait(1)
						end
					end)

					if not ok then
						notifyError("Something unexpected went wrong in Candy Corn Tween.", err)
						task.wait(1)
					end

					task.wait(math.max(module.Values.Pause, 0.1))
				end
			end)
		end,
		OnDisable = function()
			session += 1
			if currentTween then
				currentTween:Cancel()
				currentTween = nil
			end
			releaseAnchor()
			lastCandy = nil
		end,
	})
end

--------------------------------------------------------------------
-- Auto Lift
--------------------------------------------------------------------
do
	local session = 0

	local function getHumanoid()
		local character = player.Character
		return character and character:FindFirstChildOfClass("Humanoid")
	end

	-- looks in the character first (already equipped), then the backpack
	local function findTool(name)
		local character = player.Character
		local tool = character and character:FindFirstChild(name)
		if tool and tool:IsA("Tool") then
			return tool
		end
		local backpack = player:FindFirstChildOfClass("Backpack")
		tool = backpack and backpack:FindFirstChild(name)
		if tool and tool:IsA("Tool") then
			return tool
		end
		return nil
	end

	addModule(autoTab, {
		Name = "Auto Lift",
		Description = "Equips a workout tool and auto-clicks it",
		Settings = {
			{ Key = "Exercise", Name = "Exercise", Type = "Dropdown", Options = { "Weight", "Pushups", "Situps", "Handstands" }, Default = "Weight" },
			{ Key = "CPS",      Name = "Click Speed", Type = "Slider", Min = 1, Max = 10, Default = 3, Step = 1, Suffix = " clicks/s" },
			{ Key = "Unequip",  Name = "Unequip When Off", Type = "Toggle", Default = true },
		},
		OnEnable = function(module)
			session += 1
			local mySession = session

			task.spawn(function()
				while session == mySession do
					local ok, err = pcall(function()
						local values = module.Values
						local hum = getHumanoid()

						if not hum then
							notifyError("Your character isn't loaded yet.", "Humanoid not found")
							task.wait(1)
							return
						end
						if hum.Health <= 0 then
							notifyError("You're dead right now. Waiting for you to respawn.")
							task.wait(1)
							return
						end

						local tool = findTool(values.Exercise)
						if not tool then
							notifyError(
								"Couldn't find '" .. values.Exercise .. "' in your backpack. You may not have it unlocked yet.",
								"No Tool named '" .. values.Exercise .. "' in Backpack or Character"
							)
							task.wait(1)
							return
						end

						-- equip it if it isn't already in your hands
						if tool.Parent ~= player.Character then
							hum:EquipTool(tool)
							task.wait(0.15)
							if tool.Parent ~= player.Character then
								notifyError(
									"The game wouldn't let you equip '" .. values.Exercise .. "' right now.",
									"EquipTool didn't move the tool into your character"
								)
								task.wait(1)
								return
							end
						end

						tool:Activate()
						task.wait(math.max(1 / values.CPS, 0.07))
					end)

					if not ok then
						notifyError("Something unexpected went wrong in Auto Lift.", err)
						task.wait(1)
					end
				end
			end)
		end,
		OnDisable = function(module)
			session += 1
			if module.Values.Unequip then
				local hum = getHumanoid()
				if hum then
					hum:UnequipTools()
				end
			end
		end,
	})
end

--------------------------------------------------------------------
-- TAB: PLAYER
--------------------------------------------------------------------
local playerTab = addTab("Player")

do
	local session = 0

	addModule(playerTab, {
		Name = "Walk Speed",
		Description = "Overrides your walk speed",
		Settings = {
			{ Key = "Speed", Name = "Walk Speed", Type = "Slider", Min = 16, Max = 200, Default = 50, Step = 1 },
		},
		OnEnable = function(module)
			session += 1
			local mySession = session

			task.spawn(function()
				while session == mySession do
					local character = player.Character
					local humanoid = character and character:FindFirstChildOfClass("Humanoid")
					if humanoid then
						humanoid.WalkSpeed = module.Values.Speed
					end
					task.wait(0.1)
				end
			end)
		end,
		OnDisable = function()
			session += 1
			local character = player.Character
			local humanoid = character and character:FindFirstChildOfClass("Humanoid")
			if humanoid then
				humanoid.WalkSpeed = 16
			end
		end,
	})
end

--------------------------------------------------------------------
-- TAB: TELEPORTS
--------------------------------------------------------------------
local teleportTab = addTab("Teleports")

do
	local GymNames = {
		"Lobby_Hub",
		"ZombieGym",
		"WitchGym",
		"AntlerGym",
		"aura gym",
		"beachToAuraGym",
		"candy gym",
		"electric gym",
		"ice gym",
		"jungle gym",
		"eternal gym",
		"legends gym",
		"mythical gym",
		"transcendent gym",
		"space gym",
	}

	local function getRoot()
		local character = player.Character
		return character and character:FindFirstChild("HumanoidRootPart")
	end

	-- Works for Models, Parts, and Folders (falls back to the first part inside)
	local function getCFrame(obj)
		if obj:IsA("Model") then
			return obj:GetPivot()
		elseif obj:IsA("BasePart") then
			return obj.CFrame
		end
		local part = obj:FindFirstChildWhichIsA("BasePart", true)
		return part and part.CFrame or nil
	end

	local function teleportTo(name, offset)
		local root = getRoot()
		if not root then
			notifyError("Your character isn't loaded yet.", "HumanoidRootPart not found")
			return
		end

		local target = workspace:FindFirstChild(name, true)
		if not target then
			notifyError(
				"Couldn't find '" .. name .. "'. It may be too far away to load, or named differently.",
				"'" .. name .. "' not found in workspace"
			)
			return
		end

		local cf = getCFrame(target)
		if not cf then
			notifyError("'" .. name .. "' has nothing to teleport to.", "No parts inside")
			return
		end

		root.AssemblyLinearVelocity = Vector3.zero
		root.CFrame = CFrame.new(cf.Position + Vector3.new(0, offset, 0))
	end

	addModule(teleportTab, {
		Name = "Gym Teleports",
		Description = "Pick a location, then press Teleport",
		NoToggle = true,
		Open = true,
		Settings = {
			{ Key = "Gym",    Name = "Location",      Type = "Dropdown", Options = GymNames, Default = "Lobby_Hub" },
			{ Key = "Height", Name = "Height Offset", Type = "Slider", Min = 0, Max = 50, Default = 5, Step = 1, Suffix = " studs" },
			{
				Name = "Go",
				Type = "Button",
				Text = "Teleport to Gym",
				Action = function(module)
					teleportTo(module.Values.Gym, module.Values.Height)
				end,
			},
		},
	})
end

--------------------------------------------------------------------
-- TAB: SETTINGS  (appearance + window)
--------------------------------------------------------------------
local settingsTab = addTab("Settings")

local function refreshUI()
	if not (appearanceModule and windowModule) then
		return
	end
	local a, w = appearanceModule.Values, windowModule.Values

	-- colors
	local preset = Presets[a.Preset] or Presets["Pumpkin"]
	for k, v in pairs(preset) do
		Theme[k] = v
	end
	if a.CustomAccent then
		Theme.Accent = Color3.fromRGB(a.AccentR, a.AccentG, a.AccentB)
	end

	-- shape + fonts
	Appearance.Corner = a.Corner
	Appearance.Transparency = w.Transparency
	Fonts = FontSets[a.Font] or FontSets["Gotham"]

	applyTheme()

	-- window options
	uiScale.Scale = w.Scale
	titleLabel.Text = w.Title
	if w.SpookyTitle then
		titleLabel.Font = Enum.Font.Creepster
		titleLabel.TextSize = 22
	else
		titleLabel.TextSize = 16
	end
end

appearanceModule = addModule(settingsTab, {
	Name = "Appearance",
	Description = "Theme, accent color, corners and font",
	NoToggle = true,
	Open = true,
	Settings = {
		{ Key = "Preset",       Name = "Theme",          Type = "Choice", Options = PresetNames, Default = "Pumpkin" },
		{ Key = "CustomAccent", Name = "Custom Accent",  Type = "Toggle", Default = false },
		{ Key = "AccentR",      Name = "Accent Red",     Type = "Slider", Min = 0, Max = 255, Default = 255, Step = 1 },
		{ Key = "AccentG",      Name = "Accent Green",   Type = "Slider", Min = 0, Max = 255, Default = 140, Step = 1 },
		{ Key = "AccentB",      Name = "Accent Blue",    Type = "Slider", Min = 0, Max = 255, Default = 0, Step = 1 },
		{ Key = "Corner",       Name = "Roundness",      Type = "Slider", Min = 0, Max = 2, Default = 1, Step = 0.1, Suffix = "x" },
		{ Key = "Font",         Name = "Font",           Type = "Choice", Options = FontNames, Default = "Gotham" },
		{ Name = "Reset Appearance", Type = "Button", Text = "Reset", Action = resetModule },
	},
	OnChange = refreshUI,
})

windowModule = addModule(settingsTab, {
	Name = "Window",
	Description = "Title, size, transparency and hotkey",
	NoToggle = true,
	Settings = {
		{ Key = "Title",        Name = "Title",          Type = "Text", Default = "🎃 Larping Legends Script - Moon/InfStrengthXD" },
		{ Key = "SpookyTitle",  Name = "Spooky Title Font", Type = "Toggle", Default = false },
		{ Key = "Scale",        Name = "UI Size",        Type = "Slider", Min = 0.6, Max = 1.4, Default = 1, Step = 0.05, Suffix = "x" },
		{ Key = "Transparency", Name = "Transparency",   Type = "Slider", Min = 0, Max = 0.6, Default = 0, Step = 0.05 },
		{ Key = "Keybind",      Name = "Hide/Show Key",  Type = "Choice", Options = { "RightShift", "RightControl", "Insert", "Delete", "K", "M" }, Default = "RightShift" },
		{ Name = "Reset Window", Type = "Button", Text = "Reset", Action = resetModule },
	},
	OnChange = refreshUI,
})

refreshUI()
Executor compatibility changes over time. Check current reports before testing.

Copy the code above into a compatible Roblox script executor if you choose to test it.

Larping Legends 2 Script Status

✓AvailabilityFree
$Price$0
◫CompatibilityDesktop & Mobile
★SafetyVerified

Roblox and game updates can affect compatibility, so the script’s status should be treated as temporary rather than guaranteed.

Frequently Asked Questions

What is a Larping Legends 2 script?

A Larping Legends 2 script is third-party Lua code intended to run alongside the Roblox game through a compatible script executor.

What features does this Larping Legends 2 script include?

Different scripts contain different code. This version includes: Auto Ascend | Not Working, Candy Corn Tween | Working, Auto Lift | Working (Laggy), Walk Speed | Working, Gym Teleports | Working, Settings | Working.

How do you use the Larping Legends 2 script?

Pick a compatible executor such as Hydrogen, MacSploit, AWP.GG, Delta, Fluxus, Codex, launch Roblox and join the game, copy the complete Lua code from the script card above, paste it into the executor’s editor, and click Run or Execute. Compatibility changes over time, so a name here doesn’t guarantee current support.

Can a Larping Legends 2 script get your Roblox account banned?

Yes. Third-party scripts can violate Roblox rules and may put an account at risk. Only use code from a source you trust, never enter your Roblox password into a script or executor, and test on an alternate account first.

Does the Larping Legends 2 script work on mobile?

Check whether both the executor and the script support your device and the current Roblox version. Mobile executor support does not guarantee that every script will work.

Check out our Roblox Scripts hub and Roblox Games Directory for more.

Leave a Comment