--!nocheck assert(game.PlaceId == 110333320616502, "This script is only for \"Grace\"") local Library = loadstring(game:HttpGet("https://raw.githubusercontent.com/deividcomsono/Obsidian/refs/heads/main/Library.lua"))() --// Main Logic // local Speaker = game:GetService("Players").LocalPlayer local RunService = game:GetService("RunService") local Rooms = workspace:WaitForChild("Rooms") local ReplicatedStorage = game:GetService("ReplicatedStorage") local Settings = { CanStart = false, Running = false, NoEffects = false, SkipSafeRooms = false, ExitSafeRoom = false, DebugLogs = false, ProcessedRooms = {}, RemovedRemotes = {}, StuckDetection = 1, LatestSuccess = os.clock(), CurrentRoomNumber = 0, AutoPlay = nil } local function DPrint(Text: string) if Settings.DebugLogs then print(`[Grace Autoplay] {Text}`) end end local function DWarn(Text: string) if Settings.DebugLogs then warn(`[Grace Autoplay] {Text}`) end end local function ResetProcessState() Settings.ProcessedRooms = {} Settings.CurrentRoomNumber = 0 Settings.LatestSuccess = os.clock() DPrint("Processing state reset") end local function ProcessRoom(Room, RoomNumber) local Character = Speaker.Character if not Character then DWarn("Cannot process room: Character does not exist") return false end if RoomNumber > Settings.CurrentRoomNumber then Settings.CurrentRoomNumber = RoomNumber DPrint(`Updated current room number to {RoomNumber}`) end if Settings.ProcessedRooms[RoomNumber] then DPrint(`Skipped processed room {RoomNumber}`) return false end DPrint(`Start processing room {RoomNumber}`) local Entrance = Room:WaitForChild("Entrance") if not Entrance then DWarn(`Room {RoomNumber} has no entrance point`) return false end Character:PivotTo(Entrance.CFrame) DPrint(`Moved character to room {RoomNumber} entrance`) local Vault = Room:FindFirstChild("VaultEntrance") local Prompt = Vault and Vault.Hinged.Cylinder:FindFirstChild("ProximityPrompt") if not Settings.SkipSafeRooms and Prompt then DPrint(`Safe room detected, triggering prompt`) local Trigger = ReplicatedStorage:FindFirstChild("TriggerPrompt") Trigger:FireServer(Prompt) Character:PivotTo(Vault:GetPivot()) Settings.ExitSafeRoom = true Settings.ProcessedRooms[RoomNumber] = true DPrint(`Successfully entered safe room, waiting to exit`) return true elseif Settings.ExitSafeRoom then DPrint("Exiting safe room") Settings.ExitSafeRoom = false local SafeRoom = Room:FindFirstChild("SafeRoom") local Hitbox = SafeRoom and SafeRoom:FindFirstChild("hitbox") if Hitbox then DPrint("Found safe room exit, expanding hitbox") Character:PivotTo(Hitbox.CFrame) Hitbox.Size = Vector3.new(2048, 2048, 2048) Hitbox.CFrame = Character:GetPivot() else DWarn("Safe room exit hitbox not found") end return true end local Plug = Room:FindFirstChild("Plug") local Touch = Plug and Plug:FindFirstChild("plugTouch") if Touch then DPrint("Found plug, expanding trigger area") Touch.Size = Vector3.new(2048, 2048, 2048) Touch.CFrame = Character:GetPivot() end local Beacon = Room:FindFirstChild("Beacon") local Hitbox = Beacon and Beacon:FindFirstChild("Hitbox") if Hitbox then DPrint("Found beacon, expanding hitbox") Hitbox.Size = Vector3.new(2048, 2048, 2048) Hitbox.CFrame = Character:GetPivot() end Settings.ProcessedRooms[RoomNumber] = true DPrint(`Room {RoomNumber} processing complete`) return true end local function GetRoomsByNumber() local RoomsByNumber = {} local RoomNumbers = {} for _, Room in Rooms:GetChildren() do local RoomNumber = tonumber(Room.Name) if RoomNumber then RoomsByNumber[RoomNumber] = Room table.insert(RoomNumbers, RoomNumber) end end table.sort(RoomNumbers) return RoomsByNumber, RoomNumbers end local function CheckResetNeeded() local _, RoomNumbers = GetRoomsByNumber() if #RoomNumbers == 0 then return false end local LastRoomNumber = math.max(unpack(RoomNumbers)) if LastRoomNumber < Settings.CurrentRoomNumber - 5 then DPrint("Detected a significant drop in room number, resetting processing state") ResetProcessState() return true end local AllProcessed = true for _, RoomNumber in RoomNumbers do if not Settings.ProcessedRooms[RoomNumber] then AllProcessed = false break end end if AllProcessed and #RoomNumbers > 0 then local TimeSinceSuccess = os.clock() - Settings.LatestSuccess if TimeSinceSuccess > Settings.StuckDetection * 2 then DPrint("All rooms processed but character is stuck, resetting processing state") ResetProcessState() return true end end return false end local function ProcessLatestRooms() if not Settings.Running or not Settings.CanStart then return end local Character = Speaker.Character if not Character then return end local RoomsByNumber, RoomNumbers = GetRoomsByNumber() if #RoomNumbers == 0 then return end local LastRoomNumber = math.max(unpack(RoomNumbers)) if Library.Labels.LastRoom then Library.Labels.LastRoom:SetText(`Last Room: {LastRoomNumber}`) end CheckResetNeeded() if Settings.ExitSafeRoom then for RoomNumber, Room in RoomsByNumber do if ProcessRoom(Room, RoomNumber) then Settings.LatestSuccess = os.clock() break end end return end local Processed = false for i = #RoomNumbers, 1, -1 do local RoomNumber = RoomNumbers[i] if not Settings.ProcessedRooms[RoomNumber] then if ProcessRoom(RoomsByNumber[RoomNumber], RoomNumber) then Settings.LatestSuccess = os.clock() Processed = true break end end end if not Processed and os.clock() - Settings.LatestSuccess > Settings.StuckDetection then DWarn("Stuck detected, resetting processing state") ResetProcessState() end end Library:GiveSignal(Rooms.ChildAdded:Connect(function(Room) if not Settings.Running then DPrint("Autoplay is not enabled, ignoring new room") return end local RoomNumber = tonumber(Room.Name) if not RoomNumber then DWarn(`Ignoring invalid room name: {Room.Name}`) return end DPrint(`New room detected: {RoomNumber}, starting processing`) if ProcessRoom(Room, RoomNumber) then Settings.LatestSuccess = os.clock() end end)) Library:GiveSignal(Speaker.PlayerGui.ChildAdded:Connect(function(Child) if Child.Name == "rushui" and Settings.NoEffects then DPrint("Disabling rushui effect") end Child.Enabled = Child.Enabled and not (Child.Name == "rushui" and Settings.NoEffects) end)) --// UI Elements // local Window = Library:CreateWindow({ ShowCustomCursor = false, Title = "mspaint", Footer = "Game: Grace | Build 9.9.9.9", Icon = 95816097006870, Font = Enum.Font.Gotham }) local Tab = Window:AddTab("Main", "home") local Groupbox = Tab:AddLeftGroupbox("Grace Autoplay") Groupbox:AddLabel("LastRoom", { Text = "Last Room: 3" }) Groupbox:AddButton("TP to Last Room", function() local Character = Speaker.Character if not Character then DWarn("Cannot teleport to the last room: Character does not exist") return end if Settings.Running then DWarn("Cannot teleport to the last room: Autoplay is running") return end local RoomsByNumber, RoomNumbers = GetRoomsByNumber() if #RoomNumbers == 0 then DWarn("Cannot teleport: No rooms available") return end local LastRoomNumber = math.max(unpack(RoomNumbers)) local LastRoom = RoomsByNumber[LastRoomNumber] local function TPed(CFrame) Character:PivotTo(CFrame) DPrint(`Successfully teleported to room {LastRoomNumber + 1}`) Library.Labels.LastRoom:SetText(`Last Room: {LastRoomNumber + 1}`) end if not LastRoom then DWarn(`Cannot find room {LastRoomNumber}`) return end DPrint(`Teleporting to room {LastRoomNumber}`) local Entrance = LastRoom:FindFirstChild("Entrance") if Entrance then TPed(Entrance.CFrame) return end local SafeRoom = LastRoom:FindFirstChild("SafeRoom") if SafeRoom then local Scale = SafeRoom:FindFirstChild("Scale") if Scale then TPed(Scale:GetPivot()) return end local Hitbox = SafeRoom:FindFirstChild("hitbox") if Hitbox then TPed(Hitbox.CFrame) return end TPed(SafeRoom:GetPivot()) return end local Beacon = LastRoom:FindFirstChild("Beacon") if Beacon then TPed(Beacon:GetPivot()) return end local Plug = LastRoom:FindFirstChild("Plug") if Plug then TPed(Plug:GetPivot()) return end end) Groupbox:AddDivider() Settings.AutoPlay = Groupbox:AddToggle("Autoplay", { Text = "Autoplay", Changed = function(Enabled) if not Settings.CanStart then return end Settings.Running = Enabled DPrint(`Autoplay has been {Enabled and "enabled" or "disabled"}`) if Enabled then ResetProcessState() end end }) Groupbox:AddToggle("SkipSafeRooms", { Text = "Skip Safe Rooms", Changed = function(Enabled) Settings.SkipSafeRooms = Enabled DPrint(`Skip Safe Rooms has been {Enabled and "enabled" or "disabled"}`) end }) Groupbox:AddToggle("NoEffects",{ Text = "No Effects", Changed = function(Enabled) Settings.NoEffects = Enabled DPrint(`No Effects has been {Enabled and "enabled" or "disabled"}`) Speaker.PlayerGui.effect.Enabled = not Enabled end }) Groupbox:AddToggle("NoEntities", { Text = "No Entities (Anti-Lag)", Changed = function(Enabled) DPrint(`No Entities mode has been {Enabled and "enabled" or "disabled"}`) if Enabled then local Count = 0 for _, Remote in ReplicatedStorage:GetChildren() do if Remote.Name:sub(1, 4) ~= "Send" then continue end Settings.RemovedRemotes[Remote] = true Remote.Parent = nil Count += 1 end DPrint(`Removed {Count} entity remote events`) else local Count = 0 for Remote in Settings.RemovedRemotes do Remote.Parent = ReplicatedStorage Settings.RemovedRemotes[Remote] = nil Count += 1 end DPrint(`Restored {Count} entity remote events`) end end }) Groupbox:AddDivider() Groupbox:AddSlider("StuckDetection", { Text = "Stuck Detection", Min = 0.5, Max = 2.5, Default = 0.8, Rounding = 1, Compact = true, HideMax = true, Suffix = "s", Changed = function(Value) Settings.StuckDetection = Value DPrint(`Stuck detection time has been set to {Value}s`) end }) Groupbox:AddToggle("DebugLogs", { Text = "Debug Logs", Changed = function(Enabled) Settings.DebugLogs = Enabled print(`Debug Logs has been {Enabled and "enabled" or "disabled"}`) end }) --// End // do Library:GiveSignal(RunService.Heartbeat:Connect(function() if not Settings.Running then return end local TimeSinceSuccess = os.clock() - Settings.LatestSuccess if TimeSinceSuccess > Settings.StuckDetection * 3 then DWarn("Severe stuck detection, restarting autoplay") task.spawn(function() Settings.AutoPlay:SetValue(false) task.wait() Settings.AutoPlay:SetValue(true) end) end ProcessLatestRooms() end)) local RoomNumbers = {} for _, Room in Rooms:GetChildren() do local RoomNumber = tonumber(Room.Name) if RoomNumber then table.insert(RoomNumbers, RoomNumber) end end local MaxNumber = #RoomNumbers > 0 and math.max(unpack(RoomNumbers)) or 0 if MaxNumber < 24 then Settings.CanStart = false DWarn("First safe room not passed, autoplay cannot start") local TimeInstance = Instance.new("Part") Library:Notify({ Title = "Grace Autoplay", Description = "Please pass the first safe room, if not, you can't get exp and keys.", Time = TimeInstance, SoundId = 4590662766 }) Library:GiveSignal(Rooms.ChildAdded:Connect(function(Child) if Settings.CanStart then return end if Child.Name == "24" or Child.Name == "25" then DPrint("First safe room passed, autoplay can start") Settings.CanStart = true TimeInstance:Destroy() end end)) else Settings.CanStart = true DPrint("First safe room passed, autoplay can start") end end