Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- ╔══════════════════════════════════════════════════════════════╗
- ║ SAVE LOCATE (NickZ) - v5.0 ULTIMATE ║
- ║ ║
- ║ • 50 Slots de Salvamento (5 Páginas) ║
- ║ • Sistema de Posição Principal ║
- ║ • Teleporte Anti-Bug/Anti-Animação Avançado ║
- ║ • Sistema de Arquivos Multiplataforma ║
- ║ • Interface Moderna e Otimizada ║
- ║ • Código Modular e Altamente Otimizado ║
- ╚══════════════════════════════════════════════════════════════╝
- ]]
- --==============================================================================
- -- [1] INICIALIZAÇÃO E ESPERA DO JOGO
- --==============================================================================
- if not game or not game:IsLoaded() then
- game.Loaded:Wait()
- end
- repeat task.wait() until game:IsLoaded()
- --==============================================================================
- -- [2] SERVIÇOS (CACHE PARA PERFORMANCE)
- --==============================================================================
- local Services = {
- Players = game:GetService("Players"),
- HttpService = game:GetService("HttpService"),
- TweenService = game:GetService("TweenService"),
- UserInputService = game:GetService("UserInputService"),
- RunService = game:GetService("RunService"),
- CoreGui = game:GetService("CoreGui")
- }
- --==============================================================================
- -- [3] VARIÁVEIS GLOBAIS E CONFIGURAÇÃO
- --==============================================================================
- local Config = {
- MAX_SAVES = 50,
- SLOTS_PER_PAGE = 10,
- MAX_PAGES = 5,
- SAVE_DEBOUNCE = 0.5, -- Evita salvamentos múltiplos rápidos
- TWEEN_SPEED = 0.2,
- ANIMATION_INFO = TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
- }
- local State = {
- savedPositions = {},
- currentPage = 1,
- currentSaveSlot = 1,
- mainPosition = nil,
- lastSaveTime = 0,
- isInitialized = false
- }
- local Player = {
- instance = Services.Players.LocalPlayer,
- character = nil,
- humanoidRoot = nil,
- userId = Services.Players.LocalPlayer.UserId,
- userName = Services.Players.LocalPlayer.Name
- }
- --==============================================================================
- -- [4] SISTEMA DE ARQUIVOS (MULTIPLATAFORMA)
- --==============================================================================
- local FileSystem = {}
- do
- -- Verificação de capacidades do executor
- FileSystem.capabilities = {
- write = (type(writefile) == "function"),
- read = (type(readfile) == "function"),
- folder = (type(isfolder) == "function" and type(makefolder) == "function"),
- isFile = (type(isfile) == "function"),
- delete = (type(delfile) == "function")
- }
- FileSystem.hasFS = (FileSystem.capabilities.write and FileSystem.capabilities.read)
- -- Configuração de diretórios
- local SAVE_DIR = "SaveLocate_NickZ"
- local GAME_DIR = tostring(game.PlaceId)
- if FileSystem.capabilities.folder then
- if not isfolder(SAVE_DIR) then
- pcall(makefolder, SAVE_DIR)
- end
- if not isfolder(SAVE_DIR .. "/" .. GAME_DIR) then
- pcall(makefolder, SAVE_DIR .. "/" .. GAME_DIR)
- end
- end
- -- Nomes de arquivo
- local function generatePaths()
- local baseName = string.format("Saves_%s.json", Player.userId)
- local mainName = string.format("MainPos_%s.json", Player.userId)
- local baseBak = string.format("Saves_%s.bak", Player.userId)
- local mainBak = string.format("MainPos_%s.bak", Player.userId)
- if FileSystem.capabilities.folder then
- return {
- save = SAVE_DIR .. "/" .. GAME_DIR .. "/" .. baseName,
- main = SAVE_DIR .. "/" .. GAME_DIR .. "/" .. mainName,
- saveBak = SAVE_DIR .. "/" .. GAME_DIR .. "/" .. baseBak,
- mainBak = SAVE_DIR .. "/" .. GAME_DIR .. "/" .. mainBak
- }
- else
- return {
- save = string.format("SaveLocate_%d_%s.json", game.PlaceId, Player.userId),
- main = string.format("MainPos_%d_%s.json", game.PlaceId, Player.userId),
- saveBak = string.format("SaveLocate_%d_%s.bak", game.PlaceId, Player.userId),
- mainBak = string.format("MainPos_%d_%s.bak", game.PlaceId, Player.userId)
- }
- end
- end
- FileSystem.paths = generatePaths()
- -- Funções auxiliares
- function FileSystem:tryRead(path)
- if not self.hasFS or type(path) ~= "string" then return nil end
- local success, content = pcall(readfile, path)
- return (success and type(content) == "string" and #content > 0) and content or nil
- end
- function FileSystem:tryWrite(path, content)
- if not self.hasFS or type(path) ~= "string" or type(content) ~= "string" then
- return false
- end
- local success = pcall(writefile, path, content)
- return success
- end
- function FileSystem:getCandidatePaths(fileType)
- local paths = {}
- if fileType == "save" then
- table.insert(paths, self.paths.save)
- table.insert(paths, self.paths.saveBak)
- elseif fileType == "main" then
- table.insert(paths, self.paths.main)
- table.insert(paths, self.paths.mainBak)
- end
- return paths
- end
- function FileSystem:findExistingContent(fileType)
- local candidates = self:getCandidatePaths(fileType)
- for _, path in ipairs(candidates) do
- local content = self:tryRead(path)
- if content then
- return content, path
- end
- end
- return nil, nil
- end
- end
- --==============================================================================
- -- [5] CODIFICAÇÃO CFRAME (OTIMIZADO)
- --==============================================================================
- local CFrameUtils = {}
- function CFrameUtils.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,
- R00 = R00, R01 = R01, R02 = R02,
- R10 = R10, R11 = R11, R12 = R12,
- R20 = R20, R21 = R21, R22 = R22
- }
- end
- function CFrameUtils.decode(data)
- if type(data) ~= "table" or not data.x or not data.R00 then return nil end
- return CFrame.new(
- data.x, data.y, data.z,
- data.R00, data.R01, data.R02,
- data.R10, data.R11, data.R12,
- data.R20, data.R21, data.R22
- )
- end
- --==============================================================================
- -- [6] SISTEMA DE PERSISTÊNCIA (COM DEBOUNCE)
- --==============================================================================
- local Persistence = {}
- do
- local saveQueue = false
- function Persistence:saveMainPosition()
- if not FileSystem.hasFS or not State.mainPosition then return end
- local data = {
- position = CFrameUtils.encode(State.mainPosition.position),
- name = State.mainPosition.name,
- timestamp = State.mainPosition.timestamp,
- gameId = game.PlaceId,
- gameName = game.Name
- }
- local success, encoded = pcall(function()
- return Services.HttpService:JSONEncode(data)
- end)
- if success and encoded then
- FileSystem:tryWrite(FileSystem.paths.main, encoded)
- FileSystem:tryWrite(FileSystem.paths.mainBak, encoded)
- end
- end
- function Persistence:loadMainPosition()
- if not FileSystem.hasFS then return end
- local content, usedPath = FileSystem:findExistingContent("main")
- if not content then return end
- local success, data = pcall(function()
- return Services.HttpService:JSONDecode(content)
- end)
- if not success or type(data) ~= "table" then return end
- if data.gameId and data.gameId ~= game.PlaceId then return end
- local cf = CFrameUtils.decode(data.position)
- if cf then
- State.mainPosition = {
- position = cf,
- name = data.name or "Posição Principal",
- timestamp = data.timestamp or os.date("%Y-%m-%d %H:%M:%S")
- }
- if usedPath ~= FileSystem.paths.main then
- self:saveMainPosition()
- end
- end
- end
- function Persistence:saveToDisk()
- if not FileSystem.hasFS then return end
- -- Debounce para evitar salvamentos excessivos
- local currentTime = tick()
- if currentTime - State.lastSaveTime < Config.SAVE_DEBOUNCE then
- saveQueue = true
- return
- end
- State.lastSaveTime = currentTime
- saveQueue = false
- local data = {
- gameId = game.PlaceId,
- gameName = game.Name,
- userId = Player.userId,
- userName = Player.userName,
- saveDate = os.date("%Y-%m-%d %H:%M:%S"),
- version = "5.0",
- positions = {}
- }
- for slot, slotData in pairs(State.savedPositions) do
- if type(slot) == "number" and slot >= 1 and slot <= Config.MAX_SAVES then
- if typeof(slotData.position) == "CFrame" then
- data.positions[tostring(slot)] = {
- name = slotData.name or ("Slot " .. slot),
- timestamp = slotData.timestamp or os.date("%Y-%m-%d %H:%M:%S"),
- position = CFrameUtils.encode(slotData.position)
- }
- end
- end
- end
- local success, encoded = pcall(function()
- return Services.HttpService:JSONEncode(data)
- end)
- if success and encoded then
- FileSystem:tryWrite(FileSystem.paths.save, encoded)
- FileSystem:tryWrite(FileSystem.paths.saveBak, encoded)
- end
- self:saveMainPosition()
- -- Processar fila se houver
- if saveQueue then
- task.delay(Config.SAVE_DEBOUNCE, function()
- self:saveToDisk()
- end)
- end
- end
- function Persistence:loadFromDisk()
- if not FileSystem.hasFS then return end
- local content, usedPath = FileSystem:findExistingContent("save")
- if not content then
- self:loadMainPosition()
- return
- end
- local success, data = pcall(function()
- return Services.HttpService:JSONDecode(content)
- end)
- if not success or type(data) ~= "table" then
- self:loadMainPosition()
- return
- end
- local positions = data.positions or data
- if data.gameId and data.gameId ~= game.PlaceId then
- self:loadMainPosition()
- return
- end
- for key, value in pairs(positions) do
- local slot = tonumber(key)
- if slot and slot >= 1 and slot <= Config.MAX_SAVES and type(value) == "table" then
- local cf = CFrameUtils.decode(value.position)
- if cf then
- State.savedPositions[slot] = {
- position = cf,
- name = value.name or ("Slot " .. slot),
- timestamp = value.timestamp or os.date("%Y-%m-%d %H:%M:%S")
- }
- end
- end
- end
- if usedPath ~= FileSystem.paths.save then
- self:saveToDisk()
- end
- self:loadMainPosition()
- end
- end
- --==============================================================================
- -- [7] SISTEMA DE TELEPORTE AVANÇADO (ANTI-BUG)
- --==============================================================================
- local Teleport = {}
- function Teleport:updateCharacterReferences()
- Player.character = Player.instance.Character or Player.instance.CharacterAdded:Wait()
- Player.humanoidRoot = Player.character:WaitForChild("HumanoidRootPart", 5)
- return Player.humanoidRoot ~= nil
- end
- function Teleport:execute(targetCFrame)
- if typeof(targetCFrame) ~= "CFrame" then return false end
- -- Atualiza referências se necessário
- if not Player.humanoidRoot or not Player.humanoidRoot.Parent then
- if not self:updateCharacterReferences() then
- return false
- end
- end
- local hrp = Player.humanoidRoot
- local humanoid = Player.character:FindFirstChildWhichIsA("Humanoid")
- -- Método de teleporte robusto em múltiplas etapas
- local function hardTeleport(cf)
- -- 1. Força sair de assentos
- if humanoid and humanoid.SeatPart then
- humanoid.Sit = false
- task.wait(0.1)
- end
- -- 2. Ancoragem e limpeza de velocidade
- local originalAnchor = hrp.Anchored
- hrp.Anchored = true
- if hrp:IsA("BasePart") then
- hrp.Velocity = Vector3.zero
- hrp.RotVelocity = Vector3.zero
- hrp.AssemblyLinearVelocity = Vector3.zero
- hrp.AssemblyAngularVelocity = Vector3.zero
- end
- -- 3. Aplicação do CFrame (dupla para segurança)
- hrp.CFrame = cf
- Services.RunService.Heartbeat:Wait()
- hrp.CFrame = cf
- -- 4. Aguarda sincronização física
- task.wait(0.05)
- -- 5. Restaura estado
- hrp.Anchored = originalAnchor
- return true
- end
- return pcall(hardTeleport, targetCFrame)
- end
- --==============================================================================
- -- [8] INTERFACE - CORES E TEMAS (REFINADO)
- --==============================================================================
- local Theme = {
- -- Cores principais
- Primary = Color3.fromRGB(120, 90, 255),
- PrimaryHover = Color3.fromRGB(140, 110, 255),
- -- Backgrounds
- Background = Color3.fromRGB(20, 18, 28),
- BackgroundSecondary = Color3.fromRGB(30, 28, 42),
- BackgroundTertiary = Color3.fromRGB(40, 38, 55),
- -- Gradientes
- GradientStart = Color3.fromRGB(25, 22, 38),
- GradientEnd = Color3.fromRGB(45, 40, 65),
- -- Textos
- TextPrimary = Color3.fromRGB(255, 255, 255),
- TextSecondary = Color3.fromRGB(200, 200, 255),
- TextDisabled = Color3.fromRGB(130, 130, 160),
- -- Estados
- Success = Color3.fromRGB(80, 200, 120),
- SuccessHover = Color3.fromRGB(100, 220, 140),
- Error = Color3.fromRGB(220, 80, 80),
- ErrorHover = Color3.fromRGB(240, 100, 100),
- Warning = Color3.fromRGB(255, 180, 80),
- -- Bordas
- Stroke = Color3.fromRGB(120, 90, 255),
- StrokeSecondary = Color3.fromRGB(80, 70, 130),
- -- Especiais
- Gold = Color3.fromRGB(255, 215, 0),
- SlotEmpty = Color3.fromRGB(100, 100, 120)
- }
- --==============================================================================
- -- [9] UTILITÁRIOS DE UI (OTIMIZADO)
- --==============================================================================
- local UIUtils = {}
- function UIUtils.createCorner(parent, radius)
- local corner = Instance.new("UICorner")
- corner.CornerRadius = UDim.new(0, radius or 8)
- corner.Parent = parent
- return corner
- end
- function UIUtils.createStroke(parent, color, thickness)
- local stroke = Instance.new("UIStroke")
- stroke.Color = color or Theme.Stroke
- stroke.Thickness = thickness or 1.5
- stroke.Parent = parent
- return stroke
- end
- function UIUtils.createGradient(parent, colorSequence, rotation)
- local gradient = Instance.new("UIGradient")
- gradient.Color = colorSequence or ColorSequence.new({
- ColorSequenceKeypoint.new(0, Theme.GradientStart),
- ColorSequenceKeypoint.new(1, Theme.GradientEnd)
- })
- gradient.Rotation = rotation or 90
- gradient.Parent = parent
- return gradient
- end
- function UIUtils.animateHover(button, hoverColor, normalColor)
- local normalBg = normalColor or button.BackgroundColor3
- local hoverBg = hoverColor or Theme.PrimaryHover
- local enterConn = button.MouseEnter:Connect(function()
- Services.TweenService:Create(button, Config.ANIMATION_INFO, {
- BackgroundColor3 = hoverBg
- }):Play()
- end)
- local leaveConn = button.MouseLeave:Connect(function()
- Services.TweenService:Create(button, Config.ANIMATION_INFO, {
- BackgroundColor3 = normalBg
- }):Play()
- end)
- return {enterConn, leaveConn}
- end
- function UIUtils.createButton(parent, config)
- local button = Instance.new("TextButton")
- button.Size = config.Size or UDim2.new(0, 100, 0, 40)
- button.Position = config.Position or UDim2.new(0, 0, 0, 0)
- button.BackgroundColor3 = config.BackgroundColor or Theme.Primary
- button.Text = config.Text or ""
- button.Font = config.Font or Enum.Font.GothamSemibold
- button.TextSize = config.TextSize or 14
- button.TextColor3 = config.TextColor or Theme.TextPrimary
- button.AutoButtonColor = false
- button.Parent = parent
- if config.Corner ~= false then
- UIUtils.createCorner(button, config.CornerRadius)
- end
- if config.Hover then
- UIUtils.animateHover(button, config.HoverColor, config.BackgroundColor)
- end
- return button
- end
- --==============================================================================
- -- [10] CRIAÇÃO DA INTERFACE PRINCIPAL
- --==============================================================================
- local UI = {}
- function UI:initialize()
- -- ScreenGui principal
- self.ScreenGui = Instance.new("ScreenGui")
- self.ScreenGui.Name = "SaveLocate_NickZ_v5"
- self.ScreenGui.ResetOnSpawn = false
- self.ScreenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
- -- Proteção contra múltiplas instâncias
- local existing = Services.CoreGui:FindFirstChild(self.ScreenGui.Name)
- if existing then
- existing:Destroy()
- end
- self.ScreenGui.Parent = Services.CoreGui
- self:createMainFrame()
- self:createTopBar()
- self:createOpenButton()
- self:createControlsFrame()
- self:createPageControls()
- self:createSlotsContainer()
- self:createMainPositionButton()
- self:createOverlays()
- end
- function UI:createMainFrame()
- self.MainFrame = Instance.new("Frame")
- self.MainFrame.Name = "MainFrame"
- self.MainFrame.Size = UDim2.new(0, 600, 0, 480)
- self.MainFrame.Position = UDim2.new(0.5, -300, 0.5, -240)
- self.MainFrame.BackgroundColor3 = Theme.Background
- self.MainFrame.BackgroundTransparency = 0.1
- self.MainFrame.BorderSizePixel = 0
- self.MainFrame.AnchorPoint = Vector2.new(0.5, 0.5)
- self.MainFrame.Active = true
- self.MainFrame.Draggable = true
- self.MainFrame.ClipsDescendants = true
- self.MainFrame.Parent = self.ScreenGui
- UIUtils.createCorner(self.MainFrame, 10)
- UIUtils.createStroke(self.MainFrame, Theme.Stroke, 2)
- UIUtils.createGradient(self.MainFrame)
- end
- function UI:createTopBar()
- local topBar = Instance.new("Frame")
- topBar.Name = "TopBar"
- topBar.Size = UDim2.new(1, 0, 0, 45)
- topBar.BackgroundTransparency = 1
- topBar.Parent = self.MainFrame
- -- Título
- local title = Instance.new("TextLabel")
- title.Name = "Title"
- title.Size = UDim2.new(1, -50, 1, 0)
- title.Position = UDim2.new(0, 15, 0, 0)
- title.BackgroundTransparency = 1
- title.Text = "✧ Save Locate (NickZ) v5.0"
- title.TextColor3 = Theme.TextPrimary
- title.Font = Enum.Font.GothamBold
- title.TextSize = 18
- title.TextXAlignment = Enum.TextXAlignment.Left
- title.Parent = topBar
- -- Botão fechar
- self.CloseBtn = UIUtils.createButton(topBar, {
- Size = UDim2.new(0, 35, 0, 35),
- Position = UDim2.new(1, -40, 0.5, -17.5),
- BackgroundColor = Color3.fromRGB(50, 45, 70),
- Text = "✕",
- Font = Enum.Font.GothamBold,
- TextSize = 18,
- TextColor = Theme.Error,
- Hover = true,
- HoverColor = Theme.Error
- })
- self.CloseBtn.MouseButton1Click:Connect(function()
- self.MainFrame.Visible = false
- end)
- end
- function UI:createOpenButton()
- self.OpenBtn = UIUtils.createButton(self.ScreenGui, {
- Size = UDim2.new(0, 50, 0, 50),
- Position = UDim2.new(0, 15, 0.5, -25),
- BackgroundColor = Theme.Background,
- Text = "✧",
- Font = Enum.Font.GothamBold,
- TextSize = 26,
- TextColor = Theme.Primary,
- Hover = true,
- CornerRadius = 10
- })
- UIUtils.createStroke(self.OpenBtn, Theme.Stroke, 2)
- self.OpenBtn.Visible = false
- self.OpenBtn.MouseButton1Click:Connect(function()
- self.MainFrame.Visible = true
- end)
- -- Sincronização de visibilidade
- self.MainFrame:GetPropertyChangedSignal("Visible"):Connect(function()
- self.OpenBtn.Visible = not self.MainFrame.Visible
- end)
- end
- function UI:createControlsFrame()
- local controls = Instance.new("Frame")
- controls.Name = "ControlsFrame"
- controls.Size = UDim2.new(1, -20, 0, 95)
- controls.Position = UDim2.new(0, 10, 0, 50)
- controls.BackgroundTransparency = 1
- controls.Parent = self.MainFrame
- -- Botão Salvar
- self.SaveButton = UIUtils.createButton(controls, {
- Size = UDim2.new(0.48, 0, 0, 40),
- Position = UDim2.new(0, 0, 0, 0),
- BackgroundColor = Theme.Success,
- Text = "💾 Salvar Posição",
- TextSize = 15,
- Hover = true,
- HoverColor = Theme.SuccessHover
- })
- -- Campo de nome
- self.NameField = Instance.new("TextBox")
- self.NameField.Name = "NameField"
- self.NameField.Size = UDim2.new(1, 0, 0, 42)
- self.NameField.Position = UDim2.new(0, 0, 0, 48)
- self.NameField.BackgroundColor3 = Theme.BackgroundSecondary
- self.NameField.TextColor3 = Theme.TextPrimary
- self.NameField.PlaceholderColor3 = Theme.TextDisabled
- self.NameField.Font = Enum.Font.Gotham
- self.NameField.TextSize = 14
- self.NameField.PlaceholderText = "✏️ Nome da Posição (Opcional)..."
- self.NameField.Text = ""
- self.NameField.ClearTextOnFocus = false
- self.NameField.Parent = controls
- UIUtils.createCorner(self.NameField, 8)
- UIUtils.createStroke(self.NameField, Theme.StrokeSecondary, 1)
- -- Label de slot
- self.SaveSlotLabel = Instance.new("TextLabel")
- self.SaveSlotLabel.Name = "SlotLabel"
- self.SaveSlotLabel.Size = UDim2.new(0.48, 0, 0, 20)
- self.SaveSlotLabel.Position = UDim2.new(0.52, 0, 0, 5)
- self.SaveSlotLabel.BackgroundTransparency = 1
- self.SaveSlotLabel.Text = "📍 Slot: " .. State.currentSaveSlot
- self.SaveSlotLabel.TextColor3 = Theme.TextSecondary
- self.SaveSlotLabel.Font = Enum.Font.GothamSemibold
- self.SaveSlotLabel.TextSize = 13
- self.SaveSlotLabel.TextXAlignment = Enum.TextXAlignment.Left
- self.SaveSlotLabel.Parent = controls
- -- Botões de navegação de slot
- self.SlotPrev = UIUtils.createButton(controls, {
- Size = UDim2.new(0, 38, 0, 32),
- Position = UDim2.new(0.52, 0, 0, 25),
- BackgroundColor = Theme.BackgroundTertiary,
- Text = "◄",
- TextSize = 16,
- Hover = true
- })
- self.SlotNext = UIUtils.createButton(controls, {
- Size = UDim2.new(0, 38, 0, 32),
- Position = UDim2.new(0.52, 45, 0, 25),
- BackgroundColor = Theme.BackgroundTertiary,
- Text = "►",
- TextSize = 16,
- Hover = true
- })
- end
- function UI:createPageControls()
- local pageFrame = Instance.new("Frame")
- pageFrame.Name = "PageControls"
- pageFrame.Size = UDim2.new(1, -20, 0, 35)
- pageFrame.Position = UDim2.new(0, 10, 0, 150)
- pageFrame.BackgroundTransparency = 1
- pageFrame.Parent = self.MainFrame
- -- Label da página
- self.PageLabel = Instance.new("TextLabel")
- self.PageLabel.Name = "PageLabel"
- self.PageLabel.Size = UDim2.new(0, 140, 1, 0)
- self.PageLabel.Position = UDim2.new(0.5, -70, 0, 0)
- self.PageLabel.BackgroundTransparency = 1
- self.PageLabel.Text = string.format("📄 Página %d/%d", State.currentPage, Config.MAX_PAGES)
- self.PageLabel.TextColor3 = Theme.TextPrimary
- self.PageLabel.Font = Enum.Font.GothamBold
- self.PageLabel.TextSize = 15
- self.PageLabel.Parent = pageFrame
- -- Botão página anterior
- self.PagePrev = UIUtils.createButton(pageFrame, {
- Size = UDim2.new(0, 95, 0, 28),
- Position = UDim2.new(0, 0, 0, 3),
- BackgroundColor = Theme.BackgroundTertiary,
- Text = "◄ Anterior",
- TextSize = 12,
- Hover = true
- })
- -- Botão próxima página
- self.PageNext = UIUtils.createButton(pageFrame, {
- Size = UDim2.new(0, 95, 0, 28),
- Position = UDim2.new(1, -95, 0, 3),
- BackgroundColor = Theme.BackgroundTertiary,
- Text = "Próxima ►",
- TextSize = 12,
- Hover = true
- })
- end
- function UI:createSlotsContainer()
- self.SlotsContainer = Instance.new("Frame")
- self.SlotsContainer.Name = "SlotsContainer"
- self.SlotsContainer.Size = UDim2.new(1, -20, 1, -200)
- self.SlotsContainer.Position = UDim2.new(0, 10, 0, 190)
- self.SlotsContainer.BackgroundTransparency = 1
- self.SlotsContainer.ClipsDescendants = false
- self.SlotsContainer.Parent = self.MainFrame
- local gridLayout = Instance.new("UIGridLayout")
- gridLayout.CellPadding = UDim2.new(0, 10, 0, 10)
- gridLayout.CellSize = UDim2.new(0.5, -5, 0, 50)
- gridLayout.FillDirection = Enum.FillDirection.Vertical
- gridLayout.SortOrder = Enum.SortOrder.LayoutOrder
- gridLayout.StartCorner = Enum.StartCorner.TopLeft
- gridLayout.Parent = self.SlotsContainer
- end
- function UI:createMainPositionButton()
- local container = Instance.new("Frame")
- container.Name = "MainPosContainer"
- container.Size = UDim2.new(0, 55, 0, 55)
- container.Position = UDim2.new(1, -70, 0, 15)
- container.BackgroundTransparency = 1
- container.Visible = false
- container.Active = true
- container.Draggable = true
- container.Parent = self.ScreenGui
- self.MainPosBtn = Instance.new("TextButton")
- self.MainPosBtn.Name = "MainPosBtn"
- self.MainPosBtn.Size = UDim2.new(1, 0, 1, 0)
- self.MainPosBtn.BackgroundColor3 = Theme.Background
- self.MainPosBtn.BackgroundTransparency = 0.1
- self.MainPosBtn.Text = "🚀"
- self.MainPosBtn.Font = Enum.Font.GothamBold
- self.MainPosBtn.TextSize = 28
- self.MainPosBtn.TextColor3 = Theme.TextPrimary
- self.MainPosBtn.Parent = container
- UIUtils.createCorner(self.MainPosBtn, 28)
- local mainStroke = UIUtils.createStroke(self.MainPosBtn, Theme.Stroke, 2.5)
- -- Tooltip
- self.MainTooltip = Instance.new("TextLabel")
- self.MainTooltip.Name = "Tooltip"
- self.MainTooltip.Size = UDim2.new(0, 160, 0, 32)
- self.MainTooltip.Position = UDim2.new(-3.1, 0, 0.5, -16)
- self.MainTooltip.BackgroundColor3 = Theme.Background
- self.MainTooltip.BackgroundTransparency = 0.05
- self.MainTooltip.Text = ""
- self.MainTooltip.TextColor3 = Theme.TextPrimary
- self.MainTooltip.Font = Enum.Font.GothamSemibold
- self.MainTooltip.TextSize = 12
- self.MainTooltip.Visible = false
- self.MainTooltip.ZIndex = 10
- self.MainTooltip.Parent = self.MainPosBtn
- UIUtils.createCorner(self.MainTooltip, 8)
- UIUtils.createStroke(self.MainTooltip, Theme.Stroke, 1.5)
- -- Botão remover
- self.MainRemoveBtn = UIUtils.createButton(self.MainPosBtn, {
- Size = UDim2.new(0, 22, 0, 22),
- Position = UDim2.new(1, -6, 0, -6),
- BackgroundColor = Theme.Error,
- Text = "×",
- TextSize = 16,
- Font = Enum.Font.GothamBold,
- Hover = true,
- HoverColor = Theme.ErrorHover,
- CornerRadius = 11
- })
- self.MainRemoveBtn.Visible = false
- self.MainRemoveBtn.ZIndex = 5
- -- Animações hover
- self.MainPosBtn.MouseEnter:Connect(function()
- if State.mainPosition then
- self.MainTooltip.Text = State.mainPosition.name or "Principal"
- self.MainTooltip.Visible = true
- self.MainRemoveBtn.Visible = true
- end
- Services.TweenService:Create(self.MainPosBtn, Config.ANIMATION_INFO, {
- Size = UDim2.new(1.15, 0, 1.15, 0)
- }):Play()
- Services.TweenService:Create(mainStroke, Config.ANIMATION_INFO, {
- Thickness = 3.5
- }):Play()
- end)
- self.MainPosBtn.MouseLeave:Connect(function()
- self.MainTooltip.Visible = false
- self.MainRemoveBtn.Visible = false
- Services.TweenService:Create(self.MainPosBtn, Config.ANIMATION_INFO, {
- Size = UDim2.new(1, 0, 1, 0)
- }):Play()
- Services.TweenService:Create(mainStroke, Config.ANIMATION_INFO, {
- Thickness = 2.5
- }):Play()
- end)
- self.MainPosContainer = container
- end
- function UI:createOverlays()
- self:createConfirmationOverlay()
- self:createRenameOverlay()
- self:createMoveOverlay()
- end
- function UI:createConfirmationOverlay()
- local overlay = Instance.new("Frame")
- overlay.Name = "ConfirmationOverlay"
- overlay.Size = UDim2.new(1, 0, 1, 0)
- overlay.BackgroundColor3 = Color3.new(0, 0, 0)
- overlay.BackgroundTransparency = 0.5
- overlay.Visible = false
- overlay.ZIndex = 100
- overlay.Parent = self.MainFrame
- local frame = Instance.new("Frame")
- frame.Size = UDim2.new(0, 320, 0, 130)
- frame.Position = UDim2.new(0.5, -160, 0.5, -65)
- frame.BackgroundColor3 = Theme.BackgroundSecondary
- frame.BackgroundTransparency = 0.05
- frame.ZIndex = 101
- frame.Parent = overlay
- UIUtils.createCorner(frame, 10)
- UIUtils.createStroke(frame, Theme.Stroke, 2)
- self.ConfirmLabel = Instance.new("TextLabel")
- self.ConfirmLabel.Size = UDim2.new(1, -20, 0, 65)
- self.ConfirmLabel.Position = UDim2.new(0, 10, 0, 5)
- self.ConfirmLabel.BackgroundTransparency = 1
- self.ConfirmLabel.TextColor3 = Theme.TextPrimary
- self.ConfirmLabel.Font = Enum.Font.GothamSemibold
- self.ConfirmLabel.TextSize = 15
- self.ConfirmLabel.TextWrapped = true
- self.ConfirmLabel.ZIndex = 102
- self.ConfirmLabel.Parent = frame
- self.ConfirmYes = UIUtils.createButton(frame, {
- Size = UDim2.new(0.48, 0, 0, 42),
- Position = UDim2.new(0, 10, 1, -52),
- BackgroundColor = Theme.Error,
- Text = "✓ Sim",
- TextSize = 14,
- Hover = true,
- HoverColor = Theme.ErrorHover
- })
- self.ConfirmYes.ZIndex = 102
- self.ConfirmNo = UIUtils.createButton(frame, {
- Size = UDim2.new(0.48, 0, 0, 42),
- Position = UDim2.new(0.52, 0, 1, -52),
- BackgroundColor = Theme.BackgroundTertiary,
- Text = "✕ Não",
- TextSize = 14,
- Hover = true
- })
- self.ConfirmNo.ZIndex = 102
- self.ConfirmationOverlay = overlay
- end
- function UI:createRenameOverlay()
- local overlay = Instance.new("Frame")
- overlay.Name = "RenameOverlay"
- overlay.Size = UDim2.new(1, 0, 1, 0)
- overlay.BackgroundColor3 = Color3.new(0, 0, 0)
- overlay.BackgroundTransparency = 0.5
- overlay.Visible = false
- overlay.ZIndex = 110
- overlay.Parent = self.MainFrame
- local frame = Instance.new("Frame")
- frame.Size = UDim2.new(0, 360, 0, 170)
- frame.Position = UDim2.new(0.5, -180, 0.5, -85)
- frame.BackgroundColor3 = Theme.BackgroundSecondary
- frame.BackgroundTransparency = 0.05
- frame.ZIndex = 111
- frame.Parent = overlay
- UIUtils.createCorner(frame, 10)
- UIUtils.createStroke(frame, Theme.Stroke, 2)
- self.RenameTitle = Instance.new("TextLabel")
- self.RenameTitle.Size = UDim2.new(1, -20, 0, 40)
- self.RenameTitle.Position = UDim2.new(0, 10, 0, 5)
- self.RenameTitle.BackgroundTransparency = 1
- self.RenameTitle.Text = "✏️ Renomear Posição"
- self.RenameTitle.TextColor3 = Theme.TextPrimary
- self.RenameTitle.Font = Enum.Font.GothamBold
- self.RenameTitle.TextSize = 16
- self.RenameTitle.ZIndex = 112
- self.RenameTitle.Parent = frame
- self.RenameField = Instance.new("TextBox")
- self.RenameField.Size = UDim2.new(1, -20, 0, 45)
- self.RenameField.Position = UDim2.new(0, 10, 0, 55)
- self.RenameField.BackgroundColor3 = Theme.Background
- self.RenameField.TextColor3 = Theme.TextPrimary
- self.RenameField.Font = Enum.Font.Gotham
- self.RenameField.TextSize = 14
- self.RenameField.PlaceholderText = "Digite o novo nome..."
- self.RenameField.ZIndex = 112
- self.RenameField.Parent = frame
- UIUtils.createCorner(self.RenameField, 8)
- UIUtils.createStroke(self.RenameField, Theme.StrokeSecondary, 1)
- self.RenameConfirm = UIUtils.createButton(frame, {
- Size = UDim2.new(0.48, 0, 0, 42),
- Position = UDim2.new(0, 10, 1, -52),
- BackgroundColor = Theme.Primary,
- Text = "✓ Confirmar",
- TextSize = 14,
- Hover = true,
- HoverColor = Theme.PrimaryHover
- })
- self.RenameConfirm.ZIndex = 112
- self.RenameCancel = UIUtils.createButton(frame, {
- Size = UDim2.new(0.48, 0, 0, 42),
- Position = UDim2.new(0.52, 0, 1, -52),
- BackgroundColor = Theme.BackgroundTertiary,
- Text = "✕ Cancelar",
- TextSize = 14,
- Hover = true
- })
- self.RenameCancel.ZIndex = 112
- self.RenameOverlay = overlay
- end
- function UI:createMoveOverlay()
- local overlay = Instance.new("Frame")
- overlay.Name = "MoveOverlay"
- overlay.Size = UDim2.new(1, 0, 1, 0)
- overlay.BackgroundColor3 = Color3.new(0, 0, 0)
- overlay.BackgroundTransparency = 0.5
- overlay.Visible = false
- overlay.ZIndex = 120
- overlay.Parent = self.MainFrame
- local frame = Instance.new("Frame")
- frame.Size = UDim2.new(0, 320, 0, 190)
- frame.Position = UDim2.new(0.5, -160, 0.5, -95)
- frame.BackgroundColor3 = Theme.BackgroundSecondary
- frame.BackgroundTransparency = 0.05
- frame.ZIndex = 121
- frame.Parent = overlay
- UIUtils.createCorner(frame, 10)
- UIUtils.createStroke(frame, Theme.Stroke, 2)
- self.MoveTitle = Instance.new("TextLabel")
- self.MoveTitle.Size = UDim2.new(1, -20, 0, 35)
- self.MoveTitle.Position = UDim2.new(0, 10, 0, 10)
- self.MoveTitle.BackgroundTransparency = 1
- self.MoveTitle.Text = "↔️ Mover Posição"
- self.MoveTitle.TextColor3 = Theme.TextPrimary
- self.MoveTitle.Font = Enum.Font.GothamBold
- self.MoveTitle.TextSize = 16
- self.MoveTitle.ZIndex = 122
- self.MoveTitle.Parent = frame
- local moveInfo = Instance.new("TextLabel")
- moveInfo.Size = UDim2.new(1, -20, 0, 22)
- moveInfo.Position = UDim2.new(0, 10, 0, 50)
- moveInfo.BackgroundTransparency = 1
- moveInfo.Text = "Digite o slot de destino (1-50):"
- moveInfo.TextColor3 = Theme.TextSecondary
- moveInfo.Font = Enum.Font.Gotham
- moveInfo.TextSize = 13
- moveInfo.ZIndex = 122
- moveInfo.Parent = frame
- self.MoveInput = Instance.new("TextBox")
- self.MoveInput.Size = UDim2.new(0, 90, 0, 45)
- self.MoveInput.Position = UDim2.new(0.5, -45, 0, 80)
- self.MoveInput.BackgroundColor3 = Theme.Background
- self.MoveInput.TextColor3 = Theme.TextPrimary
- self.MoveInput.Font = Enum.Font.GothamBold
- self.MoveInput.TextSize = 22
- self.MoveInput.PlaceholderText = "1-50"
- self.MoveInput.Text = ""
- self.MoveInput.ZIndex = 122
- self.MoveInput.Parent = frame
- UIUtils.createCorner(self.MoveInput, 8)
- UIUtils.createStroke(self.MoveInput, Theme.StrokeSecondary, 1)
- self.MoveConfirm = UIUtils.createButton(frame, {
- Size = UDim2.new(0.48, 0, 0, 40),
- Position = UDim2.new(0, 10, 1, -50),
- BackgroundColor = Theme.Primary,
- Text = "✓ Confirmar",
- TextSize = 14,
- Hover = true,
- HoverColor = Theme.PrimaryHover
- })
- self.MoveConfirm.ZIndex = 122
- self.MoveCancel = UIUtils.createButton(frame, {
- Size = UDim2.new(0.48, 0, 0, 40),
- Position = UDim2.new(0.52, 0, 1, -50),
- BackgroundColor = Theme.BackgroundTertiary,
- Text = "✕ Cancelar",
- TextSize = 14,
- Hover = true
- })
- self.MoveCancel.ZIndex = 122
- self.MoveOverlay = overlay
- end
- --==============================================================================
- -- [11] LÓGICA DE NEGÓCIO
- --==============================================================================
- local Logic = {}
- do
- Logic.onConfirmCallback = nil
- Logic.slotToRename = nil
- Logic.slotToMove = nil
- function Logic:updateMainPositionUI()
- UI.MainPosContainer.Visible = (State.mainPosition ~= nil)
- end
- function Logic:setMainPosition(slot)
- if State.savedPositions[slot] then
- State.mainPosition = {
- position = State.savedPositions[slot].position,
- name = State.savedPositions[slot].name,
- timestamp = State.savedPositions[slot].timestamp
- }
- Persistence:saveMainPosition()
- self:updateMainPositionUI()
- end
- end
- function Logic:removeMainPosition()
- State.mainPosition = nil
- if FileSystem.capabilities.delete then
- pcall(delfile, FileSystem.paths.main)
- pcall(delfile, FileSystem.paths.mainBak)
- end
- self:updateMainPositionUI()
- end
- function Logic:swapPositions(fromSlot, toSlot)
- if fromSlot == toSlot then return end
- if State.savedPositions[toSlot] then
- -- Swap
- local temp = State.savedPositions[fromSlot]
- State.savedPositions[fromSlot] = State.savedPositions[toSlot]
- State.savedPositions[toSlot] = temp
- else
- -- Move simples
- State.savedPositions[toSlot] = State.savedPositions[fromSlot]
- State.savedPositions[fromSlot] = nil
- end
- Persistence:saveToDisk()
- end
- function Logic:findNextEmptySlot()
- for i = 1, Config.MAX_SAVES do
- if not State.savedPositions[i] then
- return i
- end
- end
- return (State.currentSaveSlot % Config.MAX_SAVES) + 1
- end
- function Logic:selectNextSlot()
- State.currentSaveSlot = self:findNextEmptySlot()
- UI.SaveSlotLabel.Text = "📍 Slot: " .. State.currentSaveSlot
- end
- function Logic:changePage(direction)
- local newPage = State.currentPage + direction
- if newPage < 1 then newPage = Config.MAX_PAGES end
- if newPage > Config.MAX_PAGES then newPage = 1 end
- State.currentPage = newPage
- UI.PageLabel.Text = string.format("📄 Página %d/%d", State.currentPage, Config.MAX_PAGES)
- self:updateSlotsList()
- end
- function Logic:changeSlot(direction)
- State.currentSaveSlot = ((State.currentSaveSlot - 1 + direction + Config.MAX_SAVES) % Config.MAX_SAVES) + 1
- UI.SaveSlotLabel.Text = "📍 Slot: " .. State.currentSaveSlot
- end
- function Logic:performSave()
- if not Player.humanoidRoot then return false end
- local name = UI.NameField.Text
- State.savedPositions[State.currentSaveSlot] = {
- position = Player.humanoidRoot.CFrame,
- name = (#name > 0) and name or ("Slot " .. State.currentSaveSlot),
- timestamp = os.date("%Y-%m-%d %H:%M:%S")
- }
- Persistence:saveToDisk()
- -- Navega para a página do slot salvo
- local pageOfSlot = math.ceil(State.currentSaveSlot / Config.SLOTS_PER_PAGE)
- if pageOfSlot ~= State.currentPage then
- State.currentPage = pageOfSlot
- UI.PageLabel.Text = string.format("📄 Página %d/%d", State.currentPage, Config.MAX_PAGES)
- end
- self:updateSlotsList()
- return true
- end
- function Logic:animateSaveButton(success)
- local button = UI.SaveButton
- local originalColor = Theme.Success
- local originalText = "💾 Salvar Posição"
- if success then
- button.Text = "✅ Salvo!"
- button.BackgroundColor3 = Theme.SuccessHover
- else
- button.Text = "❌ Erro!"
- button.BackgroundColor3 = Theme.Error
- end
- task.wait(1.5)
- button.Text = originalText
- Services.TweenService:Create(button, Config.ANIMATION_INFO, {
- BackgroundColor3 = originalColor
- }):Play()
- end
- function Logic:updateSlotsList()
- -- Limpa slots existentes
- for _, child in ipairs(UI.SlotsContainer:GetChildren()) do
- if child:IsA("Frame") then
- child:Destroy()
- end
- end
- -- Calcula range da página atual
- local startSlot = (State.currentPage - 1) * Config.SLOTS_PER_PAGE + 1
- local endSlot = State.currentPage * Config.SLOTS_PER_PAGE
- for slot = startSlot, endSlot do
- self:createSlotEntry(slot)
- end
- end
- function Logic:createSlotEntry(slot)
- local entryFrame = Instance.new("Frame")
- entryFrame.Name = "Slot_" .. slot
- entryFrame.LayoutOrder = slot
- entryFrame.BackgroundColor3 = Theme.BackgroundTertiary
- entryFrame.BackgroundTransparency = 0.3
- entryFrame.BorderSizePixel = 0
- entryFrame.Parent = UI.SlotsContainer
- UIUtils.createCorner(entryFrame, 8)
- local slotData = State.savedPositions[slot]
- if slotData then
- self:createFilledSlot(entryFrame, slot, slotData)
- else
- self:createEmptySlot(entryFrame, slot)
- end
- end
- function Logic:createFilledSlot(parent, slot, data)
- -- Container de ações (estrela + menu)
- local actionsFrame = Instance.new("Frame")
- actionsFrame.Name = "ActionsFrame"
- actionsFrame.Size = UDim2.new(0, 32, 1, 0)
- actionsFrame.Position = UDim2.new(0, 5, 0, 0)
- actionsFrame.BackgroundTransparency = 1
- actionsFrame.Parent = parent
- -- Botão de posição principal
- local mainBtn = Instance.new("TextButton")
- mainBtn.Name = "MainBtn"
- local isMain = (State.mainPosition and State.mainPosition.position == data.position)
- mainBtn.Text = isMain and "★" or "☆"
- mainBtn.Size = UDim2.new(1, 0, 0, 24)
- mainBtn.Position = UDim2.new(0, 0, 0, 2)
- mainBtn.BackgroundTransparency = 1
- mainBtn.Font = Enum.Font.GothamBold
- mainBtn.TextSize = 15
- mainBtn.TextColor3 = Theme.Gold
- mainBtn.Parent = actionsFrame
- mainBtn.MouseEnter:Connect(function()
- mainBtn.TextSize = 17
- end)
- mainBtn.MouseLeave:Connect(function()
- mainBtn.TextSize = 15
- end)
- -- Botão de menu
- local menuBtn = Instance.new("TextButton")
- menuBtn.Name = "MenuBtn"
- menuBtn.Text = "⋮"
- menuBtn.Size = UDim2.new(1, 0, 0, 24)
- menuBtn.Position = UDim2.new(0, 0, 0, 26)
- menuBtn.BackgroundTransparency = 1
- menuBtn.Font = Enum.Font.GothamBold
- menuBtn.TextSize = 19
- menuBtn.TextColor3 = Theme.TextDisabled
- menuBtn.Parent = actionsFrame
- menuBtn.MouseEnter:Connect(function()
- menuBtn.TextColor3 = Theme.TextPrimary
- menuBtn.TextSize = 21
- end)
- menuBtn.MouseLeave:Connect(function()
- menuBtn.TextColor3 = Theme.TextDisabled
- menuBtn.TextSize = 19
- end)
- -- Nome da posição
- local nameLabel = Instance.new("TextLabel")
- nameLabel.Name = "NameLabel"
- nameLabel.Size = UDim2.new(1, -95, 0, 26)
- nameLabel.Position = UDim2.new(0, 42, 0, 4)
- nameLabel.BackgroundTransparency = 1
- nameLabel.Text = data.name
- nameLabel.TextColor3 = Theme.TextSecondary
- nameLabel.Font = Enum.Font.GothamSemibold
- nameLabel.TextSize = 14
- nameLabel.TextXAlignment = Enum.TextXAlignment.Left
- nameLabel.TextTruncate = Enum.TextTruncate.AtEnd
- nameLabel.Parent = parent
- -- Timestamp
- local timeLabel = Instance.new("TextLabel")
- timeLabel.Name = "TimeLabel"
- timeLabel.Size = UDim2.new(1, -95, 0, 16)
- timeLabel.Position = UDim2.new(0, 42, 0, 30)
- timeLabel.BackgroundTransparency = 1
- timeLabel.Text = "🕒 " .. data.timestamp
- timeLabel.TextColor3 = Theme.TextDisabled
- timeLabel.Font = Enum.Font.Gotham
- timeLabel.TextSize = 10
- timeLabel.TextXAlignment = Enum.TextXAlignment.Left
- timeLabel.Parent = parent
- -- Botão de teleporte
- local tpBtn = UIUtils.createButton(parent, {
- Size = UDim2.new(0, 40, 0, 38),
- Position = UDim2.new(1, -45, 0.5, -19),
- BackgroundColor = Theme.Primary,
- Text = "TP",
- TextSize = 14,
- Hover = true,
- HoverColor = Theme.PrimaryHover
- })
- -- Menu de opções (criado dinamicamente)
- local optionsFrame = self:createOptionsMenu(slot, data)
- local menuOpen = false
- local closeConnection = nil
- menuBtn.MouseButton1Click:Connect(function()
- menuOpen = not menuOpen
- if menuOpen then
- local btnPos = menuBtn.AbsolutePosition
- optionsFrame.Position = UDim2.new(0, btnPos.X + 40, 0, btnPos.Y - 10)
- optionsFrame.Visible = true
- -- Fecha outros menus
- for _, child in ipairs(UI.ScreenGui:GetChildren()) do
- if child.Name:match("^OptionsMenu_") and child ~= optionsFrame then
- child.Visible = false
- end
- end
- -- Sistema de fechar ao clicar fora
- if closeConnection then closeConnection:Disconnect() end
- closeConnection = Services.UserInputService.InputBegan:Connect(function(input)
- if input.UserInputType == Enum.UserInputType.MouseButton1 then
- task.wait(0.05)
- local mousePos = Services.UserInputService:GetMouseLocation()
- local framePos = optionsFrame.AbsolutePosition
- local frameSize = optionsFrame.AbsoluteSize
- if mousePos.X < framePos.X or mousePos.X > framePos.X + frameSize.X or
- mousePos.Y < framePos.Y or mousePos.Y > framePos.Y + frameSize.Y then
- optionsFrame.Visible = false
- menuOpen = false
- if closeConnection then
- closeConnection:Disconnect()
- closeConnection = nil
- end
- end
- end
- end)
- else
- optionsFrame.Visible = false
- if closeConnection then
- closeConnection:Disconnect()
- closeConnection = nil
- end
- end
- end)
- -- Lógica do botão principal
- mainBtn.MouseButton1Click:Connect(function()
- local isAlreadyMain = (State.mainPosition and State.mainPosition.position == data.position)
- if isAlreadyMain then
- UI.ConfirmLabel.Text = "Remover posição principal?"
- UI.ConfirmYes.Text = "✓ Remover"
- Logic.onConfirmCallback = function()
- Logic:removeMainPosition()
- Logic:updateSlotsList()
- end
- else
- UI.ConfirmLabel.Text = State.mainPosition and "Substituir posição principal?" or "Definir como principal?"
- UI.ConfirmYes.Text = "✓ Sim"
- Logic.onConfirmCallback = function()
- Logic:setMainPosition(slot)
- Logic:updateSlotsList()
- end
- end
- UI.ConfirmationOverlay.Visible = true
- end)
- -- Lógica do botão de teleporte
- tpBtn.MouseButton1Click:Connect(function()
- local success = Teleport:execute(data.position)
- if success then
- tpBtn.Text = "✅"
- tpBtn.BackgroundColor3 = Theme.Success
- task.wait(1)
- if tpBtn.Parent then
- tpBtn.Text = "TP"
- Services.TweenService:Create(tpBtn, Config.ANIMATION_INFO, {
- BackgroundColor3 = Theme.Primary
- }):Play()
- end
- else
- tpBtn.Text = "❌"
- tpBtn.BackgroundColor3 = Theme.Error
- task.wait(1)
- if tpBtn.Parent then
- tpBtn.Text = "TP"
- Services.TweenService:Create(tpBtn, Config.ANIMATION_INFO, {
- BackgroundColor3 = Theme.Primary
- }):Play()
- end
- end
- end)
- end
- function Logic:createEmptySlot(parent, slot)
- local label = Instance.new("TextLabel")
- label.Name = "EmptyLabel"
- label.Size = UDim2.new(1, -20, 1, 0)
- label.Position = UDim2.new(0, 10, 0, 0)
- label.BackgroundTransparency = 1
- label.Text = string.format("Slot %d: Vazio", slot)
- label.TextColor3 = Theme.SlotEmpty
- label.Font = Enum.Font.Gotham
- label.TextSize = 14
- label.TextXAlignment = Enum.TextXAlignment.Left
- label.Parent = parent
- end
- function Logic:createOptionsMenu(slot, data)
- local optionsFrame = Instance.new("Frame")
- optionsFrame.Name = "OptionsMenu_" .. slot
- optionsFrame.Size = UDim2.new(0, 125, 0, 115)
- optionsFrame.BackgroundColor3 = Theme.BackgroundSecondary
- optionsFrame.BackgroundTransparency = 0.02
- optionsFrame.BorderSizePixel = 0
- optionsFrame.Visible = false
- optionsFrame.ZIndex = 200
- optionsFrame.Parent = UI.ScreenGui
- UIUtils.createCorner(optionsFrame, 10)
- UIUtils.createStroke(optionsFrame, Theme.Stroke, 2)
- local buttonsContainer = Instance.new("Frame")
- buttonsContainer.Name = "Container"
- buttonsContainer.Size = UDim2.new(1, -12, 1, -28)
- buttonsContainer.Position = UDim2.new(0, 6, 0, 24)
- buttonsContainer.BackgroundTransparency = 1
- buttonsContainer.ZIndex = 201
- buttonsContainer.Parent = optionsFrame
- -- Helper para botões de menu
- local function createMenuButton(pos, text, icon, textColor)
- local btn = Instance.new("TextButton")
- btn.Size = UDim2.new(1, 0, 0, 26)
- btn.Position = UDim2.new(0, 0, 0, pos)
- btn.BackgroundColor3 = Theme.BackgroundTertiary
- btn.BackgroundTransparency = 0.4
- btn.Text = icon .. " " .. text
- btn.TextColor3 = textColor or Theme.TextSecondary
- btn.Font = Enum.Font.GothamSemibold
- btn.TextSize = 12
- btn.ZIndex = 202
- btn.Parent = buttonsContainer
- UIUtils.createCorner(btn, 6)
- btn.MouseEnter:Connect(function()
- Services.TweenService:Create(btn, Config.ANIMATION_INFO, {
- BackgroundTransparency = 0,
- BackgroundColor3 = Color3.fromRGB(65, 60, 85)
- }):Play()
- end)
- btn.MouseLeave:Connect(function()
- Services.TweenService:Create(btn, Config.ANIMATION_INFO, {
- BackgroundTransparency = 0.4,
- BackgroundColor3 = Theme.BackgroundTertiary
- }):Play()
- end)
- return btn
- end
- local renameBtn = createMenuButton(0, "Renomear", "✏️")
- local moveBtn = createMenuButton(29, "Mover", "↔️")
- local deleteBtn = createMenuButton(58, "Excluir", "🗑️", Theme.Error)
- -- Eventos dos botões
- renameBtn.MouseButton1Click:Connect(function()
- optionsFrame.Visible = false
- Logic.slotToRename = slot
- UI.RenameTitle.Text = string.format("✏️ Renomear Slot %d", slot)
- UI.RenameField.Text = data.name
- UI.RenameOverlay.Visible = true
- end)
- moveBtn.MouseButton1Click:Connect(function()
- optionsFrame.Visible = false
- Logic.slotToMove = slot
- UI.MoveTitle.Text = string.format("↔️ Mover: %s", data.name)
- UI.MoveInput.Text = ""
- UI.MoveOverlay.Visible = true
- end)
- deleteBtn.MouseButton1Click:Connect(function()
- optionsFrame.Visible = false
- UI.ConfirmLabel.Text = string.format("Excluir o Slot %d?\n\"%s\"", slot, data.name)
- UI.ConfirmYes.Text = "✓ Excluir"
- Logic.onConfirmCallback = function()
- State.savedPositions[slot] = nil
- Persistence:saveToDisk()
- Logic:updateSlotsList()
- Logic:selectNextSlot()
- end
- UI.ConfirmationOverlay.Visible = true
- end)
- return optionsFrame
- end
- end
- --==============================================================================
- -- [12] CONECTAR EVENTOS DA UI
- --==============================================================================
- local function connectUIEvents()
- -- Botão de salvar
- UI.SaveButton.MouseButton1Click:Connect(function()
- if State.savedPositions[State.currentSaveSlot] then
- UI.ConfirmLabel.Text = string.format("Substituir posição do Slot %d?", State.currentSaveSlot)
- UI.ConfirmYes.Text = "✓ Substituir"
- Logic.onConfirmCallback = function()
- local success = Logic:performSave()
- if success then Logic:selectNextSlot() end
- Logic:animateSaveButton(success)
- end
- UI.ConfirmationOverlay.Visible = true
- else
- local success = Logic:performSave()
- if success then Logic:selectNextSlot() end
- Logic:animateSaveButton(success)
- end
- end)
- -- Navegação de slots
- UI.SlotPrev.MouseButton1Click:Connect(function()
- Logic:changeSlot(-1)
- end)
- UI.SlotNext.MouseButton1Click:Connect(function()
- Logic:changeSlot(1)
- end)
- -- Navegação de páginas
- UI.PagePrev.MouseButton1Click:Connect(function()
- Logic:changePage(-1)
- end)
- UI.PageNext.MouseButton1Click:Connect(function()
- Logic:changePage(1)
- end)
- -- Confirmação overlay
- UI.ConfirmYes.MouseButton1Click:Connect(function()
- if Logic.onConfirmCallback then
- Logic.onConfirmCallback()
- end
- Logic.onConfirmCallback = nil
- UI.ConfirmationOverlay.Visible = false
- end)
- UI.ConfirmNo.MouseButton1Click:Connect(function()
- Logic.onConfirmCallback = nil
- UI.ConfirmationOverlay.Visible = false
- end)
- -- Rename overlay
- UI.RenameConfirm.MouseButton1Click:Connect(function()
- if Logic.slotToRename and #UI.RenameField.Text > 0 then
- local newName = UI.RenameField.Text
- local slotData = State.savedPositions[Logic.slotToRename]
- if slotData then
- slotData.name = newName
- -- Atualiza nome principal se for o mesmo
- if State.mainPosition and State.mainPosition.position == slotData.position then
- State.mainPosition.name = newName
- Persistence:saveMainPosition()
- end
- Persistence:saveToDisk()
- Logic:updateSlotsList()
- Logic:updateMainPositionUI()
- end
- UI.RenameOverlay.Visible = false
- Logic.slotToRename = nil
- end
- end)
- UI.RenameCancel.MouseButton1Click:Connect(function()
- UI.RenameOverlay.Visible = false
- Logic.slotToRename = nil
- end)
- -- Move overlay
- UI.MoveConfirm.MouseButton1Click:Connect(function()
- local targetSlot = tonumber(UI.MoveInput.Text)
- if Logic.slotToMove and targetSlot and targetSlot >= 1 and targetSlot <= Config.MAX_SAVES then
- Logic:swapPositions(Logic.slotToMove, targetSlot)
- Logic:updateSlotsList()
- UI.MoveOverlay.Visible = false
- Logic.slotToMove = nil
- else
- UI.MoveInput.Text = ""
- UI.MoveInput.PlaceholderText = "❌ Inválido!"
- task.wait(1)
- UI.MoveInput.PlaceholderText = "1-50"
- end
- end)
- UI.MoveCancel.MouseButton1Click:Connect(function()
- UI.MoveOverlay.Visible = false
- Logic.slotToMove = nil
- end)
- -- Botão de posição principal
- UI.MainPosBtn.MouseButton1Click:Connect(function()
- if State.mainPosition then
- local success = Teleport:execute(State.mainPosition.position)
- if success then
- UI.MainPosBtn.Text = "✅"
- UI.MainPosBtn.BackgroundColor3 = Theme.Success
- task.wait(1)
- if UI.MainPosBtn.Parent then
- UI.MainPosBtn.Text = "🚀"
- Services.TweenService:Create(UI.MainPosBtn, Config.ANIMATION_INFO, {
- BackgroundColor3 = Theme.Background
- }):Play()
- end
- end
- end
- end)
- -- Remover posição principal
- UI.MainRemoveBtn.MouseButton1Click:Connect(function()
- UI.ConfirmLabel.Text = "Remover posição principal?"
- UI.ConfirmYes.Text = "✓ Remover"
- Logic.onConfirmCallback = function()
- Logic:removeMainPosition()
- Logic:updateSlotsList()
- end
- UI.ConfirmationOverlay.Visible = true
- end)
- end
- --==============================================================================
- -- [13] INICIALIZAÇÃO E GESTÃO DE CICLO DE VIDA
- --==============================================================================
- local function initialize()
- -- Atualiza referências do personagem
- Teleport:updateCharacterReferences()
- -- Carrega dados salvos
- Persistence:loadFromDisk()
- -- Inicializa UI
- UI:initialize()
- -- Conecta eventos
- connectUIEvents()
- -- Atualiza interface inicial
- Logic:selectNextSlot()
- Logic:updateSlotsList()
- Logic:updateMainPositionUI()
- State.isInitialized = true
- -- Log de inicialização
- print("╔═══════════════════════════════════════════════════╗")
- print("║ ✅ Save Locate (NickZ) v5.0 - Carregado! ║")
- print("║ ║")
- print("║ 📦 Total de Slots: 50 (5 Páginas de 10) ║")
- print("║ 🚀 Sistema de Teleporte Anti-Bug Ativo ║")
- print("║ 💾 Sistema de Persistência: " .. (FileSystem.hasFS and "✓ Ativo" or "✗ Inativo") .. " ║")
- print("║ 🎨 Interface Moderna e Otimizada ║")
- print("╚═══════════════════════════════════════════════════╝")
- end
- -- Gerenciamento de respawn do personagem
- Player.instance.CharacterAdded:Connect(function(character)
- Player.character = character
- Player.humanoidRoot = character:WaitForChild("HumanoidRootPart", 10)
- end)
- -- Salvamento ao fechar o jogo
- game:BindToClose(function()
- if State.isInitialized then
- Persistence:saveToDisk()
- end
- end)
- -- Inicializa o sistema
- initialize()
Add Comment
Please, Sign In to add comment