Nevzyn

Untitled

Mar 8th, 2026
25
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 70.90 KB | None | 0 0
  1. --[[
  2. ╔══════════════════════════════════════════════════════════════╗
  3. ║ Save Locate (NickZ) — V5.0 MEGA UPDATE ║
  4. ║ ║
  5. ║ • Arquitetura modular profissional ║
  6. ║ • Teleporte robusto (anti-void, anti-seat, retry) ║
  7. ║ • UI componentizada e otimizada ║
  8. ║ • Persistência com validação e integridade ║
  9. ║ • Sistema de paginação avançado ║
  10. ║ • Atalhos de teclado ║
  11. ║ • Performance: pooling, cleanup, throttle ║
  12. ║ • Compatível: Delta, Fluxus, KRNL, Arceus, Solara ║
  13. ╚══════════════════════════════════════════════════════════════╝
  14. --]]
  15.  
  16. ----------------------------------------------------------------
  17. -- SEÇÃO 0 · BOOTSTRAP SEGURO
  18. ----------------------------------------------------------------
  19. if not game then return end
  20. if not game:IsLoaded() then game.Loaded:Wait() end
  21.  
  22. -- Anti-duplicação
  23. local SCRIPT_ID = "SaveLocate_NickZ_V5"
  24. if getgenv and type(getgenv) == "function" then
  25. if getgenv()[SCRIPT_ID] then
  26. -- Destrói instância anterior
  27. local oldGui = game:GetService("CoreGui"):FindFirstChild("SaveLocateNickZV5")
  28. if oldGui then oldGui:Destroy() end
  29. end
  30. getgenv()[SCRIPT_ID] = true
  31. end
  32.  
  33. ----------------------------------------------------------------
  34. -- SEÇÃO 1 · SERVIÇOS (cache único)
  35. ----------------------------------------------------------------
  36. local Services = setmetatable({}, {
  37. __index = function(self, name)
  38. local service = game:GetService(name)
  39. rawset(self, name, service)
  40. return service
  41. end
  42. })
  43.  
  44. local Players = Services.Players
  45. local HttpService = Services.HttpService
  46. local TweenService = Services.TweenService
  47. local UserInputService = Services.UserInputService
  48. local RunService = Services.RunService
  49. local CoreGui = Services.CoreGui
  50.  
  51. ----------------------------------------------------------------
  52. -- SEÇÃO 2 · REFERÊNCIAS DO JOGADOR
  53. ----------------------------------------------------------------
  54. local Player = Players.LocalPlayer
  55.  
  56. local CharacterManager = {
  57. character = nil,
  58. rootPart = nil,
  59. humanoid = nil,
  60. _connections = {},
  61. }
  62.  
  63. function CharacterManager:Refresh()
  64. local char = Player.Character
  65. if not char then return false end
  66.  
  67. self.character = char
  68. self.rootPart = char:FindFirstChild("HumanoidRootPart")
  69. self.humanoid = char:FindFirstChildWhichIsA("Humanoid")
  70.  
  71. return self.rootPart ~= nil
  72. end
  73.  
  74. function CharacterManager:WaitForCharacter()
  75. local char = Player.Character or Player.CharacterAdded:Wait()
  76. self.character = char
  77. self.rootPart = char:WaitForChild("HumanoidRootPart", 15)
  78. self.humanoid = char:WaitForChild("Humanoid", 15)
  79. return self.rootPart ~= nil
  80. end
  81.  
  82. function CharacterManager:IsAlive()
  83. if not self.character or not self.character.Parent then return false end
  84. if not self.rootPart or not self.rootPart.Parent then return false end
  85. if not self.humanoid or self.humanoid.Health <= 0 then return false end
  86. return true
  87. end
  88.  
  89. function CharacterManager:Init()
  90. self:WaitForCharacter()
  91.  
  92. -- Reconectar quando personagem renascer
  93. table.insert(self._connections, Player.CharacterAdded:Connect(function(char)
  94. self.character = char
  95. self.rootPart = char:WaitForChild("HumanoidRootPart", 15)
  96. self.humanoid = char:WaitForChild("Humanoid", 15)
  97. end))
  98. end
  99.  
  100. function CharacterManager:Destroy()
  101. for _, conn in ipairs(self._connections) do
  102. if conn.Connected then conn:Disconnect() end
  103. end
  104. self._connections = {}
  105. end
  106.  
  107. ----------------------------------------------------------------
  108. -- SEÇÃO 3 · DETECÇÃO DE AMBIENTE (UNC / Executor)
  109. ----------------------------------------------------------------
  110. local Environment = {}
  111.  
  112. function Environment:Detect()
  113. self.hasWriteFile = type(writefile) == "function"
  114. self.hasReadFile = type(readfile) == "function"
  115. self.hasFileSystem = self.hasWriteFile and self.hasReadFile
  116. self.hasIsFolder = type(isfolder) == "function"
  117. self.hasMakeFolder = type(makefolder) == "function"
  118. self.hasFolderOps = self.hasIsFolder and self.hasMakeFolder
  119. self.hasIsFile = type(isfile) == "function"
  120. self.hasDelFile = type(delfile) == "function"
  121. self.hasListFiles = type(listfiles) == "function"
  122.  
  123. -- Identificar executor
  124. self.executorName = "Unknown"
  125. if type(identifyexecutor) == "function" then
  126. local ok, name = pcall(identifyexecutor)
  127. if ok and name then self.executorName = tostring(name) end
  128. end
  129.  
  130. -- Clipboard
  131. self.hasClipboard = type(setclipboard) == "function"
  132.  
  133. return self
  134. end
  135.  
  136. Environment:Detect()
  137.  
  138. ----------------------------------------------------------------
  139. -- SEÇÃO 4 · CODIFICAÇÃO / DECODIFICAÇÃO DE CFRAME
  140. ----------------------------------------------------------------
  141. local CFrameCodec = {}
  142.  
  143. function CFrameCodec.Encode(cf)
  144. if typeof(cf) ~= "CFrame" then return nil end
  145. local components = {cf:GetComponents()}
  146. return {
  147. x = components[1], y = components[2], z = components[3],
  148. R00 = components[4], R01 = components[5], R02 = components[6],
  149. R10 = components[7], R11 = components[8], R12 = components[9],
  150. R20 = components[10], R21 = components[11], R22 = components[12],
  151. }
  152. end
  153.  
  154. function CFrameCodec.Decode(t)
  155. if type(t) ~= "table" then return nil end
  156. if not (t.x and t.y and t.z and t.R00) then return nil end
  157.  
  158. local ok, cf = pcall(CFrame.new,
  159. t.x, t.y, t.z,
  160. t.R00, t.R01, t.R02,
  161. t.R10, t.R11, t.R12,
  162. t.R20, t.R21, t.R22
  163. )
  164.  
  165. return ok and cf or nil
  166. end
  167.  
  168. function CFrameCodec.IsValid(cf)
  169. if typeof(cf) ~= "CFrame" then return false end
  170. local pos = cf.Position
  171. -- Proteção anti-void: rejeita coordenadas absurdas
  172. local MAX = 1e7
  173. if math.abs(pos.X) > MAX or math.abs(pos.Y) > MAX or math.abs(pos.Z) > MAX then
  174. return false
  175. end
  176. -- Verifica NaN
  177. if pos.X ~= pos.X or pos.Y ~= pos.Y or pos.Z ~= pos.Z then
  178. return false
  179. end
  180. return true
  181. end
  182.  
  183. ----------------------------------------------------------------
  184. -- SEÇÃO 5 · SISTEMA DE PERSISTÊNCIA
  185. ----------------------------------------------------------------
  186. local Persistence = {
  187. SAVE_DIR = "SaveLocate_NickZ",
  188. VERSION = 5,
  189. MAX_SAVES = 50,
  190. _gameDir = nil,
  191. _saveFile = nil,
  192. _mainFile = nil,
  193. _saveBak = nil,
  194. _mainBak = nil,
  195. }
  196.  
  197. function Persistence:Init()
  198. local gameId = tostring(game.PlaceId)
  199. local userId = tostring(Player.UserId)
  200.  
  201. if Environment.hasFolderOps then
  202. -- Cria diretórios
  203. if not isfolder(self.SAVE_DIR) then
  204. pcall(makefolder, self.SAVE_DIR)
  205. end
  206.  
  207. self._gameDir = self.SAVE_DIR .. "/" .. gameId
  208. if not isfolder(self._gameDir) then
  209. pcall(makefolder, self._gameDir)
  210. end
  211.  
  212. self._saveFile = self._gameDir .. "/Saves_" .. userId .. ".json"
  213. self._mainFile = self._gameDir .. "/MainPos_" .. userId .. ".json"
  214. self._saveBak = self._gameDir .. "/Saves_" .. userId .. ".bak"
  215. self._mainBak = self._gameDir .. "/MainPos_" .. userId .. ".bak"
  216. else
  217. -- Fallback sem pastas
  218. local prefix = string.format("SL_%s_%s", gameId, userId)
  219. self._saveFile = prefix .. "_saves.json"
  220. self._mainFile = prefix .. "_main.json"
  221. self._saveBak = prefix .. "_saves.bak"
  222. self._mainBak = prefix .. "_main.bak"
  223. end
  224. end
  225.  
  226. function Persistence:_SafeRead(path)
  227. if not Environment.hasFileSystem or type(path) ~= "string" then return nil end
  228.  
  229. -- Verifica se arquivo existe antes de ler
  230. if Environment.hasIsFile then
  231. local exists = pcall(function() return isfile(path) end)
  232. if not exists then return nil end
  233. local ok, result = pcall(isfile, path)
  234. if not ok or not result then return nil end
  235. end
  236.  
  237. local ok, content = pcall(readfile, path)
  238. if ok and type(content) == "string" and #content > 2 then
  239. return content
  240. end
  241. return nil
  242. end
  243.  
  244. function Persistence:_SafeWrite(path, content)
  245. if not Environment.hasWriteFile or type(path) ~= "string" then return false end
  246. local ok = pcall(writefile, path, content)
  247. return ok
  248. end
  249.  
  250. function Persistence:_SafeDelete(path)
  251. if not Environment.hasDelFile or type(path) ~= "string" then return false end
  252. local ok = pcall(delfile, path)
  253. return ok
  254. end
  255.  
  256. function Persistence:_ReadWithFallback(primary, backup)
  257. local content = self:_SafeRead(primary)
  258. if content then return content, primary end
  259.  
  260. content = self:_SafeRead(backup)
  261. if content then return content, backup end
  262.  
  263. -- Tenta caminhos legados (V4)
  264. local legacyPaths = {
  265. string.format("SaveLocate_NickZ/%s/Saves_%s.json", game.PlaceId, Player.UserId),
  266. string.format("SkyTP/Saves_%d_%d.json", game.PlaceId, Player.UserId),
  267. string.format("Saves_%d_%d.json", game.PlaceId, Player.UserId),
  268. string.format("SaveLocate_%d_%s.json", game.PlaceId, Player.UserId),
  269. }
  270.  
  271. for _, path in ipairs(legacyPaths) do
  272. content = self:_SafeRead(path)
  273. if content then return content, path end
  274. end
  275.  
  276. return nil, nil
  277. end
  278.  
  279. function Persistence:_Validate(data)
  280. if type(data) ~= "table" then return false end
  281. -- Aceita dados sem gameId (migração) ou com gameId correto
  282. if data.gameId and data.gameId ~= game.PlaceId then return false end
  283. return true
  284. end
  285.  
  286. function Persistence:SavePositions(positions)
  287. if not Environment.hasFileSystem then return false end
  288.  
  289. local payload = {
  290. version = self.VERSION,
  291. gameId = game.PlaceId,
  292. gameName = game.Name,
  293. userId = Player.UserId,
  294. userName = Player.Name,
  295. saveDate = os.date("%Y-%m-%d %H:%M:%S"),
  296. positions = {},
  297. }
  298.  
  299. for slot, data in pairs(positions) do
  300. if type(slot) == "number" and slot >= 1 and slot <= self.MAX_SAVES then
  301. if typeof(data.position) == "CFrame" and CFrameCodec.IsValid(data.position) then
  302. payload.positions[tostring(slot)] = {
  303. name = data.name or ("Slot " .. slot),
  304. timestamp = data.timestamp or os.date("%Y-%m-%d %H:%M:%S"),
  305. position = CFrameCodec.Encode(data.position),
  306. }
  307. end
  308. end
  309. end
  310.  
  311. local ok, encoded = pcall(function()
  312. return HttpService:JSONEncode(payload)
  313. end)
  314.  
  315. if not ok or not encoded then return false end
  316.  
  317. -- Escreve backup primeiro, depois principal (mais seguro)
  318. self:_SafeWrite(self._saveBak, encoded)
  319. return self:_SafeWrite(self._saveFile, encoded)
  320. end
  321.  
  322. function Persistence:LoadPositions()
  323. local positions = {}
  324.  
  325. if not Environment.hasFileSystem then return positions end
  326.  
  327. local content, usedPath = self:_ReadWithFallback(self._saveFile, self._saveBak)
  328. if not content then return positions end
  329.  
  330. local ok, data = pcall(function()
  331. return HttpService:JSONDecode(content)
  332. end)
  333.  
  334. if not ok or not self:_Validate(data) then return positions end
  335.  
  336. local rawPositions = data.positions or data
  337.  
  338. for k, v in pairs(rawPositions) do
  339. local slot = tonumber(k)
  340. if slot and slot >= 1 and slot <= self.MAX_SAVES and type(v) == "table" then
  341. local cf = CFrameCodec.Decode(v.position)
  342. if cf and CFrameCodec.IsValid(cf) then
  343. positions[slot] = {
  344. position = cf,
  345. name = tostring(v.name or ("Slot " .. slot)),
  346. timestamp = tostring(v.timestamp or ""),
  347. }
  348. end
  349. end
  350. end
  351.  
  352. -- Se leu de caminho legado, salva no caminho novo
  353. if usedPath and usedPath ~= self._saveFile then
  354. -- Migração silenciosa
  355. task.defer(function()
  356. local tempPositions = positions
  357. -- Salva no formato novo
  358. local p = {
  359. version = self.VERSION,
  360. gameId = game.PlaceId,
  361. gameName = game.Name,
  362. userId = Player.UserId,
  363. userName = Player.Name,
  364. saveDate = os.date("%Y-%m-%d %H:%M:%S"),
  365. positions = {},
  366. }
  367. for s, d in pairs(tempPositions) do
  368. p.positions[tostring(s)] = {
  369. name = d.name,
  370. timestamp = d.timestamp,
  371. position = CFrameCodec.Encode(d.position),
  372. }
  373. end
  374. local okE, enc = pcall(function() return HttpService:JSONEncode(p) end)
  375. if okE and enc then
  376. self:_SafeWrite(self._saveFile, enc)
  377. self:_SafeWrite(self._saveBak, enc)
  378. end
  379. end)
  380. end
  381.  
  382. return positions
  383. end
  384.  
  385. function Persistence:SaveMainPosition(mainPos)
  386. if not Environment.hasFileSystem or not mainPos then return false end
  387.  
  388. if not CFrameCodec.IsValid(mainPos.position) then return false end
  389.  
  390. local payload = {
  391. version = self.VERSION,
  392. gameId = game.PlaceId,
  393. position = CFrameCodec.Encode(mainPos.position),
  394. name = mainPos.name or "Principal",
  395. timestamp = mainPos.timestamp or os.date("%Y-%m-%d %H:%M:%S"),
  396. }
  397.  
  398. local ok, encoded = pcall(function()
  399. return HttpService:JSONEncode(payload)
  400. end)
  401.  
  402. if not ok or not encoded then return false end
  403.  
  404. self:_SafeWrite(self._mainBak, encoded)
  405. return self:_SafeWrite(self._mainFile, encoded)
  406. end
  407.  
  408. function Persistence:LoadMainPosition()
  409. if not Environment.hasFileSystem then return nil end
  410.  
  411. local content = self:_SafeRead(self._mainFile) or self:_SafeRead(self._mainBak)
  412. if not content then return nil end
  413.  
  414. local ok, data = pcall(function()
  415. return HttpService:JSONDecode(content)
  416. end)
  417.  
  418. if not ok or type(data) ~= "table" then return nil end
  419. if data.gameId and data.gameId ~= game.PlaceId then return nil end
  420.  
  421. local cf = CFrameCodec.Decode(data.position)
  422. if not cf or not CFrameCodec.IsValid(cf) then return nil end
  423.  
  424. return {
  425. position = cf,
  426. name = data.name or "Principal",
  427. timestamp = data.timestamp or "",
  428. }
  429. end
  430.  
  431. function Persistence:DeleteMainPosition()
  432. self:_SafeDelete(self._mainFile)
  433. self:_SafeDelete(self._mainBak)
  434. end
  435.  
  436. ----------------------------------------------------------------
  437. -- SEÇÃO 6 · SISTEMA DE TELEPORTE ROBUSTO
  438. ----------------------------------------------------------------
  439. local Teleporter = {
  440. MAX_RETRIES = 3,
  441. RETRY_DELAY = 0.1,
  442. STABILIZE_FRAMES = 2,
  443. }
  444.  
  445. function Teleporter:Execute(targetCFrame)
  446. -- Validações
  447. if not CFrameCodec.IsValid(targetCFrame) then
  448. return false, "CFrame inválido"
  449. end
  450.  
  451. if not CharacterManager:Refresh() then
  452. return false, "Personagem não encontrado"
  453. end
  454.  
  455. if not CharacterManager:IsAlive() then
  456. return false, "Personagem morto"
  457. end
  458.  
  459. local root = CharacterManager.rootPart
  460. local hum = CharacterManager.humanoid
  461.  
  462. -- 1. Dessentar se estiver sentado
  463. if hum and hum.Sit then
  464. hum.Sit = false
  465. hum:ChangeState(Enum.HumanoidStateType.GettingUp)
  466. task.wait(0.15)
  467. end
  468.  
  469. -- 2. Salva estados originais
  470. local originalAnchored = root.Anchored
  471.  
  472. -- 3. Ancora para evitar interferência da física
  473. root.Anchored = true
  474.  
  475. -- 4. Zera velocidades
  476. root.Velocity = Vector3.zero
  477. root.RotVelocity = Vector3.zero
  478. root.AssemblyLinearVelocity = Vector3.zero
  479. root.AssemblyAngularVelocity = Vector3.zero
  480.  
  481. -- 5. Aplica CFrame com retry
  482. local success = false
  483. for attempt = 1, self.MAX_RETRIES do
  484. root.CFrame = targetCFrame
  485.  
  486. -- Espera frames de física para estabilizar
  487. for _ = 1, self.STABILIZE_FRAMES do
  488. RunService.Heartbeat:Wait()
  489. end
  490.  
  491. -- Verifica se a posição foi aplicada (tolerância de 5 studs)
  492. local distance = (root.Position - targetCFrame.Position).Magnitude
  493. if distance < 5 then
  494. success = true
  495. break
  496. end
  497.  
  498. -- Retry: limpa velocidade novamente
  499. root.Velocity = Vector3.zero
  500. root.RotVelocity = Vector3.zero
  501. root.AssemblyLinearVelocity = Vector3.zero
  502. root.AssemblyAngularVelocity = Vector3.zero
  503.  
  504. if attempt < self.MAX_RETRIES then
  505. task.wait(self.RETRY_DELAY)
  506. end
  507. end
  508.  
  509. -- 6. Restaura ancoragem
  510. root.Anchored = originalAnchored
  511.  
  512. -- 7. Zera velocidade final
  513. root.Velocity = Vector3.zero
  514. root.RotVelocity = Vector3.zero
  515. root.AssemblyLinearVelocity = Vector3.zero
  516. root.AssemblyAngularVelocity = Vector3.zero
  517.  
  518. return success, success and "Teleportado" or "Falha após tentativas"
  519. end
  520.  
  521. ----------------------------------------------------------------
  522. -- SEÇÃO 7 · STATE MANAGER (Estado centralizado)
  523. ----------------------------------------------------------------
  524. local State = {
  525. positions = {}, -- {[slot] = {position, name, timestamp}}
  526. mainPosition = nil, -- {position, name, timestamp}
  527. currentPage = 1,
  528. currentSlot = 1,
  529. maxSaves = 50,
  530. slotsPerPage = 10,
  531. maxPages = 5,
  532. _listeners = {},
  533. }
  534.  
  535. function State:GetPageRange()
  536. local startSlot = (self.currentPage - 1) * self.slotsPerPage + 1
  537. local endSlot = self.currentPage * self.slotsPerPage
  538. return startSlot, endSlot
  539. end
  540.  
  541. function State:FindNextEmptySlot()
  542. for i = 1, self.maxSaves do
  543. if not self.positions[i] then return i end
  544. end
  545. return (self.currentSlot % self.maxSaves) + 1
  546. end
  547.  
  548. function State:GetSlotCount()
  549. local count = 0
  550. for _ in pairs(self.positions) do count = count + 1 end
  551. return count
  552. end
  553.  
  554. function State:GetPageSlotCount(page)
  555. local count = 0
  556. local s = (page - 1) * self.slotsPerPage + 1
  557. local e = page * self.slotsPerPage
  558. for i = s, e do
  559. if self.positions[i] then count = count + 1 end
  560. end
  561. return count
  562. end
  563.  
  564. function State:SavePosition(slot, cframe, name)
  565. if not CFrameCodec.IsValid(cframe) then return false end
  566. if slot < 1 or slot > self.maxSaves then return false end
  567.  
  568. self.positions[slot] = {
  569. position = cframe,
  570. name = (#(name or "") > 0) and name or ("Slot " .. slot),
  571. timestamp = os.date("%Y-%m-%d %H:%M:%S"),
  572. }
  573.  
  574. self:_PersistAll()
  575. self:_Notify("positions_changed")
  576. return true
  577. end
  578.  
  579. function State:DeletePosition(slot)
  580. if not self.positions[slot] then return false end
  581.  
  582. -- Se era a posição principal, remove também
  583. if self.mainPosition and self.positions[slot] then
  584. local mainCF = self.mainPosition.position
  585. local slotCF = self.positions[slot].position
  586. if mainCF and slotCF and mainCF == slotCF then
  587. self.mainPosition = nil
  588. Persistence:DeleteMainPosition()
  589. self:_Notify("main_changed")
  590. end
  591. end
  592.  
  593. self.positions[slot] = nil
  594. self:_PersistAll()
  595. self:_Notify("positions_changed")
  596. return true
  597. end
  598.  
  599. function State:RenamePosition(slot, newName)
  600. if not self.positions[slot] or #newName == 0 then return false end
  601.  
  602. self.positions[slot].name = newName
  603.  
  604. -- Atualiza nome na posição principal se for a mesma
  605. if self.mainPosition then
  606. local mainCF = self.mainPosition.position
  607. local slotCF = self.positions[slot].position
  608. if mainCF and slotCF and mainCF == slotCF then
  609. self.mainPosition.name = newName
  610. Persistence:SaveMainPosition(self.mainPosition)
  611. end
  612. end
  613.  
  614. self:_PersistAll()
  615. self:_Notify("positions_changed")
  616. return true
  617. end
  618.  
  619. function State:SwapPositions(fromSlot, toSlot)
  620. if fromSlot == toSlot then return false end
  621. if fromSlot < 1 or fromSlot > self.maxSaves then return false end
  622. if toSlot < 1 or toSlot > self.maxSaves then return false end
  623.  
  624. local temp = self.positions[fromSlot]
  625. self.positions[fromSlot] = self.positions[toSlot]
  626. self.positions[toSlot] = temp
  627.  
  628. self:_PersistAll()
  629. self:_Notify("positions_changed")
  630. return true
  631. end
  632.  
  633. function State:SetMainPosition(slot)
  634. if not self.positions[slot] then return false end
  635.  
  636. self.mainPosition = {
  637. position = self.positions[slot].position,
  638. name = self.positions[slot].name,
  639. timestamp = self.positions[slot].timestamp,
  640. }
  641.  
  642. Persistence:SaveMainPosition(self.mainPosition)
  643. self:_Notify("main_changed")
  644. return true
  645. end
  646.  
  647. function State:ClearMainPosition()
  648. self.mainPosition = nil
  649. Persistence:DeleteMainPosition()
  650. self:_Notify("main_changed")
  651. end
  652.  
  653. function State:IsMainPosition(slot)
  654. if not self.mainPosition or not self.positions[slot] then return false end
  655. return self.mainPosition.position == self.positions[slot].position
  656. end
  657.  
  658. function State:SetPage(page)
  659. page = math.clamp(page, 1, self.maxPages)
  660. if page ~= self.currentPage then
  661. self.currentPage = page
  662. self:_Notify("page_changed")
  663. end
  664. end
  665.  
  666. function State:NextPage()
  667. local newPage = self.currentPage + 1
  668. if newPage > self.maxPages then newPage = 1 end
  669. self:SetPage(newPage)
  670. end
  671.  
  672. function State:PrevPage()
  673. local newPage = self.currentPage - 1
  674. if newPage < 1 then newPage = self.maxPages end
  675. self:SetPage(newPage)
  676. end
  677.  
  678. function State:UpdateSlot()
  679. self.currentSlot = self:FindNextEmptySlot()
  680. self:_Notify("slot_changed")
  681. end
  682.  
  683. function State:NextSlot()
  684. self.currentSlot = (self.currentSlot % self.maxSaves) + 1
  685. self:_Notify("slot_changed")
  686. end
  687.  
  688. function State:PrevSlot()
  689. self.currentSlot = ((self.currentSlot - 2 + self.maxSaves) % self.maxSaves) + 1
  690. self:_Notify("slot_changed")
  691. end
  692.  
  693. function State:_PersistAll()
  694. task.defer(function()
  695. Persistence:SavePositions(self.positions)
  696. end)
  697. end
  698.  
  699. function State:_Notify(event)
  700. for _, callback in ipairs(self._listeners) do
  701. task.defer(callback, event)
  702. end
  703. end
  704.  
  705. function State:OnChange(callback)
  706. table.insert(self._listeners, callback)
  707. end
  708.  
  709. function State:Load()
  710. self.positions = Persistence:LoadPositions()
  711. self.mainPosition = Persistence:LoadMainPosition()
  712. self:UpdateSlot()
  713. end
  714.  
  715. ----------------------------------------------------------------
  716. -- SEÇÃO 8 · UI FACTORY (Componentes reutilizáveis)
  717. ----------------------------------------------------------------
  718. local Theme = {
  719. bg_primary = Color3.fromRGB(18, 16, 26),
  720. bg_secondary = Color3.fromRGB(28, 25, 40),
  721. bg_tertiary = Color3.fromRGB(38, 35, 52),
  722. bg_input = Color3.fromRGB(25, 22, 36),
  723. accent = Color3.fromRGB(110, 85, 255),
  724. accent_hover = Color3.fromRGB(135, 110, 255),
  725. accent_light = Color3.fromRGB(150, 130, 255),
  726. success = Color3.fromRGB(55, 180, 110),
  727. success_hover = Color3.fromRGB(75, 210, 135),
  728. danger = Color3.fromRGB(220, 75, 75),
  729. danger_hover = Color3.fromRGB(240, 100, 100),
  730. warning = Color3.fromRGB(255, 200, 60),
  731. text_primary = Color3.fromRGB(240, 240, 255),
  732. text_secondary = Color3.fromRGB(170, 170, 195),
  733. text_muted = Color3.fromRGB(110, 108, 135),
  734. stroke = Color3.fromRGB(100, 80, 200),
  735. stroke_dim = Color3.fromRGB(60, 55, 85),
  736. star_gold = Color3.fromRGB(255, 210, 50),
  737. corner_radius = UDim.new(0, 7),
  738. corner_round = UDim.new(0.5, 0),
  739. }
  740.  
  741. local UI = {}
  742.  
  743. function UI.Corner(parent, radius)
  744. local c = Instance.new("UICorner")
  745. c.CornerRadius = radius or Theme.corner_radius
  746. c.Parent = parent
  747. return c
  748. end
  749.  
  750. function UI.Stroke(parent, color, thickness)
  751. local s = Instance.new("UIStroke")
  752. s.Color = color or Theme.stroke
  753. s.Thickness = thickness or 1.2
  754. s.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
  755. s.Parent = parent
  756. return s
  757. end
  758.  
  759. function UI.Padding(parent, top, right, bottom, left)
  760. local p = Instance.new("UIPadding")
  761. p.PaddingTop = UDim.new(0, top or 0)
  762. p.PaddingRight = UDim.new(0, right or 0)
  763. p.PaddingBottom = UDim.new(0, bottom or 0)
  764. p.PaddingLeft = UDim.new(0, left or 0)
  765. p.Parent = parent
  766. return p
  767. end
  768.  
  769. function UI.Tween(instance, props, duration, style, direction)
  770. return TweenService:Create(
  771. instance,
  772. TweenInfo.new(
  773. duration or 0.18,
  774. style or Enum.EasingStyle.Quad,
  775. direction or Enum.EasingDirection.Out
  776. ),
  777. props
  778. )
  779. end
  780.  
  781. function UI.HoverEffect(button, hoverColor, normalColor, props)
  782. props = props or {}
  783. button.MouseEnter:Connect(function()
  784. UI.Tween(button, {BackgroundColor3 = hoverColor}, 0.15):Play()
  785. if props.scale then
  786. UI.Tween(button, {Size = props.scaleUp}, 0.15):Play()
  787. end
  788. end)
  789. button.MouseLeave:Connect(function()
  790. UI.Tween(button, {BackgroundColor3 = normalColor}, 0.15):Play()
  791. if props.scale then
  792. UI.Tween(button, {Size = props.scaleDown}, 0.15):Play()
  793. end
  794. end)
  795. end
  796.  
  797. function UI.CreateButton(config)
  798. local btn = Instance.new("TextButton")
  799. btn.Name = config.name or "Button"
  800. btn.Text = config.text or ""
  801. btn.Size = config.size or UDim2.new(0, 100, 0, 36)
  802. btn.Position = config.position or UDim2.new(0, 0, 0, 0)
  803. btn.BackgroundColor3 = config.color or Theme.accent
  804. btn.Font = config.font or Enum.Font.GothamSemibold
  805. btn.TextSize = config.textSize or 14
  806. btn.TextColor3 = config.textColor or Theme.text_primary
  807. btn.AutoButtonColor = false
  808. btn.BorderSizePixel = 0
  809. btn.Parent = config.parent
  810.  
  811. UI.Corner(btn, config.cornerRadius)
  812.  
  813. if config.hoverColor then
  814. UI.HoverEffect(btn, config.hoverColor, config.color or Theme.accent)
  815. end
  816.  
  817. if config.stroke then
  818. UI.Stroke(btn, config.strokeColor, config.strokeThickness)
  819. end
  820.  
  821. return btn
  822. end
  823.  
  824. function UI.CreateLabel(config)
  825. local label = Instance.new("TextLabel")
  826. label.Name = config.name or "Label"
  827. label.Text = config.text or ""
  828. label.Size = config.size or UDim2.new(1, 0, 0, 20)
  829. label.Position = config.position or UDim2.new(0, 0, 0, 0)
  830. label.BackgroundTransparency = 1
  831. label.Font = config.font or Enum.Font.Gotham
  832. label.TextSize = config.textSize or 14
  833. label.TextColor3 = config.textColor or Theme.text_primary
  834. label.TextXAlignment = config.alignX or Enum.TextXAlignment.Left
  835. label.TextTruncate = config.truncate or Enum.TextTruncate.None
  836. label.Parent = config.parent
  837. return label
  838. end
  839.  
  840. function UI.CreateInput(config)
  841. local input = Instance.new("TextBox")
  842. input.Name = config.name or "Input"
  843. input.Text = config.text or ""
  844. input.PlaceholderText = config.placeholder or ""
  845. input.Size = config.size or UDim2.new(1, 0, 0, 38)
  846. input.Position = config.position or UDim2.new(0, 0, 0, 0)
  847. input.BackgroundColor3 = config.color or Theme.bg_input
  848. input.TextColor3 = config.textColor or Theme.text_primary
  849. input.PlaceholderColor3 = config.placeholderColor or Theme.text_muted
  850. input.Font = config.font or Enum.Font.Gotham
  851. input.TextSize = config.textSize or 14
  852. input.ClearTextOnFocus = config.clearOnFocus or false
  853. input.BorderSizePixel = 0
  854. input.Parent = config.parent
  855.  
  856. UI.Corner(input, config.cornerRadius)
  857. UI.Stroke(input, Theme.stroke_dim, 1)
  858.  
  859. -- Focus effect
  860. input.Focused:Connect(function()
  861. local s = input:FindFirstChildWhichIsA("UIStroke")
  862. if s then UI.Tween(s, {Color = Theme.accent}, 0.2):Play() end
  863. end)
  864. input.FocusLost:Connect(function()
  865. local s = input:FindFirstChildWhichIsA("UIStroke")
  866. if s then UI.Tween(s, {Color = Theme.stroke_dim}, 0.2):Play() end
  867. end)
  868.  
  869. return input
  870. end
  871.  
  872. function UI.CreateFrame(config)
  873. local frame = Instance.new("Frame")
  874. frame.Name = config.name or "Frame"
  875. frame.Size = config.size or UDim2.new(1, 0, 0, 50)
  876. frame.Position = config.position or UDim2.new(0, 0, 0, 0)
  877. frame.BackgroundColor3 = config.color or Theme.bg_secondary
  878. frame.BackgroundTransparency = config.transparency or 0
  879. frame.BorderSizePixel = 0
  880. frame.ClipsDescendants = config.clip ~= false
  881. frame.Parent = config.parent
  882.  
  883. if config.corner ~= false then
  884. UI.Corner(frame, config.cornerRadius)
  885. end
  886.  
  887. return frame
  888. end
  889.  
  890. ----------------------------------------------------------------
  891. -- SEÇÃO 9 · OVERLAY SYSTEM (Modais reutilizáveis)
  892. ----------------------------------------------------------------
  893. local OverlaySystem = {
  894. _activeOverlay = nil,
  895. }
  896.  
  897. function OverlaySystem:CreateOverlay(parent, config)
  898. local overlay = UI.CreateFrame({
  899. name = config.name or "Overlay",
  900. size = UDim2.new(1, 0, 1, 0),
  901. color = Color3.new(0, 0, 0),
  902. transparency = 0.55,
  903. parent = parent,
  904. corner = false,
  905. })
  906. overlay.ZIndex = config.zIndex or 10
  907. overlay.Visible = false
  908.  
  909. local dialog = UI.CreateFrame({
  910. name = "Dialog",
  911. size = config.dialogSize or UDim2.new(0, 320, 0, 140),
  912. position = UDim2.new(0.5, 0, 0.5, 0),
  913. color = Theme.bg_secondary,
  914. parent = overlay,
  915. })
  916. dialog.AnchorPoint = Vector2.new(0.5, 0.5)
  917. dialog.ZIndex = (config.zIndex or 10) + 1
  918. UI.Stroke(dialog, Theme.accent, 1.5)
  919.  
  920. return overlay, dialog
  921. end
  922.  
  923. function OverlaySystem:Show(overlay)
  924. if self._activeOverlay and self._activeOverlay ~= overlay then
  925. self._activeOverlay.Visible = false
  926. end
  927. overlay.Visible = true
  928. self._activeOverlay = overlay
  929. end
  930.  
  931. function OverlaySystem:Hide(overlay)
  932. overlay.Visible = false
  933. if self._activeOverlay == overlay then
  934. self._activeOverlay = nil
  935. end
  936. end
  937.  
  938. ----------------------------------------------------------------
  939. -- SEÇÃO 10 · CONSTRUÇÃO PRINCIPAL DA UI
  940. ----------------------------------------------------------------
  941. local Connections = {} -- Para cleanup
  942.  
  943. -- ScreenGui
  944. local ScreenGui = Instance.new("ScreenGui")
  945. ScreenGui.Name = "SaveLocateNickZV5"
  946. ScreenGui.ResetOnSpawn = false
  947. ScreenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
  948. ScreenGui.DisplayOrder = 100
  949. ScreenGui.Parent = CoreGui
  950.  
  951. -- ===== FRAME PRINCIPAL =====
  952. local MainFrame = UI.CreateFrame({
  953. name = "MainFrame",
  954. size = UDim2.new(0, 620, 0, 480),
  955. position = UDim2.new(0.5, 0, 0.5, 0),
  956. color = Theme.bg_primary,
  957. transparency = 0.05,
  958. parent = ScreenGui,
  959. })
  960. MainFrame.AnchorPoint = Vector2.new(0.5, 0.5)
  961. MainFrame.Active = true
  962. MainFrame.Draggable = true
  963. UI.Stroke(MainFrame, Theme.accent, 1.5)
  964.  
  965. -- Gradiente de fundo
  966. local bgGradient = Instance.new("UIGradient", MainFrame)
  967. bgGradient.Color = ColorSequence.new({
  968. ColorSequenceKeypoint.new(0, Color3.fromRGB(22, 19, 34)),
  969. ColorSequenceKeypoint.new(1, Color3.fromRGB(38, 34, 58)),
  970. })
  971. bgGradient.Rotation = 135
  972.  
  973. -- ===== TOP BAR =====
  974. local TopBar = UI.CreateFrame({
  975. name = "TopBar",
  976. size = UDim2.new(1, 0, 0, 42),
  977. color = Theme.bg_primary,
  978. transparency = 0.8,
  979. parent = MainFrame,
  980. corner = false,
  981. })
  982.  
  983. local TitleLabel = UI.CreateLabel({
  984. name = "Title",
  985. text = "✦ Save Locate (NickZ) · V5",
  986. size = UDim2.new(1, -50, 1, 0),
  987. position = UDim2.new(0, 16, 0, 0),
  988. font = Enum.Font.GothamBold,
  989. textSize = 17,
  990. textColor = Theme.text_primary,
  991. parent = TopBar,
  992. })
  993.  
  994. -- Badge de versão
  995. local VersionBadge = UI.CreateLabel({
  996. name = "VersionBadge",
  997. text = Environment.executorName,
  998. size = UDim2.new(0, 80, 0, 18),
  999. position = UDim2.new(1, -130, 0.5, -9),
  1000. font = Enum.Font.GothamBold,
  1001. textSize = 9,
  1002. textColor = Theme.accent_light,
  1003. alignX = Enum.TextXAlignment.Center,
  1004. parent = TopBar,
  1005. })
  1006. local vBadgeBg = UI.CreateFrame({
  1007. name = "BadgeBG",
  1008. size = UDim2.new(0, 80, 0, 18),
  1009. position = UDim2.new(1, -130, 0.5, -9),
  1010. color = Theme.accent,
  1011. transparency = 0.85,
  1012. parent = TopBar,
  1013. })
  1014. vBadgeBg.ZIndex = 0
  1015. UI.Corner(vBadgeBg, UDim.new(0, 4))
  1016.  
  1017. -- Botão Fechar
  1018. local CloseBtn = UI.CreateButton({
  1019. name = "Close",
  1020. text = "✕",
  1021. size = UDim2.new(0, 32, 0, 32),
  1022. position = UDim2.new(1, -40, 0.5, -16),
  1023. color = Theme.bg_tertiary,
  1024. hoverColor = Theme.danger,
  1025. textColor = Theme.danger,
  1026. textSize = 15,
  1027. font = Enum.Font.GothamBold,
  1028. parent = TopBar,
  1029. })
  1030.  
  1031. -- Linha separadora
  1032. local Separator1 = UI.CreateFrame({
  1033. name = "Sep1",
  1034. size = UDim2.new(1, -24, 0, 1),
  1035. position = UDim2.new(0, 12, 0, 42),
  1036. color = Theme.stroke_dim,
  1037. transparency = 0.5,
  1038. parent = MainFrame,
  1039. corner = false,
  1040. })
  1041.  
  1042. -- ===== CONTROLES (Salvar + Slot + Nome) =====
  1043. local ControlsFrame = UI.CreateFrame({
  1044. name = "Controls",
  1045. size = UDim2.new(1, -24, 0, 88),
  1046. position = UDim2.new(0, 12, 0, 48),
  1047. transparency = 1,
  1048. parent = MainFrame,
  1049. })
  1050.  
  1051. -- Linha superior: Botão Salvar + Seletor de Slot
  1052. local SaveButton = UI.CreateButton({
  1053. name = "SaveBtn",
  1054. text = "💾 Salvar Posição",
  1055. size = UDim2.new(0.52, -4, 0, 40),
  1056. position = UDim2.new(0, 0, 0, 0),
  1057. color = Theme.success,
  1058. hoverColor = Theme.success_hover,
  1059. font = Enum.Font.GothamBold,
  1060. textSize = 15,
  1061. parent = ControlsFrame,
  1062. })
  1063.  
  1064. -- Slot selector
  1065. local SlotFrame = UI.CreateFrame({
  1066. name = "SlotFrame",
  1067. size = UDim2.new(0.48, -4, 0, 40),
  1068. position = UDim2.new(0.52, 4, 0, 0),
  1069. color = Theme.bg_secondary,
  1070. parent = ControlsFrame,
  1071. })
  1072. UI.Stroke(SlotFrame, Theme.stroke_dim, 1)
  1073.  
  1074. local SlotLabel = UI.CreateLabel({
  1075. name = "SlotLabel",
  1076. text = "Slot: 1",
  1077. size = UDim2.new(1, -80, 1, 0),
  1078. position = UDim2.new(0, 12, 0, 0),
  1079. font = Enum.Font.GothamSemibold,
  1080. textSize = 14,
  1081. textColor = Theme.text_primary,
  1082. parent = SlotFrame,
  1083. })
  1084.  
  1085. local SlotPrev = UI.CreateButton({
  1086. name = "SlotPrev",
  1087. text = "◂",
  1088. size = UDim2.new(0, 34, 0, 30),
  1089. position = UDim2.new(1, -74, 0.5, -15),
  1090. color = Theme.bg_tertiary,
  1091. hoverColor = Theme.accent,
  1092. font = Enum.Font.GothamBold,
  1093. textSize = 16,
  1094. parent = SlotFrame,
  1095. })
  1096.  
  1097. local SlotNext = UI.CreateButton({
  1098. name = "SlotNext",
  1099. text = "▸",
  1100. size = UDim2.new(0, 34, 0, 30),
  1101. position = UDim2.new(1, -38, 0.5, -15),
  1102. color = Theme.bg_tertiary,
  1103. hoverColor = Theme.accent,
  1104. font = Enum.Font.GothamBold,
  1105. textSize = 16,
  1106. parent = SlotFrame,
  1107. })
  1108.  
  1109. -- Nome da posição
  1110. local NameField = UI.CreateInput({
  1111. name = "NameField",
  1112. placeholder = "Nome da posição (opcional)...",
  1113. size = UDim2.new(1, 0, 0, 38),
  1114. position = UDim2.new(0, 0, 0, 48),
  1115. parent = ControlsFrame,
  1116. })
  1117.  
  1118. -- Linha separadora 2
  1119. local Separator2 = UI.CreateFrame({
  1120. name = "Sep2",
  1121. size = UDim2.new(1, -24, 0, 1),
  1122. position = UDim2.new(0, 12, 0, 140),
  1123. color = Theme.stroke_dim,
  1124. transparency = 0.5,
  1125. parent = MainFrame,
  1126. corner = false,
  1127. })
  1128.  
  1129. -- ===== PAGINAÇÃO =====
  1130. local PageFrame = UI.CreateFrame({
  1131. name = "PageFrame",
  1132. size = UDim2.new(1, -24, 0, 32),
  1133. position = UDim2.new(0, 12, 0, 146),
  1134. transparency = 1,
  1135. parent = MainFrame,
  1136. })
  1137.  
  1138. local PagePrev = UI.CreateButton({
  1139. name = "PagePrev",
  1140. text = "◄ Anterior",
  1141. size = UDim2.new(0, 100, 0, 28),
  1142. position = UDim2.new(0, 0, 0, 2),
  1143. color = Theme.bg_tertiary,
  1144. hoverColor = Theme.accent,
  1145. font = Enum.Font.GothamSemibold,
  1146. textSize = 11,
  1147. parent = PageFrame,
  1148. })
  1149.  
  1150. local PageLabel = UI.CreateLabel({
  1151. name = "PageLabel",
  1152. text = "Página 1 / 5",
  1153. size = UDim2.new(1, -220, 1, 0),
  1154. position = UDim2.new(0, 110, 0, 0),
  1155. font = Enum.Font.GothamBold,
  1156. textSize = 15,
  1157. textColor = Theme.text_primary,
  1158. alignX = Enum.TextXAlignment.Center,
  1159. parent = PageFrame,
  1160. })
  1161.  
  1162. local PageCounter = UI.CreateLabel({
  1163. name = "PageCounter",
  1164. text = "0 / 10 slots",
  1165. size = UDim2.new(0, 80, 1, 0),
  1166. position = UDim2.new(1, -190, 0, 0),
  1167. font = Enum.Font.Gotham,
  1168. textSize = 11,
  1169. textColor = Theme.text_muted,
  1170. alignX = Enum.TextXAlignment.Center,
  1171. parent = PageFrame,
  1172. })
  1173.  
  1174. local PageNext = UI.CreateButton({
  1175. name = "PageNext",
  1176. text = "Próxima ►",
  1177. size = UDim2.new(0, 100, 0, 28),
  1178. position = UDim2.new(1, -100, 0, 2),
  1179. color = Theme.bg_tertiary,
  1180. hoverColor = Theme.accent,
  1181. font = Enum.Font.GothamSemibold,
  1182. textSize = 11,
  1183. parent = PageFrame,
  1184. })
  1185.  
  1186. -- Indicadores de página (dots)
  1187. local DotsFrame = UI.CreateFrame({
  1188. name = "Dots",
  1189. size = UDim2.new(0, 80, 0, 8),
  1190. position = UDim2.new(0.5, -40, 1, -3),
  1191. transparency = 1,
  1192. parent = PageFrame,
  1193. corner = false,
  1194. })
  1195.  
  1196. local dotInstances = {}
  1197. for i = 1, State.maxPages do
  1198. local dot = UI.CreateFrame({
  1199. name = "Dot" .. i,
  1200. size = UDim2.new(0, 8, 0, 8),
  1201. position = UDim2.new(0, (i - 1) * 16, 0, 0),
  1202. color = i == 1 and Theme.accent or Theme.stroke_dim,
  1203. parent = DotsFrame,
  1204. cornerRadius = Theme.corner_round,
  1205. })
  1206. dotInstances[i] = dot
  1207. end
  1208.  
  1209. -- ===== CONTAINER DE SLOTS =====
  1210. local SlotsContainer = UI.CreateFrame({
  1211. name = "SlotsContainer",
  1212. size = UDim2.new(1, -24, 1, -195),
  1213. position = UDim2.new(0, 12, 0, 185),
  1214. transparency = 1,
  1215. parent = MainFrame,
  1216. corner = false,
  1217. })
  1218.  
  1219. local GridLayout = Instance.new("UIGridLayout", SlotsContainer)
  1220. GridLayout.CellPadding = UDim2.new(0, 8, 0, 6)
  1221. GridLayout.CellSize = UDim2.new(0.5, -4, 0, 50)
  1222. GridLayout.FillDirection = Enum.FillDirection.Vertical
  1223. GridLayout.SortOrder = Enum.SortOrder.LayoutOrder
  1224. GridLayout.StartCorner = Enum.StartCorner.TopLeft
  1225.  
  1226. -- ===== BOTÃO ABRIR (quando minimizado) =====
  1227. local OpenBtn = UI.CreateButton({
  1228. name = "OpenBtn",
  1229. text = "✦",
  1230. size = UDim2.new(0, 48, 0, 48),
  1231. position = UDim2.new(0, 15, 0.5, -24),
  1232. color = Theme.bg_primary,
  1233. hoverColor = Theme.accent,
  1234. font = Enum.Font.GothamBold,
  1235. textSize = 24,
  1236. textColor = Theme.accent,
  1237. stroke = true,
  1238. strokeColor = Theme.accent,
  1239. parent = ScreenGui,
  1240. })
  1241. OpenBtn.Visible = false
  1242.  
  1243. -- ===== BOTÃO POSIÇÃO PRINCIPAL (Flutuante) =====
  1244. local MainPosContainer = UI.CreateFrame({
  1245. name = "MainPosContainer",
  1246. size = UDim2.new(0, 52, 0, 52),
  1247. position = UDim2.new(1, -62, 0, 12),
  1248. transparency = 1,
  1249. parent = ScreenGui,
  1250. corner = false,
  1251. })
  1252. MainPosContainer.Visible = false
  1253. MainPosContainer.Active = true
  1254. MainPosContainer.Draggable = true
  1255.  
  1256. local MainPosBtn = UI.CreateButton({
  1257. name = "MainPosBtn",
  1258. text = "🚀",
  1259. size = UDim2.new(1, 0, 1, 0),
  1260. color = Theme.bg_primary,
  1261. font = Enum.Font.GothamBold,
  1262. textSize = 22,
  1263. parent = MainPosContainer,
  1264. cornerRadius = Theme.corner_round,
  1265. stroke = true,
  1266. strokeColor = Theme.accent,
  1267. strokeThickness = 2,
  1268. })
  1269.  
  1270. -- Tooltip
  1271. local MainPosTooltip = UI.CreateLabel({
  1272. name = "Tooltip",
  1273. text = "",
  1274. size = UDim2.new(0, 160, 0, 28),
  1275. position = UDim2.new(-3.4, 0, 0.5, -14),
  1276. font = Enum.Font.GothamSemibold,
  1277. textSize = 11,
  1278. textColor = Theme.text_primary,
  1279. alignX = Enum.TextXAlignment.Center,
  1280. parent = MainPosBtn,
  1281. })
  1282. MainPosTooltip.BackgroundColor3 = Theme.bg_primary
  1283. MainPosTooltip.BackgroundTransparency = 0.08
  1284. MainPosTooltip.Visible = false
  1285. MainPosTooltip.ZIndex = 5
  1286. UI.Corner(MainPosTooltip, UDim.new(0, 5))
  1287. UI.Stroke(MainPosTooltip, Theme.accent, 1)
  1288.  
  1289. -- Botão remover posição principal
  1290. local MainPosRemove = UI.CreateButton({
  1291. name = "RemoveMainPos",
  1292. text = "×",
  1293. size = UDim2.new(0, 20, 0, 20),
  1294. position = UDim2.new(1, -3, 0, -3),
  1295. color = Theme.danger,
  1296. hoverColor = Theme.danger_hover,
  1297. font = Enum.Font.GothamBold,
  1298. textSize = 14,
  1299. parent = MainPosBtn,
  1300. cornerRadius = Theme.corner_round,
  1301. })
  1302. MainPosRemove.Visible = false
  1303. MainPosRemove.ZIndex = 6
  1304.  
  1305. ----------------------------------------------------------------
  1306. -- SEÇÃO 11 · OVERLAYS (Confirmação, Renomear, Mover)
  1307. ----------------------------------------------------------------
  1308.  
  1309. -- ===== OVERLAY DE CONFIRMAÇÃO =====
  1310. local ConfirmOverlay, ConfirmDialog = OverlaySystem:CreateOverlay(MainFrame, {
  1311. name = "ConfirmOverlay",
  1312. zIndex = 20,
  1313. dialogSize = UDim2.new(0, 320, 0, 135),
  1314. })
  1315.  
  1316. local ConfirmLabel = UI.CreateLabel({
  1317. name = "Message",
  1318. text = "",
  1319. size = UDim2.new(1, -24, 0, 60),
  1320. position = UDim2.new(0, 12, 0, 8),
  1321. font = Enum.Font.GothamSemibold,
  1322. textSize = 15,
  1323. textColor = Theme.text_primary,
  1324. parent = ConfirmDialog,
  1325. })
  1326. ConfirmLabel.TextWrapped = true
  1327. ConfirmLabel.ZIndex = 21
  1328.  
  1329. local ConfirmYes = UI.CreateButton({
  1330. name = "Yes",
  1331. text = "Sim",
  1332. size = UDim2.new(0.5, -16, 0, 38),
  1333. position = UDim2.new(0, 10, 1, -48),
  1334. color = Theme.danger,
  1335. hoverColor = Theme.danger_hover,
  1336. font = Enum.Font.GothamBold,
  1337. parent = ConfirmDialog,
  1338. })
  1339. ConfirmYes.ZIndex = 21
  1340.  
  1341. local ConfirmNo = UI.CreateButton({
  1342. name = "No",
  1343. text = "Não",
  1344. size = UDim2.new(0.5, -16, 0, 38),
  1345. position = UDim2.new(0.5, 6, 1, -48),
  1346. color = Theme.bg_tertiary,
  1347. hoverColor = Theme.accent,
  1348. font = Enum.Font.GothamBold,
  1349. parent = ConfirmDialog,
  1350. })
  1351. ConfirmNo.ZIndex = 21
  1352.  
  1353. -- ===== OVERLAY DE RENOMEAR =====
  1354. local RenameOverlay, RenameDialog = OverlaySystem:CreateOverlay(MainFrame, {
  1355. name = "RenameOverlay",
  1356. zIndex = 25,
  1357. dialogSize = UDim2.new(0, 360, 0, 170),
  1358. })
  1359.  
  1360. local RenameTitle = UI.CreateLabel({
  1361. name = "Title",
  1362. text = "Renomear Posição",
  1363. size = UDim2.new(1, -24, 0, 36),
  1364. position = UDim2.new(0, 12, 0, 5),
  1365. font = Enum.Font.GothamBold,
  1366. textSize = 16,
  1367. textColor = Theme.text_primary,
  1368. parent = RenameDialog,
  1369. })
  1370. RenameTitle.ZIndex = 26
  1371.  
  1372. local RenameField = UI.CreateInput({
  1373. name = "RenameInput",
  1374. placeholder = "Novo nome...",
  1375. size = UDim2.new(1, -24, 0, 40),
  1376. position = UDim2.new(0, 12, 0, 44),
  1377. parent = RenameDialog,
  1378. })
  1379. RenameField.ZIndex = 26
  1380.  
  1381. local RenameConfirm = UI.CreateButton({
  1382. name = "Confirm",
  1383. text = "✓ Confirmar",
  1384. size = UDim2.new(0.5, -16, 0, 38),
  1385. position = UDim2.new(0, 10, 1, -48),
  1386. color = Theme.accent,
  1387. hoverColor = Theme.accent_hover,
  1388. font = Enum.Font.GothamBold,
  1389. parent = RenameDialog,
  1390. })
  1391. RenameConfirm.ZIndex = 26
  1392.  
  1393. local RenameCancel = UI.CreateButton({
  1394. name = "Cancel",
  1395. text = "Cancelar",
  1396. size = UDim2.new(0.5, -16, 0, 38),
  1397. position = UDim2.new(0.5, 6, 1, -48),
  1398. color = Theme.bg_tertiary,
  1399. hoverColor = Theme.accent,
  1400. font = Enum.Font.GothamBold,
  1401. parent = RenameDialog,
  1402. })
  1403. RenameCancel.ZIndex = 26
  1404.  
  1405. -- ===== OVERLAY DE MOVER =====
  1406. local MoveOverlay, MoveDialog = OverlaySystem:CreateOverlay(MainFrame, {
  1407. name = "MoveOverlay",
  1408. zIndex = 30,
  1409. dialogSize = UDim2.new(0, 320, 0, 190),
  1410. })
  1411.  
  1412. local MoveTitle = UI.CreateLabel({
  1413. name = "Title",
  1414. text = "Mover Posição",
  1415. size = UDim2.new(1, -24, 0, 36),
  1416. position = UDim2.new(0, 12, 0, 5),
  1417. font = Enum.Font.GothamBold,
  1418. textSize = 16,
  1419. textColor = Theme.text_primary,
  1420. parent = MoveDialog,
  1421. })
  1422. MoveTitle.ZIndex = 31
  1423.  
  1424. local MoveInfo = UI.CreateLabel({
  1425. name = "Info",
  1426. text = "Slot de destino (1-50):",
  1427. size = UDim2.new(1, -24, 0, 22),
  1428. position = UDim2.new(0, 12, 0, 42),
  1429. font = Enum.Font.Gotham,
  1430. textSize = 13,
  1431. textColor = Theme.text_secondary,
  1432. parent = MoveDialog,
  1433. })
  1434. MoveInfo.ZIndex = 31
  1435.  
  1436. local MoveInput = UI.CreateInput({
  1437. name = "MoveInput",
  1438. placeholder = "1-50",
  1439. size = UDim2.new(0, 100, 0, 42),
  1440. position = UDim2.new(0.5, -50, 0, 68),
  1441. parent = MoveDialog,
  1442. textSize = 20,
  1443. font = Enum.Font.GothamBold,
  1444. })
  1445. MoveInput.ZIndex = 31
  1446. MoveInput.TextXAlignment = Enum.TextXAlignment.Center
  1447.  
  1448. local MoveConfirm = UI.CreateButton({
  1449. name = "Confirm",
  1450. text = "✓ Mover",
  1451. size = UDim2.new(0.5, -16, 0, 38),
  1452. position = UDim2.new(0, 10, 1, -48),
  1453. color = Theme.accent,
  1454. hoverColor = Theme.accent_hover,
  1455. font = Enum.Font.GothamBold,
  1456. parent = MoveDialog,
  1457. })
  1458. MoveConfirm.ZIndex = 31
  1459.  
  1460. local MoveCancel = UI.CreateButton({
  1461. name = "Cancel",
  1462. text = "Cancelar",
  1463. size = UDim2.new(0.5, -16, 0, 38),
  1464. position = UDim2.new(0.5, 6, 1, -48),
  1465. color = Theme.bg_tertiary,
  1466. hoverColor = Theme.accent,
  1467. font = Enum.Font.GothamBold,
  1468. parent = MoveDialog,
  1469. })
  1470. MoveCancel.ZIndex = 31
  1471.  
  1472. ----------------------------------------------------------------
  1473. -- SEÇÃO 12 · RENDERIZAÇÃO DOS SLOTS
  1474. ----------------------------------------------------------------
  1475. local activeMenuFrame = nil -- Referência ao menu de contexto aberto
  1476. local activeMenuConnection = nil
  1477.  
  1478. local function CloseActiveMenu()
  1479. if activeMenuFrame then
  1480. activeMenuFrame.Visible = false
  1481. activeMenuFrame = nil
  1482. end
  1483. if activeMenuConnection then
  1484. activeMenuConnection:Disconnect()
  1485. activeMenuConnection = nil
  1486. end
  1487. end
  1488.  
  1489. local function RenderSlots()
  1490. -- Fecha menu de contexto aberto
  1491. CloseActiveMenu()
  1492.  
  1493. -- Limpa slots existentes (apenas Frames do container)
  1494. for _, child in ipairs(SlotsContainer:GetChildren()) do
  1495. if child:IsA("Frame") then child:Destroy() end
  1496. end
  1497.  
  1498. -- Remove menus de contexto órfãos
  1499. for _, child in ipairs(ScreenGui:GetChildren()) do
  1500. if child.Name:match("^CtxMenu_") then child:Destroy() end
  1501. end
  1502.  
  1503. local startSlot, endSlot = State:GetPageRange()
  1504.  
  1505. -- Atualiza contador
  1506. local pageCount = State:GetPageSlotCount(State.currentPage)
  1507. PageCounter.Text = pageCount .. " / " .. State.slotsPerPage .. " slots"
  1508.  
  1509. for slot = startSlot, endSlot do
  1510. local data = State.positions[slot]
  1511.  
  1512. local slotFrame = UI.CreateFrame({
  1513. name = "Slot_" .. slot,
  1514. size = UDim2.new(0.5, -4, 0, 50),
  1515. color = Theme.bg_tertiary,
  1516. transparency = data and 0.15 or 0.55,
  1517. parent = SlotsContainer,
  1518. })
  1519. slotFrame.LayoutOrder = slot
  1520.  
  1521. if data then
  1522. -- === SLOT COM DADOS ===
  1523. local isMain = State:IsMainPosition(slot)
  1524.  
  1525. -- Coluna esquerda: ações rápidas
  1526. local actionsCol = UI.CreateFrame({
  1527. name = "Actions",
  1528. size = UDim2.new(0, 32, 1, -6),
  1529. position = UDim2.new(0, 4, 0, 3),
  1530. transparency = 1,
  1531. parent = slotFrame,
  1532. corner = false,
  1533. })
  1534.  
  1535. -- Estrela (definir como principal)
  1536. local starBtn = UI.CreateButton({
  1537. name = "Star",
  1538. text = isMain and "★" or "☆",
  1539. size = UDim2.new(1, 0, 0, 20),
  1540. position = UDim2.new(0, 0, 0, 0),
  1541. color = Theme.bg_tertiary,
  1542. textColor = isMain and Theme.star_gold or Theme.text_muted,
  1543. font = Enum.Font.GothamBold,
  1544. textSize = 14,
  1545. parent = actionsCol,
  1546. cornerRadius = UDim.new(0, 4),
  1547. })
  1548.  
  1549. -- Menu (⋮)
  1550. local menuBtn = UI.CreateButton({
  1551. name = "Menu",
  1552. text = "⋮",
  1553. size = UDim2.new(1, 0, 0, 20),
  1554. position = UDim2.new(0, 0, 0, 23),
  1555. color = Theme.bg_tertiary,
  1556. textColor = Theme.text_muted,
  1557. font = Enum.Font.GothamBold,
  1558. textSize = 18,
  1559. parent = actionsCol,
  1560. cornerRadius = UDim.new(0, 4),
  1561. })
  1562.  
  1563. -- Nome
  1564. local nameLabel = UI.CreateLabel({
  1565. name = "Name",
  1566. text = data.name,
  1567. size = UDim2.new(1, -85, 0, 24),
  1568. position = UDim2.new(0, 40, 0, 4),
  1569. font = Enum.Font.GothamSemibold,
  1570. textSize = 13,
  1571. textColor = Theme.text_primary,
  1572. truncate = Enum.TextTruncate.AtEnd,
  1573. parent = slotFrame,
  1574. })
  1575.  
  1576. -- Timestamp
  1577. local timeLabel = UI.CreateLabel({
  1578. name = "Time",
  1579. text = "📅 " .. (data.timestamp or ""),
  1580. size = UDim2.new(1, -85, 0, 14),
  1581. position = UDim2.new(0, 40, 0, 30),
  1582. font = Enum.Font.Gotham,
  1583. textSize = 10,
  1584. textColor = Theme.text_muted,
  1585. parent = slotFrame,
  1586. })
  1587.  
  1588. -- Botão TP
  1589. local tpBtn = UI.CreateButton({
  1590. name = "TP",
  1591. text = "TP",
  1592. size = UDim2.new(0, 38, 0, 38),
  1593. position = UDim2.new(1, -44, 0.5, -19),
  1594. color = Theme.accent,
  1595. hoverColor = Theme.accent_hover,
  1596. font = Enum.Font.GothamBold,
  1597. textSize = 13,
  1598. parent = slotFrame,
  1599. })
  1600.  
  1601. -- Indicador de posição principal
  1602. if isMain then
  1603. local mainIndicator = UI.CreateFrame({
  1604. name = "MainIndicator",
  1605. size = UDim2.new(0, 3, 1, -8),
  1606. position = UDim2.new(0, 0, 0, 4),
  1607. color = Theme.star_gold,
  1608. parent = slotFrame,
  1609. cornerRadius = UDim.new(0, 2),
  1610. })
  1611. end
  1612.  
  1613. -- === EVENTOS DO SLOT ===
  1614.  
  1615. -- TP
  1616. tpBtn.MouseButton1Click:Connect(function()
  1617. local success, msg = Teleporter:Execute(data.position)
  1618. if success then
  1619. tpBtn.Text = "✓"
  1620. UI.Tween(tpBtn, {BackgroundColor3 = Theme.success}, 0.15):Play()
  1621. task.delay(1.2, function()
  1622. if tpBtn.Parent then
  1623. tpBtn.Text = "TP"
  1624. UI.Tween(tpBtn, {BackgroundColor3 = Theme.accent}, 0.15):Play()
  1625. end
  1626. end)
  1627. else
  1628. tpBtn.Text = "✕"
  1629. UI.Tween(tpBtn, {BackgroundColor3 = Theme.danger}, 0.15):Play()
  1630. task.delay(1.2, function()
  1631. if tpBtn.Parent then
  1632. tpBtn.Text = "TP"
  1633. UI.Tween(tpBtn, {BackgroundColor3 = Theme.accent}, 0.15):Play()
  1634. end
  1635. end)
  1636. end
  1637. end)
  1638.  
  1639. -- Estrela
  1640. starBtn.MouseButton1Click:Connect(function()
  1641. if State:IsMainPosition(slot) then
  1642. ConfirmLabel.Text = "Remover como posição principal?"
  1643. ConfirmYes.Text = "Remover"
  1644. local _onConfirm
  1645. _onConfirm = ConfirmYes.MouseButton1Click:Once(function()
  1646. State:ClearMainPosition()
  1647. RenderSlots()
  1648. OverlaySystem:Hide(ConfirmOverlay)
  1649. end)
  1650. local _onCancel
  1651. _onCancel = ConfirmNo.MouseButton1Click:Once(function()
  1652. OverlaySystem:Hide(ConfirmOverlay)
  1653. end)
  1654. OverlaySystem:Show(ConfirmOverlay)
  1655. else
  1656. local msg = State.mainPosition
  1657. and "Substituir posição principal atual?"
  1658. or "Definir como posição principal?"
  1659. ConfirmLabel.Text = msg
  1660. ConfirmYes.Text = "Sim"
  1661. ConfirmYes.MouseButton1Click:Once(function()
  1662. State:SetMainPosition(slot)
  1663. RenderSlots()
  1664. OverlaySystem:Hide(ConfirmOverlay)
  1665. end)
  1666. ConfirmNo.MouseButton1Click:Once(function()
  1667. OverlaySystem:Hide(ConfirmOverlay)
  1668. end)
  1669. OverlaySystem:Show(ConfirmOverlay)
  1670. end
  1671. end)
  1672.  
  1673. -- Menu de contexto
  1674. menuBtn.MouseButton1Click:Connect(function()
  1675. CloseActiveMenu()
  1676.  
  1677. -- Cria menu de contexto no ScreenGui
  1678. local ctxMenu = UI.CreateFrame({
  1679. name = "CtxMenu_" .. slot,
  1680. size = UDim2.new(0, 130, 0, 100),
  1681. color = Theme.bg_secondary,
  1682. parent = ScreenGui,
  1683. })
  1684. ctxMenu.ZIndex = 50
  1685. UI.Stroke(ctxMenu, Theme.accent, 1.2)
  1686.  
  1687. -- Posiciona perto do botão
  1688. local btnPos = menuBtn.AbsolutePosition
  1689. ctxMenu.Position = UDim2.new(0, btnPos.X + 35, 0, btnPos.Y - 5)
  1690.  
  1691. -- Botões do menu
  1692. local ctxBtns = {
  1693. {text = "✏️ Renomear", action = "rename", color = Theme.text_primary},
  1694. {text = "↔️ Mover", action = "move", color = Theme.text_primary},
  1695. {text = "🗑️ Excluir", action = "delete", color = Theme.danger},
  1696. }
  1697.  
  1698. for i, info in ipairs(ctxBtns) do
  1699. local ctxBtn = UI.CreateButton({
  1700. name = info.action,
  1701. text = info.text,
  1702. size = UDim2.new(1, -8, 0, 27),
  1703. position = UDim2.new(0, 4, 0, 4 + (i - 1) * 31),
  1704. color = Theme.bg_tertiary,
  1705. hoverColor = Theme.accent,
  1706. font = Enum.Font.Gotham,
  1707. textSize = 12,
  1708. textColor = info.color,
  1709. parent = ctxMenu,
  1710. })
  1711. ctxBtn.ZIndex = 51
  1712. ctxBtn.TextXAlignment = Enum.TextXAlignment.Left
  1713. UI.Padding(ctxBtn, 0, 0, 0, 8)
  1714.  
  1715. ctxBtn.MouseButton1Click:Connect(function()
  1716. CloseActiveMenu()
  1717.  
  1718. if info.action == "rename" then
  1719. RenameTitle.Text = string.format("Renomear Slot %d", slot)
  1720. RenameField.Text = data.name
  1721.  
  1722. RenameConfirm.MouseButton1Click:Once(function()
  1723. if #RenameField.Text > 0 then
  1724. State:RenamePosition(slot, RenameField.Text)
  1725. RenderSlots()
  1726. end
  1727. OverlaySystem:Hide(RenameOverlay)
  1728. end)
  1729. RenameCancel.MouseButton1Click:Once(function()
  1730. OverlaySystem:Hide(RenameOverlay)
  1731. end)
  1732. OverlaySystem:Show(RenameOverlay)
  1733.  
  1734. elseif info.action == "move" then
  1735. MoveTitle.Text = string.format("Mover: %s (Slot %d)", data.name, slot)
  1736. MoveInput.Text = ""
  1737.  
  1738. MoveConfirm.MouseButton1Click:Once(function()
  1739. local target = tonumber(MoveInput.Text)
  1740. if target and target >= 1 and target <= State.maxSaves then
  1741. State:SwapPositions(slot, target)
  1742. -- Navega para a página do destino
  1743. local targetPage = math.ceil(target / State.slotsPerPage)
  1744. State:SetPage(targetPage)
  1745. RenderSlots()
  1746. else
  1747. MoveInput.Text = ""
  1748. MoveInput.PlaceholderText = "Inválido!"
  1749. task.delay(1, function()
  1750. if MoveInput.Parent then
  1751. MoveInput.PlaceholderText = "1-50"
  1752. end
  1753. end)
  1754. return
  1755. end
  1756. OverlaySystem:Hide(MoveOverlay)
  1757. end)
  1758. MoveCancel.MouseButton1Click:Once(function()
  1759. OverlaySystem:Hide(MoveOverlay)
  1760. end)
  1761. OverlaySystem:Show(MoveOverlay)
  1762.  
  1763. elseif info.action == "delete" then
  1764. ConfirmLabel.Text = string.format(
  1765. "Excluir \"%s\" (Slot %d)?",
  1766. data.name, slot
  1767. )
  1768. ConfirmYes.Text = "Sim, Excluir"
  1769.  
  1770. ConfirmYes.MouseButton1Click:Once(function()
  1771. State:DeletePosition(slot)
  1772. State:UpdateSlot()
  1773. RenderSlots()
  1774. OverlaySystem:Hide(ConfirmOverlay)
  1775. end)
  1776. ConfirmNo.MouseButton1Click:Once(function()
  1777. OverlaySystem:Hide(ConfirmOverlay)
  1778. end)
  1779. OverlaySystem:Show(ConfirmOverlay)
  1780. end
  1781. end)
  1782. end
  1783.  
  1784. activeMenuFrame = ctxMenu
  1785.  
  1786. -- Fecha ao clicar fora
  1787. activeMenuConnection = UserInputService.InputBegan:Connect(function(input)
  1788. if input.UserInputType == Enum.UserInputType.MouseButton1
  1789. or input.UserInputType == Enum.UserInputType.Touch then
  1790. task.defer(function()
  1791. if not ctxMenu.Parent then return end
  1792. local mouse = UserInputService:GetMouseLocation()
  1793. local pos = ctxMenu.AbsolutePosition
  1794. local size = ctxMenu.AbsoluteSize
  1795. if mouse.X < pos.X or mouse.X > pos.X + size.X
  1796. or mouse.Y < pos.Y or mouse.Y > pos.Y + size.Y then
  1797. CloseActiveMenu()
  1798. end
  1799. end)
  1800. end
  1801. end)
  1802. end)
  1803.  
  1804. -- Hover no slot
  1805. slotFrame.MouseEnter:Connect(function()
  1806. UI.Tween(slotFrame, {BackgroundTransparency = 0.05}, 0.12):Play()
  1807. end)
  1808. slotFrame.MouseLeave:Connect(function()
  1809. UI.Tween(slotFrame, {BackgroundTransparency = 0.15}, 0.12):Play()
  1810. end)
  1811.  
  1812. else
  1813. -- === SLOT VAZIO ===
  1814. local emptyLabel = UI.CreateLabel({
  1815. name = "Empty",
  1816. text = string.format(" Slot %d · Vazio", slot),
  1817. size = UDim2.new(1, -12, 1, 0),
  1818. position = UDim2.new(0, 6, 0, 0),
  1819. font = Enum.Font.Gotham,
  1820. textSize = 13,
  1821. textColor = Theme.text_muted,
  1822. parent = slotFrame,
  1823. })
  1824. end
  1825. end
  1826. end
  1827.  
  1828. ----------------------------------------------------------------
  1829. -- SEÇÃO 13 · ATUALIZAÇÃO DA UI DE POSIÇÃO PRINCIPAL
  1830. ----------------------------------------------------------------
  1831. local function UpdateMainPositionUI()
  1832. if State.mainPosition then
  1833. MainPosContainer.Visible = true
  1834. MainPosTooltip.Text = State.mainPosition.name or "Principal"
  1835. else
  1836. MainPosContainer.Visible = false
  1837. end
  1838. end
  1839.  
  1840. ----------------------------------------------------------------
  1841. -- SEÇÃO 14 · ATUALIZAÇÃO DE PAGINAÇÃO
  1842. ----------------------------------------------------------------
  1843. local function UpdatePageUI()
  1844. PageLabel.Text = string.format("Página %d / %d", State.currentPage, State.maxPages)
  1845.  
  1846. -- Atualiza dots
  1847. for i, dot in ipairs(dotInstances) do
  1848. local targetColor = (i == State.currentPage) and Theme.accent or Theme.stroke_dim
  1849. UI.Tween(dot, {BackgroundColor3 = targetColor}, 0.2):Play()
  1850. end
  1851.  
  1852. RenderSlots()
  1853. end
  1854.  
  1855. local function UpdateSlotUI()
  1856. SlotLabel.Text = "Slot: " .. State.currentSlot
  1857. end
  1858.  
  1859. ----------------------------------------------------------------
  1860. -- SEÇÃO 15 · CONEXÕES DE EVENTOS
  1861. ----------------------------------------------------------------
  1862.  
  1863. -- Fechar / Abrir
  1864. CloseBtn.MouseButton1Click:Connect(function()
  1865. UI.Tween(MainFrame, {
  1866. Position = UDim2.new(0.5, 0, 0.5, 20),
  1867. BackgroundTransparency = 1,
  1868. }, 0.25):Play()
  1869. task.delay(0.25, function()
  1870. MainFrame.Visible = false
  1871. MainFrame.Position = UDim2.new(0.5, 0, 0.5, 0)
  1872. MainFrame.BackgroundTransparency = 0.05
  1873. end)
  1874. end)
  1875.  
  1876. OpenBtn.MouseButton1Click:Connect(function()
  1877. MainFrame.Visible = true
  1878. MainFrame.BackgroundTransparency = 1
  1879. MainFrame.Position = UDim2.new(0.5, 0, 0.5, -20)
  1880. UI.Tween(MainFrame, {
  1881. Position = UDim2.new(0.5, 0, 0.5, 0),
  1882. BackgroundTransparency = 0.05,
  1883. }, 0.25):Play()
  1884. end)
  1885.  
  1886. MainFrame:GetPropertyChangedSignal("Visible"):Connect(function()
  1887. OpenBtn.Visible = not MainFrame.Visible
  1888. end)
  1889.  
  1890. -- Paginação
  1891. PagePrev.MouseButton1Click:Connect(function() State:PrevPage() end)
  1892. PageNext.MouseButton1Click:Connect(function() State:NextPage() end)
  1893.  
  1894. -- Slot selector
  1895. SlotPrev.MouseButton1Click:Connect(function() State:PrevSlot() end)
  1896. SlotNext.MouseButton1Click:Connect(function() State:NextSlot() end)
  1897.  
  1898. -- Salvar
  1899. SaveButton.MouseButton1Click:Connect(function()
  1900. local function doSave()
  1901. if not CharacterManager:Refresh() then
  1902. SaveButton.Text = "❌ Sem personagem"
  1903. UI.Tween(SaveButton, {BackgroundColor3 = Theme.danger}, 0.15):Play()
  1904. task.delay(1.5, function()
  1905. if SaveButton.Parent then
  1906. SaveButton.Text = "💾 Salvar Posição"
  1907. UI.Tween(SaveButton, {BackgroundColor3 = Theme.success}, 0.15):Play()
  1908. end
  1909. end)
  1910. return
  1911. end
  1912.  
  1913. local name = NameField.Text
  1914. local success = State:SavePosition(
  1915. State.currentSlot,
  1916. CharacterManager.rootPart.CFrame,
  1917. name
  1918. )
  1919.  
  1920. if success then
  1921. -- Navega para a página do slot salvo
  1922. local savedPage = math.ceil(State.currentSlot / State.slotsPerPage)
  1923. State:SetPage(savedPage)
  1924. State:UpdateSlot()
  1925.  
  1926. SaveButton.Text = "✅ Salvo!"
  1927. UI.Tween(SaveButton, {BackgroundColor3 = Color3.fromRGB(50, 200, 100)}):Play()
  1928. NameField.Text = ""
  1929. else
  1930. SaveButton.Text = "❌ Erro!"
  1931. UI.Tween(SaveButton, {BackgroundColor3 = Theme.danger}):Play()
  1932. end
  1933.  
  1934. task.delay(1.5, function()
  1935. if SaveButton.Parent then
  1936. SaveButton.Text = "💾 Salvar Posição"
  1937. UI.Tween(SaveButton, {BackgroundColor3 = Theme.success}):Play()
  1938. end
  1939. end)
  1940. end
  1941.  
  1942. -- Se o slot já tem dados, pede confirmação
  1943. if State.positions[State.currentSlot] then
  1944. ConfirmLabel.Text = string.format(
  1945. "O Slot %d já contém \"%s\".\nSubstituir?",
  1946. State.currentSlot,
  1947. State.positions[State.currentSlot].name
  1948. )
  1949. ConfirmYes.Text = "Substituir"
  1950. ConfirmYes.MouseButton1Click:Once(function()
  1951. OverlaySystem:Hide(ConfirmOverlay)
  1952. doSave()
  1953. end)
  1954. ConfirmNo.MouseButton1Click:Once(function()
  1955. OverlaySystem:Hide(ConfirmOverlay)
  1956. end)
  1957. OverlaySystem:Show(ConfirmOverlay)
  1958. else
  1959. doSave()
  1960. end
  1961. end)
  1962.  
  1963. -- Posição Principal
  1964. MainPosBtn.MouseButton1Click:Connect(function()
  1965. if not State.mainPosition then return end
  1966.  
  1967. local success = Teleporter:Execute(State.mainPosition.position)
  1968. if success then
  1969. MainPosBtn.Text = "✅"
  1970. UI.Tween(MainPosBtn, {BackgroundColor3 = Theme.success}, 0.15):Play()
  1971. else
  1972. MainPosBtn.Text = "❌"
  1973. UI.Tween(MainPosBtn, {BackgroundColor3 = Theme.danger}, 0.15):Play()
  1974. end
  1975.  
  1976. task.delay(1.2, function()
  1977. if MainPosBtn.Parent then
  1978. MainPosBtn.Text = "🚀"
  1979. UI.Tween(MainPosBtn, {BackgroundColor3 = Theme.bg_primary}, 0.15):Play()
  1980. end
  1981. end)
  1982. end)
  1983.  
  1984. MainPosBtn.MouseEnter:Connect(function()
  1985. if State.mainPosition then
  1986. MainPosTooltip.Visible = true
  1987. MainPosRemove.Visible = true
  1988. end
  1989. local stroke = MainPosBtn:FindFirstChildWhichIsA("UIStroke")
  1990. if stroke then UI.Tween(stroke, {Thickness = 3}, 0.15):Play() end
  1991. end)
  1992.  
  1993. MainPosBtn.MouseLeave:Connect(function()
  1994. MainPosTooltip.Visible = false
  1995. MainPosRemove.Visible = false
  1996. local stroke = MainPosBtn:FindFirstChildWhichIsA("UIStroke")
  1997. if stroke then UI.Tween(stroke, {Thickness = 2}, 0.15):Play() end
  1998. end)
  1999.  
  2000. MainPosRemove.MouseButton1Click:Connect(function()
  2001. ConfirmLabel.Text = "Remover posição principal?"
  2002. ConfirmYes.Text = "Remover"
  2003. ConfirmYes.MouseButton1Click:Once(function()
  2004. State:ClearMainPosition()
  2005. UpdateMainPositionUI()
  2006. RenderSlots()
  2007. OverlaySystem:Hide(ConfirmOverlay)
  2008. end)
  2009. ConfirmNo.MouseButton1Click:Once(function()
  2010. OverlaySystem:Hide(ConfirmOverlay)
  2011. end)
  2012. OverlaySystem:Show(ConfirmOverlay)
  2013. end)
  2014.  
  2015. -- Listener do State
  2016. State:OnChange(function(event)
  2017. if event == "positions_changed" then
  2018. RenderSlots()
  2019. elseif event == "main_changed" then
  2020. UpdateMainPositionUI()
  2021. RenderSlots()
  2022. elseif event == "page_changed" then
  2023. UpdatePageUI()
  2024. elseif event == "slot_changed" then
  2025. UpdateSlotUI()
  2026. end
  2027. end)
  2028.  
  2029. ----------------------------------------------------------------
  2030. -- SEÇÃO 16 · ATALHOS DE TECLADO
  2031. ----------------------------------------------------------------
  2032. table.insert(Connections, UserInputService.InputBegan:Connect(function(input, processed)
  2033. if processed then return end
  2034.  
  2035. -- F9 = Toggle UI
  2036. if input.KeyCode == Enum.KeyCode.F9 then
  2037. MainFrame.Visible = not MainFrame.Visible
  2038. end
  2039.  
  2040. -- Ctrl+S = Salvar rápido no slot atual
  2041. if input.KeyCode == Enum.KeyCode.S and UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) then
  2042. if CharacterManager:Refresh() then
  2043. local success = State:SavePosition(
  2044. State.currentSlot,
  2045. CharacterManager.rootPart.CFrame,
  2046. "Quick Save " .. State.currentSlot
  2047. )
  2048. if success then
  2049. State:UpdateSlot()
  2050. end
  2051. end
  2052. end
  2053.  
  2054. -- Ctrl+Shift+M = Teleportar para posição principal
  2055. if input.KeyCode == Enum.KeyCode.M
  2056. and UserInputService:IsKeyDown(Enum.KeyCode.LeftControl)
  2057. and UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) then
  2058. if State.mainPosition then
  2059. Teleporter:Execute(State.mainPosition.position)
  2060. end
  2061. end
  2062. end))
  2063.  
  2064. ----------------------------------------------------------------
  2065. -- SEÇÃO 17 · INICIALIZAÇÃO
  2066. ----------------------------------------------------------------
  2067. Persistence:Init()
  2068. CharacterManager:Init()
  2069. State:Load()
  2070.  
  2071. -- Renderização inicial
  2072. UpdatePageUI()
  2073. UpdateSlotUI()
  2074. UpdateMainPositionUI()
  2075.  
  2076. -- Auto-save periódico (a cada 60s)
  2077. task.spawn(function()
  2078. while task.wait(60) do
  2079. if ScreenGui.Parent then
  2080. Persistence:SavePositions(State.positions)
  2081. if State.mainPosition then
  2082. Persistence:SaveMainPosition(State.mainPosition)
  2083. end
  2084. else
  2085. break
  2086. end
  2087. end
  2088. end)
  2089.  
  2090. -- Cleanup ao fechar o jogo
  2091. if type(game.BindToClose) == "function" then
  2092. game:BindToClose(function()
  2093. Persistence:SavePositions(State.positions)
  2094. if State.mainPosition then
  2095. Persistence:SaveMainPosition(State.mainPosition)
  2096. end
  2097. end)
  2098. end
  2099.  
  2100. ----------------------------------------------------------------
  2101. -- SEÇÃO 18 · LOG DE INICIALIZAÇÃO
  2102. ----------------------------------------------------------------
  2103. local totalSaved = State:GetSlotCount()
  2104. print("═══════════════════════════════════════════════")
  2105. print(" ✦ Save Locate (NickZ) — V5.0 MEGA UPDATE")
  2106. print("═══════════════════════════════════════════════")
  2107. print(string.format(" 📍 Slots: %d/%d ocupados", totalSaved, State.maxSaves))
  2108. print(string.format(" 📄 Páginas: %d", State.maxPages))
  2109. print(string.format(" 🎮 Executor: %s", Environment.executorName))
  2110. print(string.format(" 💾 FileSystem: %s", Environment.hasFileSystem and "✓" or "✕"))
  2111. print(string.format(" ⭐ Pos. Principal: %s", State.mainPosition and State.mainPosition.name or "Nenhuma"))
  2112. print(" ─────────────────────────────────────────────")
  2113. print(" ⌨️ Atalhos:")
  2114. print(" F9 ─────────── Toggle UI")
  2115. print(" Ctrl+S ──────── Quick Save")
  2116. print(" Ctrl+Shift+M ── TP Principal")
  2117. print("═══════════════════════════════════════════════")
Add Comment
Please, Sign In to add comment