Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- ╔══════════════════════════════════════════════════════════════════════╗
- ║ Save Locate (NickZ) - V5.2 ║
- ║ ║
- ║ Alterações V5.2: ║
- ║ 1. Botão ≡ substitui estrela + menu antigo ║
- ║ 2. Painel lateral de ações (principal/nomear/mover/excluir) ║
- ║ 3. Botão voltar no painel lateral ║
- ║ 4. Interface sem emojis - ícones profissionais ║
- ╚══════════════════════════════════════════════════════════════════════╝
- ]]
- -- ============================================================
- -- SEÇÃO 1 ► AGUARDAR CARREGAMENTO DO JOGO
- -- ============================================================
- if not game:IsLoaded() then
- game.Loaded:Wait()
- end
- -- ============================================================
- -- SEÇÃO 2 ► SERVIÇOS
- -- ============================================================
- local Players = game:GetService("Players")
- local HttpService = game:GetService("HttpService")
- local TweenService = game:GetService("TweenService")
- local UserInputService = game:GetService("UserInputService")
- local RunService = game:GetService("RunService")
- local CoreGui = game:GetService("CoreGui")
- -- ============================================================
- -- SEÇÃO 3 ► REFERÊNCIAS DO JOGADOR
- -- ============================================================
- local plr = Players.LocalPlayer
- local chr = plr.Character or plr.CharacterAdded:Wait()
- local root = chr:WaitForChild("HumanoidRootPart")
- -- ============================================================
- -- SEÇÃO 4 ► CONSTANTES DE CONFIGURAÇÃO
- -- ============================================================
- local CFG = {
- MAX_SAVES = 50,
- SLOTS_PER_PAGE = 10,
- MAX_PAGES = 5,
- SAVE_DIR = "SaveLocate_NickZ",
- VERSION = "5.2",
- TWEEN_FAST = TweenInfo.new(0.18, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
- TWEEN_MED = TweenInfo.new(0.28, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
- TWEEN_SLOW = TweenInfo.new(0.45, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
- }
- -- Paleta de cores centralizada
- local CLR = {
- BG_DARK = Color3.fromRGB(14, 12, 22),
- BG_MID = Color3.fromRGB(22, 19, 35),
- BG_LIGHT = Color3.fromRGB(32, 28, 50),
- BG_CARD = Color3.fromRGB(28, 25, 42),
- BG_CARD_HOV = Color3.fromRGB(38, 34, 58),
- ACCENT = Color3.fromRGB(110, 80, 255),
- ACCENT_HOV = Color3.fromRGB(135, 105, 255),
- ACCENT_DIM = Color3.fromRGB(70, 55, 160),
- SUCCESS = Color3.fromRGB(60, 200, 110),
- SUCCESS_HOV = Color3.fromRGB(80, 225, 130),
- DANGER = Color3.fromRGB(220, 70, 70),
- DANGER_HOV = Color3.fromRGB(240, 95, 95),
- WARNING = Color3.fromRGB(220, 160, 40),
- INFO = Color3.fromRGB(70, 150, 230),
- INFO_HOV = Color3.fromRGB(90, 170, 250),
- TEXT_PRI = Color3.fromRGB(240, 238, 255),
- TEXT_SEC = Color3.fromRGB(170, 165, 200),
- TEXT_DIM = Color3.fromRGB(110, 105, 140),
- TEXT_MUTED = Color3.fromRGB(75, 70, 100),
- SLOT_NUM = Color3.fromRGB(110, 80, 255),
- GOLD = Color3.fromRGB(255, 210, 60),
- STROKE = Color3.fromRGB(80, 65, 160),
- STROKE_BRIGHT= Color3.fromRGB(120, 95, 255),
- OVERLAY = Color3.fromRGB(5, 4, 10),
- WHITE = Color3.new(1, 1, 1),
- }
- -- ============================================================
- -- SEÇÃO 5 ► SISTEMA DE FILESYSTEM
- -- ============================================================
- local FS = {}
- do
- FS.canWrite = type(writefile) == "function"
- FS.canRead = type(readfile) == "function"
- FS.canFolder = type(isfolder) == "function" and type(makefolder) == "function"
- FS.canIsFile = type(isfile) == "function"
- FS.canDelete = type(delfile) == "function"
- FS.active = FS.canWrite and FS.canRead
- local GAME_ID = tostring(game.PlaceId)
- local USER_ID = tostring(plr.UserId)
- local BASE_DIR = CFG.SAVE_DIR .. "/" .. GAME_ID
- if FS.canFolder then
- if not isfolder(CFG.SAVE_DIR) then pcall(makefolder, CFG.SAVE_DIR) end
- if not isfolder(BASE_DIR) then pcall(makefolder, BASE_DIR) end
- end
- if FS.canFolder then
- FS.SAVE_FILE = BASE_DIR .. "/Saves_" .. USER_ID .. ".json"
- FS.SAVE_BAK = BASE_DIR .. "/Saves_" .. USER_ID .. ".bak"
- FS.MAIN_FILE = BASE_DIR .. "/MainPos_" .. USER_ID .. ".json"
- FS.MAIN_BAK = BASE_DIR .. "/MainPos_" .. USER_ID .. ".bak"
- else
- local prefix = string.format("%s_%s_%s", CFG.SAVE_DIR, GAME_ID, USER_ID)
- FS.SAVE_FILE = prefix .. "_Saves.json"
- FS.SAVE_BAK = prefix .. "_Saves.bak"
- FS.MAIN_FILE = prefix .. "_Main.json"
- FS.MAIN_BAK = prefix .. "_Main.bak"
- end
- FS.LEGACY_PATHS_SAVES = {
- string.format("SkyTP/Saves_%s_%s.json", game.PlaceId, plr.UserId),
- string.format("SkyTP/Saves_%s_%s.bak", game.PlaceId, plr.UserId),
- string.format("Saves_%s_%s.json", game.PlaceId, plr.UserId),
- string.format("Saves_%s_%s.bak", game.PlaceId, plr.UserId),
- }
- FS.LEGACY_PATHS_MAIN = {
- string.format("SkyTP/MainPos_%s_%s.json", game.PlaceId, plr.UserId),
- string.format("SkyTP/MainPos_%s_%s.bak", game.PlaceId, plr.UserId),
- string.format("MainPos_%s_%s.json", game.PlaceId, plr.UserId),
- string.format("MainPos_%s_%s.bak", game.PlaceId, plr.UserId),
- }
- function FS.read(path)
- if not FS.active or type(path) ~= "string" then return nil end
- local ok, content = pcall(readfile, path)
- if ok and type(content) == "string" and #content > 2 then return content end
- return nil
- end
- function FS.readFirst(paths)
- for _, p in ipairs(paths) do
- local content = FS.read(p)
- if content then return content, p end
- end
- return nil, nil
- end
- function FS.writeSafe(primary, backup, content)
- if not FS.active then return false end
- local ok1 = pcall(writefile, primary, content)
- local ok2 = pcall(writefile, backup, content)
- return ok1 or ok2
- end
- function FS.delete(path)
- if FS.active and FS.canDelete and type(path) == "string" then
- pcall(delfile, path)
- end
- end
- end
- -- ============================================================
- -- SEÇÃO 6 ► CODIFICADOR DE CFRAME
- -- ============================================================
- local Codec = {}
- do
- function Codec.encode(cf)
- if typeof(cf) ~= "CFrame" then return nil end
- local x,y,z,r00,r01,r02,r10,r11,r12,r20,r21,r22 = cf:GetComponents()
- return { x=x, y=y, z=z, m={r00,r01,r02,r10,r11,r12,r20,r21,r22} }
- end
- function Codec.decode(t)
- if type(t) ~= "table" then return nil end
- if t.R00 ~= nil then
- return CFrame.new(t.x,t.y,t.z,
- t.R00,t.R01,t.R02,t.R10,t.R11,t.R12,t.R20,t.R21,t.R22)
- end
- if t.x and t.m and #t.m == 9 then
- local m = t.m
- return CFrame.new(t.x,t.y,t.z,m[1],m[2],m[3],m[4],m[5],m[6],m[7],m[8],m[9])
- end
- return nil
- end
- function Codec.jsonEncode(data)
- local ok, result = pcall(function() return HttpService:JSONEncode(data) end)
- return ok and result or nil
- end
- function Codec.jsonDecode(str)
- if type(str) ~= "string" or #str < 2 then return nil end
- local ok, result = pcall(function() return HttpService:JSONDecode(str) end)
- return ok and result or nil
- end
- end
- -- ============================================================
- -- SEÇÃO 7 ► ESTADO GLOBAL
- -- ============================================================
- local State = {
- saves = {},
- mainPos = nil,
- currentPage = 1,
- saveSlot = 1,
- isDirty = false,
- connections = {},
- optionFrames = {},
- }
- -- ============================================================
- -- SEÇÃO 8 ► PERSISTÊNCIA
- -- ============================================================
- local Persist = {}
- do
- function Persist.buildSavePayload()
- local positions = {}
- for slot, data in pairs(State.saves) do
- if type(slot) == "number" and slot >= 1 and slot <= CFG.MAX_SAVES
- and typeof(data.position) == "CFrame" then
- positions[tostring(slot)] = {
- name = data.name or ("Slot " .. slot),
- timestamp = data.timestamp or os.date("%Y-%m-%d %H:%M:%S"),
- position = Codec.encode(data.position),
- }
- end
- end
- return {
- version = CFG.VERSION,
- gameId = game.PlaceId,
- gameName = game.Name,
- userId = plr.UserId,
- userName = plr.Name,
- savedAt = os.date("%Y-%m-%d %H:%M:%S"),
- positions = positions,
- }
- end
- function Persist.save()
- if not FS.active then return end
- local payload = Codec.jsonEncode(Persist.buildSavePayload())
- if payload then FS.writeSafe(FS.SAVE_FILE, FS.SAVE_BAK, payload) end
- if State.mainPos then
- local mainPayload = Codec.jsonEncode({
- version = CFG.VERSION,
- gameId = game.PlaceId,
- gameName = game.Name,
- name = State.mainPos.name,
- timestamp = State.mainPos.timestamp,
- position = Codec.encode(State.mainPos.position),
- })
- if mainPayload then FS.writeSafe(FS.MAIN_FILE, FS.MAIN_BAK, mainPayload) end
- end
- State.isDirty = false
- end
- function Persist.load()
- if not FS.active then return end
- local candidates = {FS.SAVE_FILE, FS.SAVE_BAK}
- for _, p in ipairs(FS.LEGACY_PATHS_SAVES) do table.insert(candidates, p) end
- local content, usedPath = FS.readFirst(candidates)
- if content then
- local data = Codec.jsonDecode(content)
- if data and type(data) == "table" then
- if not data.gameId or data.gameId == game.PlaceId then
- local positions = data.positions or data
- for k, v in pairs(positions) do
- local slot = tonumber(k)
- if slot and slot >= 1 and slot <= CFG.MAX_SAVES and type(v) == "table" then
- local cf = Codec.decode(v.position)
- if cf then
- State.saves[slot] = {
- position = cf,
- name = v.name or ("Slot " .. slot),
- timestamp = v.timestamp or os.date("%Y-%m-%d %H:%M:%S"),
- }
- end
- end
- end
- if usedPath ~= FS.SAVE_FILE then Persist.save() end
- end
- end
- end
- local mainCandidates = {FS.MAIN_FILE, FS.MAIN_BAK}
- for _, p in ipairs(FS.LEGACY_PATHS_MAIN) do table.insert(mainCandidates, p) end
- local mainContent, mainUsedPath = FS.readFirst(mainCandidates)
- if mainContent then
- local data = Codec.jsonDecode(mainContent)
- if data and type(data) == "table" then
- if not data.gameId or data.gameId == game.PlaceId then
- local cf = Codec.decode(data.position)
- if cf then
- State.mainPos = {
- position = cf,
- name = data.name or "Posicao Principal",
- timestamp = data.timestamp or os.date("%Y-%m-%d %H:%M:%S"),
- }
- if mainUsedPath ~= FS.MAIN_FILE then Persist.save() end
- end
- end
- end
- end
- end
- local saveDebounce = false
- function Persist.scheduleSave()
- State.isDirty = true
- if saveDebounce then return end
- saveDebounce = true
- task.delay(0.5, function()
- if State.isDirty then Persist.save() end
- saveDebounce = false
- end)
- end
- function Persist.deleteMain()
- FS.delete(FS.MAIN_FILE)
- FS.delete(FS.MAIN_BAK)
- end
- end
- -- ============================================================
- -- SEÇÃO 9 ► TELEPORTE ANTI-BUG
- -- ============================================================
- local Teleport = {}
- do
- function Teleport.refreshCharacter()
- chr = plr.Character
- root = chr and chr:FindFirstChild("HumanoidRootPart")
- end
- function Teleport.to(targetCFrame)
- if not targetCFrame then return false end
- if not chr or not chr.Parent then Teleport.refreshCharacter() end
- if not root then return false end
- local hum = chr:FindFirstChildWhichIsA("Humanoid")
- if hum and hum.SeatPart then hum.Sit = false; task.wait(0.05) end
- local wasAnchored = root.Anchored
- root.Anchored = true
- if root:IsA("BasePart") then
- root.AssemblyLinearVelocity = Vector3.zero
- root.AssemblyAngularVelocity = Vector3.zero
- end
- root.CFrame = targetCFrame
- RunService.Heartbeat:Wait()
- root.CFrame = targetCFrame
- root.Anchored = wasAnchored
- return true
- end
- end
- -- ============================================================
- -- SEÇÃO 10 ► UTILITÁRIOS DE UI
- -- ============================================================
- local UI = {}
- do
- function UI.corner(parent, radius)
- local c = Instance.new("UICorner")
- c.CornerRadius = UDim.new(0, radius or 8)
- c.Parent = parent
- return c
- end
- function UI.stroke(parent, color, thickness)
- local s = Instance.new("UIStroke")
- s.Color = color or CLR.STROKE
- s.Thickness = thickness or 1.5
- s.Parent = parent
- return s
- end
- function UI.gradient(parent, colorA, colorB, rotation)
- local g = Instance.new("UIGradient")
- g.Color = ColorSequence.new({
- ColorSequenceKeypoint.new(0, colorA),
- ColorSequenceKeypoint.new(1, colorB),
- })
- g.Rotation = rotation or 90
- g.Parent = parent
- return g
- end
- function UI.hoverBG(button, hoverColor, baseColor)
- button.MouseEnter:Connect(function()
- TweenService:Create(button, CFG.TWEEN_FAST, {BackgroundColor3 = hoverColor}):Play()
- end)
- button.MouseLeave:Connect(function()
- TweenService:Create(button, CFG.TWEEN_FAST, {BackgroundColor3 = baseColor}):Play()
- end)
- end
- function UI.closeAllOptionMenus(except)
- for _, frame in pairs(State.optionFrames) do
- if frame ~= except and frame and frame.Parent then
- frame.Visible = false
- end
- end
- end
- function UI.label(parent, props)
- local l = Instance.new("TextLabel")
- l.BackgroundTransparency = 1
- l.Font = props.font or Enum.Font.Gotham
- l.TextSize = props.size or 14
- l.TextColor3 = props.color or CLR.TEXT_PRI
- l.Text = props.text or ""
- l.TextXAlignment = props.align or Enum.TextXAlignment.Left
- l.TextWrapped = props.wrap or false
- l.TextTruncate = props.truncate or Enum.TextTruncate.None
- l.Size = props.sz or UDim2.new(1, 0, 1, 0)
- l.Position = props.pos or UDim2.new(0, 0, 0, 0)
- l.TextStrokeTransparency = 1
- l.Parent = parent
- return l
- end
- function UI.button(parent, props)
- local b = Instance.new("TextButton")
- b.Font = props.font or Enum.Font.GothamSemibold
- b.TextSize = props.size or 14
- b.TextColor3 = props.textColor or CLR.WHITE
- b.Text = props.text or ""
- b.BackgroundColor3 = props.bgColor or CLR.ACCENT
- b.Size = props.sz or UDim2.new(0, 80, 0, 30)
- b.Position = props.pos or UDim2.new(0, 0, 0, 0)
- b.AutoButtonColor = false
- b.BorderSizePixel = 0
- b.TextStrokeTransparency = 1
- b.Parent = parent
- UI.corner(b, props.radius or 7)
- if props.hoverColor then
- UI.hoverBG(b, props.hoverColor, props.bgColor)
- end
- return b
- end
- end
- -- ============================================================
- -- SEÇÃO 11 ► CONSTRUÇÃO DA GUI
- -- ============================================================
- do
- local old = CoreGui:FindFirstChild("SaveLocate_NickZ_V5")
- if old then old:Destroy() end
- end
- local ScreenGui = Instance.new("ScreenGui")
- ScreenGui.Name = "SaveLocate_NickZ_V5"
- ScreenGui.DisplayOrder = 999
- ScreenGui.ResetOnSpawn = false
- ScreenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
- ScreenGui.IgnoreGuiInset = true
- ScreenGui.Parent = CoreGui
- -- ────────────────────────────────────────────────
- -- 11.1 ► JANELA PRINCIPAL
- -- ────────────────────────────────────────────────
- local MainFrame = Instance.new("Frame")
- MainFrame.Name = "MainFrame"
- MainFrame.Size = UDim2.new(0, 590, 0, 470)
- MainFrame.Position = UDim2.new(0.5, -295, 0.5, -235)
- MainFrame.BackgroundColor3 = CLR.BG_DARK
- MainFrame.BorderSizePixel = 0
- MainFrame.Active = true
- MainFrame.Draggable = true
- MainFrame.ClipsDescendants = true
- MainFrame.Parent = ScreenGui
- UI.corner(MainFrame, 10)
- UI.stroke(MainFrame, CLR.STROKE_BRIGHT, 1.5)
- UI.gradient(MainFrame, CLR.BG_MID, CLR.BG_DARK, 135)
- -- ────────────────────────────────────────────────
- -- 11.2 ► BARRA DE TITULO
- -- ────────────────────────────────────────────────
- local TitleBar = Instance.new("Frame")
- TitleBar.Name = "TitleBar"
- TitleBar.Size = UDim2.new(1, 0, 0, 44)
- TitleBar.BackgroundColor3 = CLR.BG_MID
- TitleBar.BorderSizePixel = 0
- TitleBar.Parent = MainFrame
- UI.corner(TitleBar, 10)
- local TitleDivider = Instance.new("Frame")
- TitleDivider.Size = UDim2.new(1, 0, 0, 1)
- TitleDivider.Position = UDim2.new(0, 0, 1, -1)
- TitleDivider.BackgroundColor3 = CLR.STROKE
- TitleDivider.BorderSizePixel = 0
- TitleDivider.Parent = TitleBar
- -- Icone decorativo (sem emoji - caractere unicode limpo)
- UI.label(TitleBar, {
- text = "+",
- sz = UDim2.new(0, 30, 1, 0),
- pos = UDim2.new(0, 12, 0, 0),
- size = 20,
- color = CLR.ACCENT,
- font = Enum.Font.GothamBold,
- align = Enum.TextXAlignment.Center,
- })
- UI.label(TitleBar, {
- text = "Save Locate / NickZ",
- sz = UDim2.new(1, -150, 1, 0),
- pos = UDim2.new(0, 46, 0, 0),
- size = 16,
- color = CLR.TEXT_PRI,
- font = Enum.Font.GothamBold,
- align = Enum.TextXAlignment.Left,
- })
- local VersionBadge = Instance.new("Frame")
- VersionBadge.Size = UDim2.new(0, 42, 0, 20)
- VersionBadge.Position = UDim2.new(1, -122, 0.5, -10)
- VersionBadge.BackgroundColor3 = CLR.ACCENT_DIM
- VersionBadge.Parent = TitleBar
- UI.corner(VersionBadge, 5)
- UI.label(VersionBadge, {
- text = "V 5.2",
- size = 10,
- color = CLR.TEXT_PRI,
- font = Enum.Font.GothamBold,
- align = Enum.TextXAlignment.Center,
- })
- -- Botao fechar totalmente (icone de power sem emoji)
- local DestroyBtn = UI.button(TitleBar, {
- text = "O",
- sz = UDim2.new(0, 28, 0, 28),
- pos = UDim2.new(1, -68, 0.5, -14),
- bgColor = CLR.BG_LIGHT,
- hoverColor = Color3.fromRGB(160, 60, 60),
- textColor = CLR.TEXT_DIM,
- font = Enum.Font.GothamBold,
- size = 13,
- radius = 7,
- })
- -- Botao minimizar (X)
- local CloseBtn = UI.button(TitleBar, {
- text = "X",
- sz = UDim2.new(0, 28, 0, 28),
- pos = UDim2.new(1, -36, 0.5, -14),
- bgColor = CLR.BG_LIGHT,
- hoverColor = CLR.DANGER,
- textColor = CLR.TEXT_SEC,
- font = Enum.Font.GothamBold,
- size = 13,
- radius = 7,
- })
- -- ────────────────────────────────────────────────
- -- 11.3 ► CONTROLES (Salvar / Nome / Slot)
- -- ────────────────────────────────────────────────
- local CtrlFrame = Instance.new("Frame")
- CtrlFrame.Name = "Controls"
- CtrlFrame.Size = UDim2.new(1, -20, 0, 96)
- CtrlFrame.Position = UDim2.new(0, 10, 0, 50)
- CtrlFrame.BackgroundColor3 = CLR.BG_MID
- CtrlFrame.BorderSizePixel = 0
- CtrlFrame.Parent = MainFrame
- UI.corner(CtrlFrame, 8)
- UI.stroke(CtrlFrame, CLR.STROKE, 1)
- local SaveBtn = UI.button(CtrlFrame, {
- text = "+ Salvar Posicao",
- sz = UDim2.new(0, 175, 0, 36),
- pos = UDim2.new(0, 10, 0, 10),
- bgColor = CLR.SUCCESS,
- hoverColor = CLR.SUCCESS_HOV,
- textColor = CLR.WHITE,
- font = Enum.Font.GothamBold,
- size = 14,
- radius = 7,
- })
- local NameBox = Instance.new("TextBox")
- NameBox.Name = "NameBox"
- NameBox.PlaceholderText = " Nome da posicao..."
- NameBox.Size = UDim2.new(1, -200, 0, 36)
- NameBox.Position = UDim2.new(0, 10, 0, 50)
- NameBox.BackgroundColor3 = CLR.BG_DARK
- NameBox.TextColor3 = CLR.TEXT_PRI
- NameBox.PlaceholderColor3 = CLR.TEXT_MUTED
- NameBox.Font = Enum.Font.Gotham
- NameBox.TextSize = 13
- NameBox.ClearTextOnFocus = false
- NameBox.TextXAlignment = Enum.TextXAlignment.Left
- NameBox.Parent = CtrlFrame
- UI.corner(NameBox, 7)
- UI.stroke(NameBox, CLR.STROKE, 1)
- local SlotPanel = Instance.new("Frame")
- SlotPanel.Name = "SlotPanel"
- SlotPanel.Size = UDim2.new(0, 175, 0, 76)
- SlotPanel.Position = UDim2.new(1, -185, 0, 10)
- SlotPanel.BackgroundColor3 = CLR.BG_DARK
- SlotPanel.BorderSizePixel = 0
- SlotPanel.Parent = CtrlFrame
- UI.corner(SlotPanel, 7)
- UI.stroke(SlotPanel, CLR.STROKE, 1)
- UI.label(SlotPanel, {
- text = "SLOT DE DESTINO",
- sz = UDim2.new(1, 0, 0, 20),
- pos = UDim2.new(0, 0, 0, 4),
- size = 9,
- color = CLR.TEXT_DIM,
- font = Enum.Font.GothamBold,
- align = Enum.TextXAlignment.Center,
- })
- local SlotValueLbl = UI.label(SlotPanel, {
- text = "01",
- sz = UDim2.new(0, 50, 0, 30),
- pos = UDim2.new(0.5, -25, 0, 22),
- size = 22,
- color = CLR.ACCENT,
- font = Enum.Font.GothamBold,
- align = Enum.TextXAlignment.Center,
- })
- local SlotPrevBtn = UI.button(SlotPanel, {
- text = "<",
- sz = UDim2.new(0, 32, 0, 28),
- pos = UDim2.new(0, 6, 0, 24),
- bgColor = CLR.ACCENT_DIM,
- hoverColor = CLR.ACCENT,
- size = 16,
- radius = 6,
- })
- local SlotNextBtn = UI.button(SlotPanel, {
- text = ">",
- sz = UDim2.new(0, 32, 0, 28),
- pos = UDim2.new(1, -38, 0, 24),
- bgColor = CLR.ACCENT_DIM,
- hoverColor = CLR.ACCENT,
- size = 16,
- radius = 6,
- })
- local SlotStatusLbl = UI.label(SlotPanel, {
- text = "livre",
- sz = UDim2.new(1, 0, 0, 14),
- pos = UDim2.new(0, 0, 1, -16),
- size = 9,
- color = CLR.SUCCESS,
- font = Enum.Font.GothamSemibold,
- align = Enum.TextXAlignment.Center,
- })
- -- ────────────────────────────────────────────────
- -- 11.4 ► BARRA DE PAGINACAO
- -- ────────────────────────────────────────────────
- local PageBar = Instance.new("Frame")
- PageBar.Name = "PageBar"
- PageBar.Size = UDim2.new(1, -20, 0, 34)
- PageBar.Position = UDim2.new(0, 10, 0, 152)
- PageBar.BackgroundColor3 = CLR.BG_MID
- PageBar.BorderSizePixel = 0
- PageBar.Parent = MainFrame
- UI.corner(PageBar, 7)
- UI.stroke(PageBar, CLR.STROKE, 1)
- local PagePrevBtn = UI.button(PageBar, {
- text = "<<",
- sz = UDim2.new(0, 70, 0, 26),
- pos = UDim2.new(0, 4, 0.5, -13),
- bgColor = CLR.BG_LIGHT,
- hoverColor = CLR.ACCENT_DIM,
- size = 11,
- radius = 6,
- })
- local PageLabel = UI.label(PageBar, {
- text = "PAGINA 1 / 5",
- sz = UDim2.new(1, -160, 1, 0),
- pos = UDim2.new(0, 80, 0, 0),
- size = 12,
- color = CLR.TEXT_SEC,
- font = Enum.Font.GothamBold,
- align = Enum.TextXAlignment.Center,
- })
- local PageDotsFrame = Instance.new("Frame")
- PageDotsFrame.Size = UDim2.new(0, 70, 0, 10)
- PageDotsFrame.Position = UDim2.new(0.5, -35, 1, -13)
- PageDotsFrame.BackgroundTransparency = 1
- PageDotsFrame.Parent = PageBar
- local PageDotLayout = Instance.new("UIListLayout")
- PageDotLayout.FillDirection = Enum.FillDirection.Horizontal
- PageDotLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
- PageDotLayout.Padding = UDim.new(0, 6)
- PageDotLayout.Parent = PageDotsFrame
- local PageDots = {}
- for i = 1, CFG.MAX_PAGES do
- local dot = Instance.new("Frame")
- dot.Size = UDim2.new(0, 7, 0, 7)
- dot.BackgroundColor3 = (i == 1) and CLR.ACCENT or CLR.TEXT_MUTED
- dot.Parent = PageDotsFrame
- UI.corner(dot, 4)
- PageDots[i] = dot
- end
- local PageNextBtn = UI.button(PageBar, {
- text = ">>",
- sz = UDim2.new(0, 70, 0, 26),
- pos = UDim2.new(1, -74, 0.5, -13),
- bgColor = CLR.BG_LIGHT,
- hoverColor = CLR.ACCENT_DIM,
- size = 11,
- radius = 6,
- })
- -- ────────────────────────────────────────────────
- -- 11.5 ► GRID DE SLOTS
- -- ────────────────────────────────────────────────
- local SlotsFrame = Instance.new("Frame")
- SlotsFrame.Name = "Slots"
- SlotsFrame.Size = UDim2.new(1, -20, 1, -198)
- SlotsFrame.Position = UDim2.new(0, 10, 0, 194)
- SlotsFrame.BackgroundTransparency = 1
- SlotsFrame.Parent = MainFrame
- local GridLayout = Instance.new("UIGridLayout")
- GridLayout.CellPadding = UDim2.new(0, 7, 0, 7)
- GridLayout.CellSize = UDim2.new(0.5, -4, 0, 48)
- GridLayout.FillDirection = Enum.FillDirection.Horizontal
- GridLayout.SortOrder = Enum.SortOrder.LayoutOrder
- GridLayout.Parent = SlotsFrame
- -- ────────────────────────────────────────────────
- -- 11.6 ► BOTAO FLUTUANTE
- -- ────────────────────────────────────────────────
- local FloatContainer = Instance.new("Frame")
- FloatContainer.Name = "FloatBtn"
- FloatContainer.Size = UDim2.new(0, 46, 0, 46)
- FloatContainer.Position = UDim2.new(0, 16, 0.5, -23)
- FloatContainer.BackgroundTransparency = 1
- FloatContainer.Active = true
- FloatContainer.Draggable = true
- FloatContainer.Visible = false
- FloatContainer.Parent = ScreenGui
- local FloatBtn = UI.button(FloatContainer, {
- text = "SL",
- sz = UDim2.new(1, 0, 1, 0),
- bgColor = CLR.BG_MID,
- hoverColor = CLR.BG_LIGHT,
- textColor = CLR.ACCENT,
- font = Enum.Font.GothamBold,
- size = 14,
- radius = 10,
- })
- UI.stroke(FloatBtn, CLR.STROKE_BRIGHT, 1.5)
- -- ────────────────────────────────────────────────
- -- 11.7 ► BOTAO POSICAO PRINCIPAL (flutuante)
- -- ────────────────────────────────────────────────
- local MainPosContainer = Instance.new("Frame")
- MainPosContainer.Name = "MainPosBtn"
- MainPosContainer.Size = UDim2.new(0, 50, 0, 50)
- MainPosContainer.Position = UDim2.new(1, -64, 0, 14)
- MainPosContainer.BackgroundTransparency = 1
- MainPosContainer.Active = true
- MainPosContainer.Draggable = true
- MainPosContainer.Visible = false
- MainPosContainer.Parent = ScreenGui
- local MainPosBtn = UI.button(MainPosContainer, {
- text = ">>",
- sz = UDim2.new(1, 0, 1, 0),
- bgColor = CLR.BG_MID,
- hoverColor = CLR.BG_LIGHT,
- textColor = CLR.ACCENT,
- font = Enum.Font.GothamBold,
- size = 14,
- radius = 25,
- })
- local MainPosStroke = UI.stroke(MainPosBtn, CLR.ACCENT, 2)
- local MainPosTooltip = Instance.new("Frame")
- MainPosTooltip.Name = "Tooltip"
- MainPosTooltip.Size = UDim2.new(0, 160, 0, 32)
- MainPosTooltip.Position = UDim2.new(-3.4, 0, 0.5, -16)
- MainPosTooltip.BackgroundColor3 = CLR.BG_MID
- MainPosTooltip.BackgroundTransparency = 0.05
- MainPosTooltip.Visible = false
- MainPosTooltip.ZIndex = 5
- MainPosTooltip.Parent = MainPosBtn
- UI.corner(MainPosTooltip, 7)
- UI.stroke(MainPosTooltip, CLR.ACCENT, 1)
- local TooltipLabel = UI.label(MainPosTooltip, {
- text = "",
- sz = UDim2.new(1, -8, 1, 0),
- pos = UDim2.new(0, 4, 0, 0),
- size = 11,
- color = CLR.TEXT_PRI,
- font = Enum.Font.GothamSemibold,
- align = Enum.TextXAlignment.Center,
- wrap = true,
- })
- TooltipLabel.ZIndex = 6
- local MainPosRemoveBtn = UI.button(MainPosBtn, {
- text = "X",
- sz = UDim2.new(0, 18, 0, 18),
- pos = UDim2.new(1, -4, 0, -4),
- bgColor = CLR.DANGER,
- hoverColor = CLR.DANGER_HOV,
- textColor = CLR.WHITE,
- size = 10,
- radius = 9,
- })
- MainPosRemoveBtn.Visible = false
- MainPosRemoveBtn.ZIndex = 6
- -- ────────────────────────────────────────────────
- -- 11.8 ► TOAST DE NOTIFICACAO
- -- ────────────────────────────────────────────────
- local ToastFrame = Instance.new("Frame")
- ToastFrame.Name = "Toast"
- ToastFrame.Size = UDim2.new(0, 260, 0, 38)
- ToastFrame.Position = UDim2.new(0.5, -130, 0, -50)
- ToastFrame.BackgroundColor3 = CLR.BG_MID
- ToastFrame.BackgroundTransparency = 0.05
- ToastFrame.Visible = false
- ToastFrame.ZIndex = 99
- ToastFrame.Parent = ScreenGui
- UI.corner(ToastFrame, 8)
- local ToastStroke = UI.stroke(ToastFrame, CLR.SUCCESS, 1.5)
- local ToastLabel = UI.label(ToastFrame, {
- text = "",
- sz = UDim2.new(1, -16, 1, 0),
- pos = UDim2.new(0, 8, 0, 0),
- size = 13,
- color = CLR.TEXT_PRI,
- font = Enum.Font.GothamSemibold,
- align = Enum.TextXAlignment.Center,
- })
- ToastLabel.ZIndex = 100
- local toastActive = false
- local function showToast(message, toastType)
- local colorMap = {
- success = CLR.SUCCESS,
- error = CLR.DANGER,
- info = CLR.INFO,
- warning = CLR.WARNING,
- }
- local color = colorMap[toastType] or CLR.SUCCESS
- ToastStroke.Color = color
- ToastLabel.Text = message
- ToastLabel.TextColor3 = color
- ToastFrame.Visible = true
- toastActive = true
- TweenService:Create(ToastFrame, CFG.TWEEN_MED,
- {Position = UDim2.new(0.5, -130, 0, 12)}):Play()
- task.delay(2.2, function()
- if toastActive then
- TweenService:Create(ToastFrame, CFG.TWEEN_MED,
- {Position = UDim2.new(0.5, -130, 0, -50)}):Play()
- task.wait(0.35)
- ToastFrame.Visible = false
- toastActive = false
- end
- end)
- end
- -- ────────────────────────────────────────────────
- -- 11.9 ► MODAIS (Confirmação / Renomear / Mover)
- -- ────────────────────────────────────────────────
- local function createOverlay(parent, zIndex)
- local overlay = Instance.new("Frame")
- overlay.Size = UDim2.new(1, 0, 1, 0)
- overlay.BackgroundColor3 = CLR.OVERLAY
- overlay.BackgroundTransparency = 0.5
- overlay.Visible = false
- overlay.ZIndex = zIndex
- overlay.Parent = parent
- return overlay
- end
- local function createModalFrame(parent, w, h, zIndex)
- local frame = Instance.new("Frame")
- frame.Size = UDim2.new(0, w, 0, h)
- frame.Position = UDim2.new(0.5, -w/2, 0.5, -h/2)
- frame.BackgroundColor3 = CLR.BG_MID
- frame.BorderSizePixel = 0
- frame.ZIndex = zIndex
- frame.Parent = parent
- UI.corner(frame, 10)
- UI.stroke(frame, CLR.STROKE_BRIGHT, 1.5)
- return frame
- end
- -- Modal de Confirmacao
- local ConfirmOverlay = createOverlay(MainFrame, 20)
- local ConfirmFrame = createModalFrame(ConfirmOverlay, 310, 130, 21)
- local ConfirmTitleBar = Instance.new("Frame")
- ConfirmTitleBar.Size = UDim2.new(1, 0, 0, 32)
- ConfirmTitleBar.BackgroundColor3 = CLR.BG_DARK
- ConfirmTitleBar.BorderSizePixel = 0
- ConfirmTitleBar.ZIndex = 22
- ConfirmTitleBar.Parent = ConfirmFrame
- UI.corner(ConfirmTitleBar, 10)
- UI.label(ConfirmTitleBar, {
- text = "! Confirmar Acao",
- sz = UDim2.new(1, -16, 1, 0),
- pos = UDim2.new(0, 12, 0, 0),
- size = 13,
- color = CLR.TEXT_PRI,
- font = Enum.Font.GothamBold,
- align = Enum.TextXAlignment.Left,
- }).ZIndex = 23
- local ConfirmLabel = UI.label(ConfirmFrame, {
- text = "",
- sz = UDim2.new(1, -20, 0, 40),
- pos = UDim2.new(0, 10, 0, 36),
- size = 13,
- color = CLR.TEXT_SEC,
- font = Enum.Font.Gotham,
- align = Enum.TextXAlignment.Center,
- wrap = true,
- })
- ConfirmLabel.ZIndex = 22
- local ConfirmYesBtn = UI.button(ConfirmFrame, {
- text = "Confirmar",
- sz = UDim2.new(0.5, -12, 0, 32),
- pos = UDim2.new(0, 8, 1, -40),
- bgColor = CLR.DANGER,
- hoverColor = CLR.DANGER_HOV,
- size = 13,
- radius = 7,
- })
- ConfirmYesBtn.ZIndex = 22
- local ConfirmNoBtn = UI.button(ConfirmFrame, {
- text = "Cancelar",
- sz = UDim2.new(0.5, -12, 0, 32),
- pos = UDim2.new(0.5, 4, 1, -40),
- bgColor = CLR.ACCENT_DIM,
- hoverColor = CLR.ACCENT,
- size = 13,
- radius = 7,
- })
- ConfirmNoBtn.ZIndex = 22
- -- Modal de Renomear
- local RenameOverlay = createOverlay(MainFrame, 25)
- local RenameFrame = createModalFrame(RenameOverlay, 340, 155, 26)
- local RenameTitleBar = Instance.new("Frame")
- RenameTitleBar.Size = UDim2.new(1, 0, 0, 32)
- RenameTitleBar.BackgroundColor3 = CLR.BG_DARK
- RenameTitleBar.BorderSizePixel = 0
- RenameTitleBar.ZIndex = 27
- RenameTitleBar.Parent = RenameFrame
- UI.corner(RenameTitleBar, 10)
- UI.label(RenameTitleBar, {
- text = "[ ] Renomear Posicao",
- sz = UDim2.new(1, -16, 1, 0),
- pos = UDim2.new(0, 12, 0, 0),
- size = 13,
- color = CLR.TEXT_PRI,
- font = Enum.Font.GothamBold,
- align = Enum.TextXAlignment.Left,
- }).ZIndex = 28
- local RenameField = Instance.new("TextBox")
- RenameField.Size = UDim2.new(1, -20, 0, 38)
- RenameField.Position = UDim2.new(0, 10, 0, 40)
- RenameField.BackgroundColor3 = CLR.BG_DARK
- RenameField.TextColor3 = CLR.TEXT_PRI
- RenameField.PlaceholderColor3 = CLR.TEXT_MUTED
- RenameField.PlaceholderText = " Digite o novo nome..."
- RenameField.Font = Enum.Font.Gotham
- RenameField.TextSize = 14
- RenameField.ClearTextOnFocus = false
- RenameField.ZIndex = 27
- RenameField.Parent = RenameFrame
- UI.corner(RenameField, 7)
- UI.stroke(RenameField, CLR.STROKE, 1)
- local RenameConfirmBtn = UI.button(RenameFrame, {
- text = "Confirmar",
- sz = UDim2.new(0.5, -12, 0, 34),
- pos = UDim2.new(0, 8, 1, -42),
- bgColor = CLR.INFO,
- hoverColor = CLR.INFO_HOV,
- size = 13,
- radius = 7,
- })
- RenameConfirmBtn.ZIndex = 27
- local RenameCancelBtn = UI.button(RenameFrame, {
- text = "Cancelar",
- sz = UDim2.new(0.5, -12, 0, 34),
- pos = UDim2.new(0.5, 4, 1, -42),
- bgColor = CLR.ACCENT_DIM,
- hoverColor = CLR.ACCENT,
- size = 13,
- radius = 7,
- })
- RenameCancelBtn.ZIndex = 27
- -- Modal de Mover
- local MoveOverlay = createOverlay(MainFrame, 30)
- local MoveFrame = createModalFrame(MoveOverlay, 300, 170, 31)
- local MoveTitleBar = Instance.new("Frame")
- MoveTitleBar.Size = UDim2.new(1, 0, 0, 32)
- MoveTitleBar.BackgroundColor3 = CLR.BG_DARK
- MoveTitleBar.BorderSizePixel = 0
- MoveTitleBar.ZIndex = 32
- MoveTitleBar.Parent = MoveFrame
- UI.corner(MoveTitleBar, 10)
- UI.label(MoveTitleBar, {
- text = "[ ] Mover Posicao",
- sz = UDim2.new(1, -16, 1, 0),
- pos = UDim2.new(0, 12, 0, 0),
- size = 13,
- color = CLR.TEXT_PRI,
- font = Enum.Font.GothamBold,
- align = Enum.TextXAlignment.Left,
- }).ZIndex = 33
- UI.label(MoveFrame, {
- text = "Slot de destino (1 - 50)",
- sz = UDim2.new(1, -20, 0, 22),
- pos = UDim2.new(0, 10, 0, 38),
- size = 12,
- color = CLR.TEXT_SEC,
- font = Enum.Font.Gotham,
- align = Enum.TextXAlignment.Center,
- }).ZIndex = 32
- local MoveInput = Instance.new("TextBox")
- MoveInput.Size = UDim2.new(0, 90, 0, 42)
- MoveInput.Position = UDim2.new(0.5, -45, 0, 64)
- MoveInput.BackgroundColor3 = CLR.BG_DARK
- MoveInput.TextColor3 = CLR.TEXT_PRI
- MoveInput.PlaceholderColor3 = CLR.TEXT_MUTED
- MoveInput.PlaceholderText = "1 - 50"
- MoveInput.Font = Enum.Font.GothamBold
- MoveInput.TextSize = 20
- MoveInput.ClearTextOnFocus = true
- MoveInput.ZIndex = 32
- MoveInput.Parent = MoveFrame
- UI.corner(MoveInput, 8)
- UI.stroke(MoveInput, CLR.STROKE, 1)
- local MoveConfirmBtn = UI.button(MoveFrame, {
- text = "Confirmar",
- sz = UDim2.new(0.5, -12, 0, 34),
- pos = UDim2.new(0, 8, 1, -42),
- bgColor = CLR.INFO,
- hoverColor = CLR.INFO_HOV,
- size = 13,
- radius = 7,
- })
- MoveConfirmBtn.ZIndex = 32
- local MoveCancelBtn = UI.button(MoveFrame, {
- text = "Cancelar",
- sz = UDim2.new(0.5, -12, 0, 34),
- pos = UDim2.new(0.5, 4, 1, -42),
- bgColor = CLR.ACCENT_DIM,
- hoverColor = CLR.ACCENT,
- size = 13,
- radius = 7,
- })
- MoveCancelBtn.ZIndex = 32
- -- ============================================================
- -- SEÇÃO 11.10 ► PAINEL LATERAL DE ACOES (novo - substitui menu flutuante)
- -- ============================================================
- --[[
- O painel ocupa toda a area de slots do MainFrame.
- Desliza da direita para dentro quando o botão = é clicado.
- Conteudo: cabecalho com nome do slot, 4 botoes de acao, botao voltar.
- ]]
- local ActionPanel = Instance.new("Frame")
- ActionPanel.Name = "ActionPanel"
- ActionPanel.Size = UDim2.new(1, -20, 1, -198)
- ActionPanel.Position = UDim2.new(0, 10, 0, 194)
- ActionPanel.BackgroundColor3 = CLR.BG_MID
- ActionPanel.BorderSizePixel = 0
- ActionPanel.Visible = false
- ActionPanel.ZIndex = 15
- ActionPanel.Parent = MainFrame
- UI.corner(ActionPanel, 8)
- UI.stroke(ActionPanel, CLR.STROKE_BRIGHT, 1.5)
- UI.gradient(ActionPanel, CLR.BG_DARK, CLR.BG_MID, 135)
- -- Cabecalho do painel
- local APHeader = Instance.new("Frame")
- APHeader.Name = "Header"
- APHeader.Size = UDim2.new(1, 0, 0, 44)
- APHeader.BackgroundColor3 = CLR.BG_DARK
- APHeader.BorderSizePixel = 0
- APHeader.ZIndex = 16
- APHeader.Parent = ActionPanel
- UI.corner(APHeader, 8)
- UI.stroke(APHeader, CLR.STROKE, 1)
- -- Divisor horizontal abaixo do header
- local APDivider = Instance.new("Frame")
- APDivider.Size = UDim2.new(1, 0, 0, 1)
- APDivider.Position = UDim2.new(0, 0, 1, 0)
- APDivider.BackgroundColor3 = CLR.STROKE
- APDivider.BorderSizePixel = 0
- APDivider.ZIndex = 17
- APDivider.Parent = APHeader
- -- Indicador de cor no header (barra lateral)
- local APHeaderAccent = Instance.new("Frame")
- APHeaderAccent.Size = UDim2.new(0, 3, 1, -12)
- APHeaderAccent.Position = UDim2.new(0, 0, 0, 6)
- APHeaderAccent.BackgroundColor3 = CLR.ACCENT
- APHeaderAccent.BorderSizePixel = 0
- APHeaderAccent.ZIndex = 17
- APHeaderAccent.Parent = APHeader
- UI.corner(APHeaderAccent, 2)
- -- Numero do slot (badge)
- local APSlotBadge = Instance.new("Frame")
- APSlotBadge.Size = UDim2.new(0, 28, 0, 20)
- APSlotBadge.Position = UDim2.new(0, 8, 0.5, -10)
- APSlotBadge.BackgroundColor3 = CLR.ACCENT_DIM
- APSlotBadge.BorderSizePixel = 0
- APSlotBadge.ZIndex = 17
- APSlotBadge.Parent = APHeader
- UI.corner(APSlotBadge, 4)
- local APSlotNum = UI.label(APSlotBadge, {
- text = "00",
- size = 10,
- color = CLR.TEXT_PRI,
- font = Enum.Font.GothamBold,
- align = Enum.TextXAlignment.Center,
- })
- APSlotNum.ZIndex = 18
- -- Nome da posicao no header
- local APSlotName = UI.label(APHeader, {
- text = "",
- sz = UDim2.new(1, -110, 1, 0),
- pos = UDim2.new(0, 44, 0, 0),
- size = 14,
- color = CLR.TEXT_PRI,
- font = Enum.Font.GothamBold,
- align = Enum.TextXAlignment.Left,
- truncate = Enum.TextTruncate.AtEnd,
- })
- APSlotName.ZIndex = 17
- -- Coordenadas no header
- local APCoords = UI.label(APHeader, {
- text = "",
- sz = UDim2.new(1, -110, 0, 14),
- pos = UDim2.new(0, 44, 1, -16),
- size = 9,
- color = CLR.TEXT_MUTED,
- font = Enum.Font.Gotham,
- align = Enum.TextXAlignment.Left,
- })
- APCoords.ZIndex = 17
- -- Botao Voltar (canto direito do header)
- local APBackBtn = UI.button(APHeader, {
- text = "< Voltar",
- sz = UDim2.new(0, 80, 0, 28),
- pos = UDim2.new(1, -88, 0.5, -14),
- bgColor = CLR.BG_LIGHT,
- hoverColor = CLR.ACCENT_DIM,
- textColor = CLR.TEXT_SEC,
- font = Enum.Font.GothamSemibold,
- size = 12,
- radius = 6,
- })
- APBackBtn.ZIndex = 17
- -- Area dos botoes de acao
- local APActionsFrame = Instance.new("Frame")
- APActionsFrame.Name = "Actions"
- APActionsFrame.Size = UDim2.new(1, -24, 1, -64)
- APActionsFrame.Position = UDim2.new(0, 12, 0, 54)
- APActionsFrame.BackgroundTransparency = 1
- APActionsFrame.ZIndex = 16
- APActionsFrame.Parent = ActionPanel
- -- Layout dos botoes de acao (lista vertical centralizada)
- local APListLayout = Instance.new("UIListLayout")
- APListLayout.FillDirection = Enum.FillDirection.Vertical
- APListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
- APListLayout.VerticalAlignment = Enum.VerticalAlignment.Center
- APListLayout.Padding = UDim.new(0, 10)
- APListLayout.SortOrder = Enum.SortOrder.LayoutOrder
- APListLayout.Parent = APActionsFrame
- -- Funcao helper para criar botoes de acao do painel
- local function makeActionBtn(icon, label, bgColor, hoverColor, order)
- local btn = Instance.new("TextButton")
- btn.Name = label
- btn.Size = UDim2.new(1, 0, 0, 48)
- btn.BackgroundColor3 = bgColor
- btn.BorderSizePixel = 0
- btn.AutoButtonColor = false
- btn.Text = ""
- btn.LayoutOrder = order
- btn.ZIndex = 17
- btn.Parent = APActionsFrame
- UI.corner(btn, 8)
- UI.stroke(btn, CLR.STROKE, 1)
- UI.hoverBG(btn, hoverColor, bgColor)
- -- Hover: stroke mais brilhante
- local btnStroke = btn:FindFirstChildWhichIsA("UIStroke")
- btn.MouseEnter:Connect(function()
- if btnStroke then
- TweenService:Create(btnStroke, CFG.TWEEN_FAST,
- {Color = CLR.STROKE_BRIGHT}):Play()
- end
- end)
- btn.MouseLeave:Connect(function()
- if btnStroke then
- TweenService:Create(btnStroke, CFG.TWEEN_FAST,
- {Color = CLR.STROKE}):Play()
- end
- end)
- -- Icone (lado esquerdo)
- local iconLbl = UI.label(btn, {
- text = icon,
- sz = UDim2.new(0, 48, 1, 0),
- pos = UDim2.new(0, 0, 0, 0),
- size = 18,
- color = CLR.TEXT_SEC,
- font = Enum.Font.GothamBold,
- align = Enum.TextXAlignment.Center,
- })
- iconLbl.ZIndex = 18
- -- Linha vertical separadora
- local sep = Instance.new("Frame")
- sep.Size = UDim2.new(0, 1, 1, -16)
- sep.Position = UDim2.new(0, 48, 0, 8)
- sep.BackgroundColor3 = CLR.STROKE
- sep.BorderSizePixel = 0
- sep.ZIndex = 18
- sep.Parent = btn
- -- Texto do botao
- local textLbl = UI.label(btn, {
- text = label,
- sz = UDim2.new(1, -60, 1, 0),
- pos = UDim2.new(0, 56, 0, 0),
- size = 14,
- color = CLR.TEXT_PRI,
- font = Enum.Font.GothamSemibold,
- align = Enum.TextXAlignment.Left,
- })
- textLbl.ZIndex = 18
- return btn, iconLbl, textLbl
- end
- -- Os 4 botoes de acao
- local APMainBtn, APMainIcon, APMainLbl = makeActionBtn("*", "Definir como Principal", CLR.BG_CARD, CLR.BG_CARD_HOV, 1)
- local APRenameBtn, APRenameIcon, APRenameLbl = makeActionBtn("[ ]","Renomear", CLR.BG_CARD, CLR.BG_CARD_HOV, 2)
- local APMoveBtn, APMoveIcon, APMoveLbl = makeActionBtn("<->","Mover para Slot", CLR.BG_CARD, CLR.BG_CARD_HOV, 3)
- local APDeleteBtn, APDeleteIcon, APDeleteLbl = makeActionBtn("X", "Excluir Posicao", CLR.BG_CARD, CLR.BG_CARD_HOV, 4)
- -- Cor especial para excluir
- APDeleteLbl.TextColor3 = CLR.DANGER
- APDeleteIcon.TextColor3 = CLR.DANGER
- -- Estado do painel de acoes
- local currentActionSlot = nil
- -- Funcao para abrir o painel de acoes para um slot especifico
- local function openActionPanel(slot)
- local data = State.saves[slot]
- if not data then return end
- currentActionSlot = slot
- -- Atualiza header
- APSlotNum.Text = string.format("%02d", slot)
- APSlotName.Text = data.name
- local pos = data.position.Position
- APCoords.Text = string.format("X %.1f Y %.1f Z %.1f", pos.X, pos.Y, pos.Z)
- -- Atualiza icone/texto do botao principal conforme estado atual
- local isMain = State.mainPos
- and typeof(State.mainPos.position) == "CFrame"
- and State.mainPos.position == data.position
- if isMain then
- APMainIcon.Text = "*"
- APMainLbl.Text = "Remover Principal"
- APMainLbl.TextColor3 = CLR.WARNING
- APMainIcon.TextColor3 = CLR.WARNING
- else
- APMainIcon.Text = "*"
- APMainLbl.Text = "Definir como Principal"
- APMainLbl.TextColor3 = CLR.TEXT_PRI
- APMainIcon.TextColor3 = CLR.TEXT_SEC
- end
- -- Mostra o painel e esconde os slots
- SlotsFrame.Visible = false
- ActionPanel.Visible = true
- end
- -- Funcao para fechar o painel e voltar aos slots
- local function closeActionPanel()
- ActionPanel.Visible = false
- SlotsFrame.Visible = true
- currentActionSlot = nil
- end
- -- ============================================================
- -- SEÇÃO 12 ► LOGICA DE ESTADO E ATUALIZACAO DA UI
- -- ============================================================
- local onConfirmYes = nil
- local slotRenaming = nil
- local slotMoving = nil
- local function refreshSlotUI()
- local n = State.saveSlot
- local data = State.saves[n]
- SlotValueLbl.Text = string.format("%02d", n)
- if data then
- SlotStatusLbl.Text = "ocupado"
- SlotStatusLbl.TextColor3 = CLR.WARNING
- else
- SlotStatusLbl.Text = "livre"
- SlotStatusLbl.TextColor3 = CLR.SUCCESS
- end
- end
- local function refreshPageDots()
- for i, dot in ipairs(PageDots) do
- TweenService:Create(dot, CFG.TWEEN_FAST, {
- BackgroundColor3 = (i == State.currentPage) and CLR.ACCENT or CLR.TEXT_MUTED,
- Size = (i == State.currentPage)
- and UDim2.new(0, 9, 0, 9)
- or UDim2.new(0, 7, 0, 7),
- }):Play()
- end
- end
- local function refreshPageLabel()
- PageLabel.Text = string.format("PAGINA %d / %d", State.currentPage, CFG.MAX_PAGES)
- end
- local function refreshMainPosUI()
- MainPosContainer.Visible = (State.mainPos ~= nil)
- end
- local function findNextEmpty()
- for i = 1, CFG.MAX_SAVES do
- if not State.saves[i] then return i end
- end
- return (State.saveSlot % CFG.MAX_SAVES) + 1
- end
- local function advanceSlot()
- State.saveSlot = findNextEmpty()
- refreshSlotUI()
- end
- local function showConfirm(message, yesText, callback)
- ConfirmLabel.Text = message
- ConfirmYesBtn.Text = yesText or "Confirmar"
- onConfirmYes = callback
- ConfirmOverlay.Visible = true
- end
- -- Formata coordenadas
- local function formatCoords(cf)
- local pos = cf.Position
- return string.format("X: %.1f Y: %.1f Z: %.1f", pos.X, pos.Y, pos.Z)
- end
- -- ────────────────────────────────────────────────
- -- 12.1 ► RENDERIZACAO DOS SLOTS
- -- ────────────────────────────────────────────────
- local function buildSlot(slot)
- local data = State.saves[slot]
- local card = Instance.new("Frame")
- card.Name = "Slot_" .. slot
- card.LayoutOrder = slot
- card.BackgroundColor3 = CLR.BG_CARD
- card.BorderSizePixel = 0
- card.ClipsDescendants = false
- UI.corner(card, 7)
- local cardStroke = UI.stroke(card, CLR.STROKE, 1)
- if data then
- local isMain = State.mainPos
- and typeof(State.mainPos.position) == "CFrame"
- and State.mainPos.position == data.position
- -- Barra lateral colorida (sempre roxo/accent)
- local AccentBar = Instance.new("Frame")
- AccentBar.Size = UDim2.new(0, 3, 1, -10)
- AccentBar.Position = UDim2.new(0, 0, 0, 5)
- AccentBar.BackgroundColor3 = CLR.ACCENT
- AccentBar.BorderSizePixel = 0
- AccentBar.Parent = card
- UI.corner(AccentBar, 2)
- -- Badge numero do slot
- local SlotNumBg = Instance.new("Frame")
- SlotNumBg.Size = UDim2.new(0, 24, 0, 18)
- SlotNumBg.Position = UDim2.new(0, 7, 0.5, -9)
- SlotNumBg.BackgroundColor3 = CLR.ACCENT_DIM
- SlotNumBg.BorderSizePixel = 0
- SlotNumBg.Parent = card
- UI.corner(SlotNumBg, 4)
- UI.label(SlotNumBg, {
- text = tostring(slot),
- size = 9,
- color = CLR.TEXT_PRI,
- font = Enum.Font.GothamBold,
- align = Enum.TextXAlignment.Center,
- })
- -- Indicador de principal (ponto dourado discreto, sem emoji)
- if isMain then
- local mainDot = Instance.new("Frame")
- mainDot.Size = UDim2.new(0, 5, 0, 5)
- mainDot.Position = UDim2.new(0, 7, 0, 5)
- mainDot.BackgroundColor3 = CLR.GOLD
- mainDot.BorderSizePixel = 0
- mainDot.Parent = card
- UI.corner(mainDot, 3)
- end
- -- Nome da posicao
- UI.label(card, {
- text = data.name,
- sz = UDim2.new(1, -100, 0, 22),
- pos = UDim2.new(0, 38, 0, 4),
- size = 12,
- color = CLR.TEXT_PRI,
- font = Enum.Font.GothamSemibold,
- align = Enum.TextXAlignment.Left,
- truncate = Enum.TextTruncate.AtEnd,
- })
- -- Coordenadas XYZ
- UI.label(card, {
- text = formatCoords(data.position),
- sz = UDim2.new(1, -100, 0, 14),
- pos = UDim2.new(0, 38, 0, 27),
- size = 9,
- color = CLR.TEXT_MUTED,
- font = Enum.Font.Gotham,
- align = Enum.TextXAlignment.Left,
- })
- -- Botao Teleportar
- local TpBtn = UI.button(card, {
- text = "TP",
- sz = UDim2.new(0, 34, 0, 34),
- pos = UDim2.new(1, -78, 0.5, -17),
- bgColor = CLR.ACCENT,
- hoverColor = CLR.ACCENT_HOV,
- font = Enum.Font.GothamBold,
- size = 13,
- radius = 7,
- })
- -- [ALTERACAO PRINCIPAL] Botao = (menu de acoes) - substitui estrela e menu antigo
- local MenuBtn = UI.button(card, {
- text = "=",
- sz = UDim2.new(0, 34, 0, 34),
- pos = UDim2.new(1, -40, 0.5, -17),
- bgColor = CLR.BG_LIGHT,
- hoverColor = CLR.ACCENT_DIM,
- textColor = CLR.TEXT_SEC,
- font = Enum.Font.GothamBold,
- size = 16,
- radius = 7,
- })
- -- Hover visual no card
- card.MouseEnter:Connect(function()
- TweenService:Create(card, CFG.TWEEN_FAST,
- {BackgroundColor3 = CLR.BG_CARD_HOV}):Play()
- TweenService:Create(cardStroke, CFG.TWEEN_FAST,
- {Color = CLR.STROKE_BRIGHT}):Play()
- end)
- card.MouseLeave:Connect(function()
- TweenService:Create(card, CFG.TWEEN_FAST,
- {BackgroundColor3 = CLR.BG_CARD}):Play()
- TweenService:Create(cardStroke, CFG.TWEEN_FAST,
- {Color = CLR.STROKE}):Play()
- end)
- -- Acao TP
- TpBtn.MouseButton1Click:Connect(function()
- if Teleport.to(data.position) then
- TpBtn.Text = "OK"
- TweenService:Create(TpBtn, CFG.TWEEN_FAST,
- {BackgroundColor3 = CLR.SUCCESS}):Play()
- task.wait(1.2)
- if TpBtn.Parent then
- TpBtn.Text = "TP"
- TweenService:Create(TpBtn, CFG.TWEEN_FAST,
- {BackgroundColor3 = CLR.ACCENT}):Play()
- end
- end
- end)
- -- Acao abrir painel de acoes
- MenuBtn.MouseButton1Click:Connect(function()
- openActionPanel(slot)
- end)
- else
- -- Slot vazio
- local emptyLbl = UI.label(card, {
- text = string.format("Slot %02d - vazio", slot),
- sz = UDim2.new(1, -10, 1, 0),
- pos = UDim2.new(0, 10, 0, 0),
- size = 12,
- color = CLR.TEXT_MUTED,
- font = Enum.Font.Gotham,
- align = Enum.TextXAlignment.Left,
- })
- if slot == State.saveSlot then
- cardStroke.Color = CLR.ACCENT
- cardStroke.Thickness = 1.5
- emptyLbl.TextColor3 = CLR.ACCENT
- emptyLbl.Text = string.format("> Slot %02d - selecionado", slot)
- end
- end
- return card
- end
- -- Renderiza a pagina atual
- local function renderPage()
- -- Fecha painel de acoes se estiver aberto
- if ActionPanel.Visible then
- closeActionPanel()
- end
- -- Limpa cards anteriores
- for _, child in ipairs(SlotsFrame:GetChildren()) do
- if child:IsA("Frame") then child:Destroy() end
- end
- State.optionFrames = {}
- local startSlot = (State.currentPage - 1) * CFG.SLOTS_PER_PAGE + 1
- local endSlot = State.currentPage * CFG.SLOTS_PER_PAGE
- for slot = startSlot, endSlot do
- local card = buildSlot(slot)
- card.Parent = SlotsFrame
- end
- refreshPageDots()
- refreshPageLabel()
- end
- -- BindableEvent de re-render
- local RebuildEvent = Instance.new("BindableEvent")
- RebuildEvent.Name = "__rebuild"
- RebuildEvent.Parent = ScreenGui
- RebuildEvent.Event:Connect(renderPage)
- -- ============================================================
- -- SEÇÃO 13 ► ACOES DO PAINEL LATERAL
- -- ============================================================
- -- Botao Voltar
- APBackBtn.MouseButton1Click:Connect(function()
- closeActionPanel()
- end)
- -- Botao Principal (definir / remover)
- APMainBtn.MouseButton1Click:Connect(function()
- local slot = currentActionSlot
- if not slot or not State.saves[slot] then return end
- local data = State.saves[slot]
- local isMain = State.mainPos
- and typeof(State.mainPos.position) == "CFrame"
- and State.mainPos.position == data.position
- if isMain then
- showConfirm(
- "Remover esta posicao principal?",
- "Remover",
- function()
- State.mainPos = nil
- Persist.deleteMain()
- refreshMainPosUI()
- closeActionPanel()
- renderPage()
- end
- )
- else
- showConfirm(
- State.mainPos
- and "Substituir a posicao principal atual?"
- or "Definir esta posicao como principal?",
- "Confirmar",
- function()
- State.mainPos = {
- position = data.position,
- name = data.name,
- timestamp = data.timestamp,
- }
- Persist.scheduleSave()
- refreshMainPosUI()
- closeActionPanel()
- renderPage()
- end
- )
- end
- end)
- -- Botao Renomear
- APRenameBtn.MouseButton1Click:Connect(function()
- local slot = currentActionSlot
- if not slot or not State.saves[slot] then return end
- slotRenaming = slot
- RenameField.Text = State.saves[slot].name
- RenameOverlay.Visible = true
- end)
- -- Botao Mover
- APMoveBtn.MouseButton1Click:Connect(function()
- local slot = currentActionSlot
- if not slot or not State.saves[slot] then return end
- slotMoving = slot
- MoveInput.Text = ""
- MoveOverlay.Visible = true
- end)
- -- Botao Excluir
- APDeleteBtn.MouseButton1Click:Connect(function()
- local slot = currentActionSlot
- if not slot or not State.saves[slot] then return end
- showConfirm(
- string.format("Excluir o Slot %d \"%s\" ?", slot, State.saves[slot].name),
- "Excluir",
- function()
- State.saves[slot] = nil
- Persist.scheduleSave()
- showToast("Slot " .. slot .. " excluido", "info")
- advanceSlot()
- closeActionPanel()
- renderPage()
- end
- )
- end)
- -- ============================================================
- -- SEÇÃO 14 ► CONEXOES DOS CONTROLES PRINCIPAIS
- -- ============================================================
- -- Fechar totalmente
- DestroyBtn.MouseButton1Click:Connect(function()
- showConfirm(
- "Fechar completamente o Save Locate?\n(So voltara ao executar novamente)",
- "Fechar",
- function()
- Persist.save()
- task.wait(0.1)
- ScreenGui:Destroy()
- end
- )
- end)
- -- Minimizar
- CloseBtn.MouseButton1Click:Connect(function()
- TweenService:Create(MainFrame, CFG.TWEEN_MED, {
- Size = UDim2.new(0, 590, 0, 0),
- Position = UDim2.new(0.5, -295, 0.5, 0),
- }):Play()
- task.wait(0.3)
- MainFrame.Visible = false
- MainFrame.Size = UDim2.new(0, 590, 0, 470)
- MainFrame.Position = UDim2.new(0.5, -295, 0.5, -235)
- end)
- FloatBtn.MouseButton1Click:Connect(function()
- MainFrame.Size = UDim2.new(0, 590, 0, 0)
- MainFrame.Position = UDim2.new(0.5, -295, 0.5, 0)
- MainFrame.Visible = true
- TweenService:Create(MainFrame, CFG.TWEEN_MED, {
- Size = UDim2.new(0, 590, 0, 470),
- Position = UDim2.new(0.5, -295, 0.5, -235),
- }):Play()
- end)
- MainFrame:GetPropertyChangedSignal("Visible"):Connect(function()
- FloatContainer.Visible = not MainFrame.Visible
- end)
- -- Paginacao
- local function changePage(delta)
- local newPage = State.currentPage + delta
- if newPage < 1 then newPage = CFG.MAX_PAGES end
- if newPage > CFG.MAX_PAGES then newPage = 1 end
- State.currentPage = newPage
- renderPage()
- end
- PagePrevBtn.MouseButton1Click:Connect(function() changePage(-1) end)
- PageNextBtn.MouseButton1Click:Connect(function() changePage( 1) end)
- -- Seletor de slot
- SlotPrevBtn.MouseButton1Click:Connect(function()
- State.saveSlot = ((State.saveSlot - 2 + CFG.MAX_SAVES) % CFG.MAX_SAVES) + 1
- refreshSlotUI()
- renderPage()
- end)
- SlotNextBtn.MouseButton1Click:Connect(function()
- State.saveSlot = (State.saveSlot % CFG.MAX_SAVES) + 1
- refreshSlotUI()
- renderPage()
- end)
- -- Salvar
- local function doSave()
- if not root then return false end
- local name = (NameBox.Text ~= "" and NameBox.Text) or ("Slot " .. State.saveSlot)
- State.saves[State.saveSlot] = {
- position = root.CFrame,
- name = name,
- timestamp = os.date("%Y-%m-%d %H:%M:%S"),
- }
- Persist.scheduleSave()
- local targetPage = math.ceil(State.saveSlot / CFG.SLOTS_PER_PAGE)
- if targetPage ~= State.currentPage then
- State.currentPage = targetPage
- end
- renderPage()
- showToast(string.format("Salvo no Slot %d", State.saveSlot), "success")
- advanceSlot()
- return true
- end
- SaveBtn.MouseButton1Click:Connect(function()
- if State.saves[State.saveSlot] then
- showConfirm(
- string.format("O Slot %d ja tem uma posicao.\nDeseja substituir?", State.saveSlot),
- "Substituir",
- doSave
- )
- else
- doSave()
- end
- end)
- -- Posicao Principal (botao flutuante)
- MainPosBtn.MouseButton1Click:Connect(function()
- if State.mainPos and Teleport.to(State.mainPos.position) then
- MainPosBtn.Text = "OK"
- TweenService:Create(MainPosBtn, CFG.TWEEN_FAST,
- {BackgroundColor3 = CLR.SUCCESS}):Play()
- task.wait(1)
- if MainPosBtn.Parent then
- MainPosBtn.Text = ">>"
- TweenService:Create(MainPosBtn, CFG.TWEEN_FAST,
- {BackgroundColor3 = CLR.BG_MID}):Play()
- end
- end
- end)
- MainPosBtn.MouseEnter:Connect(function()
- if State.mainPos then
- TooltipLabel.Text = State.mainPos.name or "Principal"
- MainPosTooltip.Visible = true
- MainPosRemoveBtn.Visible = true
- end
- TweenService:Create(MainPosStroke, CFG.TWEEN_FAST, {Thickness = 3}):Play()
- TweenService:Create(MainPosBtn, CFG.TWEEN_FAST,
- {Size = UDim2.new(1.1, 0, 1.1, 0)}):Play()
- end)
- MainPosBtn.MouseLeave:Connect(function()
- MainPosTooltip.Visible = false
- MainPosRemoveBtn.Visible = false
- TweenService:Create(MainPosStroke, CFG.TWEEN_FAST, {Thickness = 2}):Play()
- TweenService:Create(MainPosBtn, CFG.TWEEN_FAST,
- {Size = UDim2.new(1, 0, 1, 0)}):Play()
- end)
- MainPosRemoveBtn.MouseButton1Click:Connect(function()
- showConfirm("Remover a posicao principal?", "Remover", function()
- State.mainPos = nil
- Persist.deleteMain()
- refreshMainPosUI()
- renderPage()
- end)
- end)
- -- Confirmacao
- ConfirmYesBtn.MouseButton1Click:Connect(function()
- if onConfirmYes then onConfirmYes() end
- onConfirmYes = nil
- ConfirmOverlay.Visible = false
- end)
- ConfirmNoBtn.MouseButton1Click:Connect(function()
- onConfirmYes = nil
- ConfirmOverlay.Visible = false
- end)
- -- Renomear
- RenameConfirmBtn.MouseButton1Click:Connect(function()
- local newName = RenameField.Text
- if not slotRenaming or newName == "" then return end
- local data = State.saves[slotRenaming]
- if data then
- data.name = newName
- if State.mainPos and State.mainPos.position == data.position then
- State.mainPos.name = newName
- end
- Persist.scheduleSave()
- -- Atualiza header do painel se ainda estiver aberto para o mesmo slot
- if currentActionSlot == slotRenaming and ActionPanel.Visible then
- APSlotName.Text = newName
- end
- renderPage()
- showToast("Slot " .. slotRenaming .. " renomeado", "info")
- end
- RenameOverlay.Visible = false
- slotRenaming = nil
- end)
- RenameCancelBtn.MouseButton1Click:Connect(function()
- RenameOverlay.Visible = false
- slotRenaming = nil
- end)
- -- Mover
- MoveConfirmBtn.MouseButton1Click:Connect(function()
- local target = tonumber(MoveInput.Text)
- if not slotMoving or not target then return end
- if target < 1 or target > CFG.MAX_SAVES or target ~= math.floor(target) then
- MoveInput.Text = ""
- MoveInput.PlaceholderText = "Invalido! (1-50)"
- task.delay(1.5, function() MoveInput.PlaceholderText = "1 - 50" end)
- return
- end
- local from, to = slotMoving, target
- if from ~= to then
- if State.saves[to] then
- local temp = State.saves[from]
- State.saves[from] = State.saves[to]
- State.saves[to] = temp
- else
- State.saves[to] = State.saves[from]
- State.saves[from] = nil
- end
- Persist.scheduleSave()
- State.currentPage = math.ceil(to / CFG.SLOTS_PER_PAGE)
- closeActionPanel()
- renderPage()
- showToast(string.format("Slot %d movido para Slot %d", from, to), "info")
- end
- MoveOverlay.Visible = false
- slotMoving = nil
- end)
- MoveCancelBtn.MouseButton1Click:Connect(function()
- MoveOverlay.Visible = false
- slotMoving = nil
- end)
- -- Enter confirma campos de texto
- RenameField.FocusLost:Connect(function(enter)
- if enter then RenameConfirmBtn.MouseButton1Click:Fire() end
- end)
- MoveInput.FocusLost:Connect(function(enter)
- if enter then MoveConfirmBtn.MouseButton1Click:Fire() end
- end)
- -- ============================================================
- -- SEÇÃO 15 ► EVENTOS DE CICLO DE VIDA
- -- ============================================================
- plr.CharacterAdded:Connect(function(character)
- chr = character
- root = character:WaitForChild("HumanoidRootPart")
- end)
- game:BindToClose(Persist.save)
- task.spawn(function()
- while task.wait(60) do
- if State.isDirty then Persist.save() end
- end
- end)
- -- ============================================================
- -- SEÇÃO 16 ► INICIALIZACAO
- -- ============================================================
- Persist.load()
- refreshSlotUI()
- refreshMainPosUI()
- renderPage()
- print(string.format(
- "Save Locate (NickZ) V%s | %d slots | FS: %s",
- CFG.VERSION,
- CFG.MAX_SAVES,
- FS.active and "ativo" or "inativo"
- ))
Add Comment
Please, Sign In to add comment