Version / Update: v1.0.0
- Download / Script Link
- --!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[ View More ]
Grace auto farm script works on zen, mayhem and grace. i skidded this from the ms paint addon and it's also intirely open source use the ms paint one for a working patch.