Created
July 26, 2026 03:18
-
-
Save DuyNgao2306/b2445408eef93ea341c06b73df0c6e54 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- [[ VISUAL AUTO CLICKER - OPTIMIZED PERFORMANCE (V0.1) ]] -- | |
| local Players = game:GetService("Players") | |
| local UserInputService = game:GetService("UserInputService") | |
| local VirtualInputManager = game:GetService("VirtualInputManager") | |
| local HttpService = game:GetService("HttpService") | |
| local LocalPlayer = Players.LocalPlayer | |
| local PlayerGui = LocalPlayer:WaitForChild("PlayerGui", 10) | |
| if not PlayerGui then return end | |
| math.randomseed(os.time()) | |
| if PlayerGui:FindFirstChild("MacroSystemGui") then | |
| PlayerGui.MacroSystemGui:Destroy() | |
| end | |
| local pointsData = {} | |
| local MAX_CLICKS = 100 | |
| local currentMode = "None" | |
| local isRunning = false | |
| local isMinimized = false | |
| local connections = {} | |
| local currentlyEditingPoint = nil | |
| local runThread = nil | |
| -- SHARED DRAG STATE VARIABLES (Gộp InputChanged) | |
| local draggingUI, dragStartUI, startPosUI = false, nil, nil | |
| local draggingPoint, dragStartPoint, activeDot = false, nil, nil | |
| -- FORWARD DECLARATIONS | |
| local createWaypoint | |
| local setMode | |
| -- SAFE TABLE CLEAR HELPER | |
| local function safeClear(t) | |
| if table.clear then | |
| table.clear(t) | |
| else | |
| for k in pairs(t) do | |
| t[k] = nil | |
| end | |
| end | |
| end | |
| -- CHECK FILE SYSTEM SUPPORT | |
| local hasFileSupport = (writefile and readfile and isfolder and makefolder) | |
| local folderName = "VisualMacroSlots" | |
| if hasFileSupport then | |
| pcall(function() | |
| if not isfolder(folderName) then | |
| makefolder(folderName) | |
| end | |
| end) | |
| end | |
| -- 1. MAIN GUI CREATION | |
| local ScreenGui = Instance.new("ScreenGui") | |
| ScreenGui.Name = "MacroSystemGui" | |
| ScreenGui.ResetOnSpawn = false | |
| ScreenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling | |
| ScreenGui.DisplayOrder = 999 | |
| ScreenGui.Parent = PlayerGui | |
| local PointsFolder = Instance.new("Folder") | |
| PointsFolder.Name = "PointsFolder" | |
| PointsFolder.Parent = ScreenGui | |
| local MainFrame = Instance.new("Frame") | |
| MainFrame.Name = "MainFrame" | |
| MainFrame.Size = UDim2.new(0, 310, 0, 480) | |
| MainFrame.Position = UDim2.new(0.5, -155, 0.3, 0) | |
| MainFrame.BackgroundColor3 = Color3.fromRGB(25, 27, 38) | |
| MainFrame.BorderSizePixel = 0 | |
| MainFrame.Active = true | |
| MainFrame.Parent = ScreenGui | |
| local UICorner = Instance.new("UICorner") | |
| UICorner.CornerRadius = UDim.new(0, 10) | |
| UICorner.Parent = MainFrame | |
| local UIStroke = Instance.new("UIStroke") | |
| UIStroke.Color = Color3.fromRGB(60, 65, 85) | |
| UIStroke.Thickness = 1.5 | |
| UIStroke.Parent = MainFrame | |
| local Header = Instance.new("Frame") | |
| Header.Size = UDim2.new(1, 0, 0, 38) | |
| Header.BackgroundColor3 = Color3.fromRGB(35, 38, 52) | |
| Header.BorderSizePixel = 0 | |
| Header.Parent = MainFrame | |
| local HeaderCorner = Instance.new("UICorner") | |
| HeaderCorner.CornerRadius = UDim.new(0, 10) | |
| HeaderCorner.Parent = Header | |
| local Title = Instance.new("TextLabel") | |
| Title.Size = UDim2.new(1, -80, 1, 0) | |
| Title.Position = UDim2.new(0, 12, 0, 0) | |
| Title.BackgroundTransparency = 1 | |
| Title.Text = "⚡ VISUAL AUTO CLICKER (V0.1)" | |
| Title.TextColor3 = Color3.fromRGB(255, 255, 255) | |
| Title.TextSize = 13 | |
| Title.Font = Enum.Font.GothamBold | |
| Title.TextXAlignment = Enum.TextXAlignment.Left | |
| Title.Parent = Header | |
| local CloseBtn = Instance.new("TextButton") | |
| CloseBtn.Size = UDim2.new(0, 30, 0, 30) | |
| CloseBtn.Position = UDim2.new(1, -5, 0.5, 0) | |
| CloseBtn.AnchorPoint = Vector2.new(1, 0.5) | |
| CloseBtn.BackgroundTransparency = 1 | |
| CloseBtn.Text = "✕" | |
| CloseBtn.TextColor3 = Color3.fromRGB(255, 71, 87) | |
| CloseBtn.TextSize = 16 | |
| CloseBtn.Font = Enum.Font.GothamBold | |
| CloseBtn.ZIndex = 10 | |
| CloseBtn.Parent = Header | |
| local MinimizeBtn = Instance.new("TextButton") | |
| MinimizeBtn.Size = UDim2.new(0, 30, 0, 30) | |
| MinimizeBtn.Position = UDim2.new(1, -38, 0.5, 0) | |
| MinimizeBtn.AnchorPoint = Vector2.new(1, 0.5) | |
| MinimizeBtn.BackgroundTransparency = 1 | |
| MinimizeBtn.Text = "🗕" | |
| MinimizeBtn.TextColor3 = Color3.fromRGB(255, 234, 167) | |
| MinimizeBtn.TextSize = 16 | |
| MinimizeBtn.Font = Enum.Font.GothamBold | |
| MinimizeBtn.ZIndex = 10 | |
| MinimizeBtn.Parent = Header | |
| local StatusLabel = Instance.new("TextLabel") | |
| StatusLabel.Size = UDim2.new(1, -24, 0, 20) | |
| StatusLabel.Position = UDim2.new(0, 12, 0, 45) | |
| StatusLabel.BackgroundTransparency = 1 | |
| StatusLabel.Text = "Status: Resting" | |
| StatusLabel.TextColor3 = Color3.fromRGB(180, 185, 200) | |
| StatusLabel.TextSize = 13 | |
| StatusLabel.Font = Enum.Font.GothamMedium | |
| StatusLabel.TextXAlignment = Enum.TextXAlignment.Left | |
| StatusLabel.Parent = MainFrame | |
| -- JSON INBOX | |
| local JsonInbox = Instance.new("TextBox") | |
| JsonInbox.Name = "JsonInbox" | |
| JsonInbox.Size = UDim2.new(1, -24, 0, 45) | |
| JsonInbox.Position = UDim2.new(0, 12, 0, 70) | |
| JsonInbox.BackgroundColor3 = Color3.fromRGB(20, 22, 30) | |
| JsonInbox.TextColor3 = Color3.fromRGB(120, 224, 143) | |
| JsonInbox.TextSize = 10 | |
| JsonInbox.Font = Enum.Font.Code | |
| JsonInbox.Text = "" | |
| JsonInbox.PlaceholderText = "Saved JSON Inbox..." | |
| JsonInbox.ClearTextOnFocus = false | |
| JsonInbox.TextEditable = true | |
| JsonInbox.MultiLine = true | |
| JsonInbox.TextWrapped = true | |
| JsonInbox.Parent = MainFrame | |
| Instance.new("UICorner", JsonInbox).CornerRadius = UDim.new(0, 6) | |
| local InboxStroke = Instance.new("UIStroke") | |
| InboxStroke.Color = Color3.fromRGB(50, 55, 75) | |
| InboxStroke.Thickness = 1 | |
| InboxStroke.Parent = JsonInbox | |
| local BtnContainer = Instance.new("Frame") | |
| BtnContainer.Size = UDim2.new(1, -24, 1, -170) | |
| BtnContainer.Position = UDim2.new(0, 12, 0, 122) | |
| BtnContainer.BackgroundTransparency = 1 | |
| BtnContainer.Parent = MainFrame | |
| local GridLayout = Instance.new("UIGridLayout") | |
| GridLayout.CellSize = UDim2.new(0.5, -5, 0, 35) | |
| GridLayout.CellPadding = UDim2.new(0, 10, 0, 8) | |
| GridLayout.SortOrder = Enum.SortOrder.LayoutOrder | |
| GridLayout.Parent = BtnContainer | |
| local function createButton(name, text, color, order) | |
| local btn = Instance.new("TextButton") | |
| btn.Name = name | |
| btn.BackgroundColor3 = color | |
| btn.Text = text | |
| btn.TextColor3 = Color3.fromRGB(255, 255, 255) | |
| btn.Font = Enum.Font.GothamBold | |
| btn.TextSize = 12 | |
| btn.LayoutOrder = order | |
| btn.Parent = BtnContainer | |
| local corner = Instance.new("UICorner") | |
| corner.CornerRadius = UDim.new(0, 6) | |
| corner.Parent = btn | |
| return btn | |
| end | |
| local BtnAdd = createButton("BtnAdd", "➕ Add Point", Color3.fromRGB(108, 92, 231), 1) | |
| local BtnMove = createButton("BtnMove", "🖐️ Move Point", Color3.fromRGB(225, 112, 85), 2) | |
| local BtnTime = createButton("BtnTime", "⏱️ Edit Time", Color3.fromRGB(9, 132, 227), 3) | |
| local BtnDelOne = createButton("BtnDelOne", "❌ Delete One", Color3.fromRGB(214, 48, 49), 4) | |
| local BtnSave = createButton("BtnSave", "💾 Save Script", Color3.fromRGB(0, 206, 201), 5) | |
| local BtnLoad = createButton("BtnLoad", "📥 Load Script", Color3.fromRGB(46, 204, 113), 6) | |
| local BtnDataStore = createButton("BtnDataStore", "📦 Data Store", Color3.fromRGB(238, 82, 83), 7) | |
| local BtnClear = createButton("BtnClear", "🗑️ Clear All", Color3.fromRGB(116, 125, 140), 8) | |
| local BtnStart = createButton("BtnStart", "▶️ START MACRO", Color3.fromRGB(0, 184, 148), 9) | |
| BtnStart.Size = UDim2.new(1, 0, 0, 40) | |
| GridLayout.FillDirectionMaxCells = 2 | |
| -- 2. TIME POPUP | |
| local TimePopup = Instance.new("Frame") | |
| TimePopup.Size = UDim2.new(0, 200, 0, 120) | |
| TimePopup.Position = UDim2.new(0.5, 0, 0.5, 0) | |
| TimePopup.AnchorPoint = Vector2.new(0.5, 0.5) | |
| TimePopup.BackgroundColor3 = Color3.fromRGB(45, 52, 54) | |
| TimePopup.Visible = false | |
| TimePopup.ZIndex = 50 | |
| TimePopup.Parent = ScreenGui | |
| Instance.new("UICorner", TimePopup).CornerRadius = UDim.new(0, 10) | |
| local PopupLabel = Instance.new("TextLabel") | |
| PopupLabel.Size = UDim2.new(1, 0, 0, 30); PopupLabel.Position = UDim2.new(0,0,0,10); PopupLabel.BackgroundTransparency = 1 | |
| PopupLabel.Text = "Enter delay time (sec):" ; PopupLabel.TextColor3 = Color3.new(1,1,1); PopupLabel.Font = Enum.Font.GothamBold; PopupLabel.Parent = TimePopup | |
| local TimeInput = Instance.new("TextBox") | |
| TimeInput.Size = UDim2.new(0.8, 0, 0, 30); TimeInput.Position = UDim2.new(0.1, 0, 0, 40); TimeInput.BackgroundColor3 = Color3.fromRGB(20,20,20); TimeInput.TextColor3 = Color3.new(1,1,1) | |
| TimeInput.Text = "0.1"; TimeInput.Font = Enum.Font.GothamMedium; TimeInput.Parent = TimePopup | |
| local PopupSave = Instance.new("TextButton") | |
| PopupSave.Size = UDim2.new(0.8, 0, 0, 30); PopupSave.Position = UDim2.new(0.1, 0, 0, 80); PopupSave.BackgroundColor3 = Color3.fromRGB(0, 184, 148); PopupSave.Text = "SAVE"; PopupSave.TextColor3 = Color3.new(1,1,1); PopupSave.Font = Enum.Font.GothamBold; PopupSave.Parent = TimePopup; Instance.new("UICorner", PopupSave) | |
| -- 2.1. LOAD POPUP | |
| local LoadPopup = Instance.new("Frame") | |
| LoadPopup.Size = UDim2.new(0, 240, 0, 150) | |
| LoadPopup.Position = UDim2.new(0.5, 0, 0.5, 0) | |
| LoadPopup.AnchorPoint = Vector2.new(0.5, 0.5) | |
| LoadPopup.BackgroundColor3 = Color3.fromRGB(45, 52, 54) | |
| LoadPopup.Visible = false | |
| LoadPopup.ZIndex = 50 | |
| LoadPopup.Parent = ScreenGui | |
| Instance.new("UICorner", LoadPopup).CornerRadius = UDim.new(0, 10) | |
| local LoadPopupLabel = Instance.new("TextLabel") | |
| LoadPopupLabel.Size = UDim2.new(1, 0, 0, 30); LoadPopupLabel.Position = UDim2.new(0, 0, 0, 10); LoadPopupLabel.BackgroundTransparency = 1 | |
| LoadPopupLabel.Text = "Paste JSON Script here:"; LoadPopupLabel.TextColor3 = Color3.new(1,1,1); LoadPopupLabel.Font = Enum.Font.GothamBold; LoadPopupLabel.TextSize = 12; LoadPopupLabel.Parent = LoadPopup | |
| local LoadInput = Instance.new("TextBox") | |
| LoadInput.Size = UDim2.new(0.85, 0, 0, 45); LoadInput.Position = UDim2.new(0.075, 0, 0, 45); LoadInput.BackgroundColor3 = Color3.fromRGB(20,20,20); LoadInput.TextColor3 = Color3.new(1,1,1) | |
| LoadInput.Text = ""; LoadInput.PlaceholderText = "Paste JSON string here..."; LoadInput.Font = Enum.Font.GothamMedium; LoadInput.TextSize = 10; LoadInput.ClearTextOnFocus = false; LoadInput.Parent = LoadPopup | |
| Instance.new("UICorner", LoadInput).CornerRadius = UDim.new(0, 6) | |
| local PopupLoadBtn = Instance.new("TextButton") | |
| PopupLoadBtn.Size = UDim2.new(0.85, 0, 0, 30); PopupLoadBtn.Position = UDim2.new(0.075, 0, 0, 100); PopupLoadBtn.BackgroundColor3 = Color3.fromRGB(46, 204, 113) | |
| PopupLoadBtn.Text = "CONFIRM LOAD"; PopupLoadBtn.TextColor3 = Color3.new(1,1,1); PopupLoadBtn.Font = Enum.Font.GothamBold; PopupLoadBtn.TextSize = 12; PopupLoadBtn.Parent = LoadPopup | |
| Instance.new("UICorner", PopupLoadBtn).CornerRadius = UDim.new(0, 6) | |
| -- 2.2. DATA STORE POPUP | |
| local DataStorePopup = Instance.new("Frame") | |
| DataStorePopup.Size = UDim2.new(0, 280, 0, 260) | |
| DataStorePopup.Position = UDim2.new(0.5, 0, 0.5, 0) | |
| DataStorePopup.AnchorPoint = Vector2.new(0.5, 0.5) | |
| DataStorePopup.BackgroundColor3 = Color3.fromRGB(35, 38, 52) | |
| DataStorePopup.Visible = false | |
| DataStorePopup.ZIndex = 60 | |
| DataStorePopup.Parent = ScreenGui | |
| Instance.new("UICorner", DataStorePopup).CornerRadius = UDim.new(0, 10) | |
| Instance.new("UIStroke", DataStorePopup).Color = Color3.fromRGB(80, 90, 120) | |
| local DSHeader = Instance.new("TextLabel") | |
| DSHeader.Size = UDim2.new(1, 0, 0, 35) | |
| DSHeader.BackgroundTransparency = 1 | |
| DSHeader.Text = "📦 JSON Data Store (5 Slots)" | |
| DSHeader.TextColor3 = Color3.fromRGB(255, 255, 255) | |
| DSHeader.Font = Enum.Font.GothamBold | |
| DSHeader.TextSize = 12 | |
| DSHeader.Parent = DataStorePopup | |
| local DSCloseBtn = Instance.new("TextButton") | |
| DSCloseBtn.Size = UDim2.new(0, 30, 0, 30) | |
| DSCloseBtn.Position = UDim2.new(1, -5, 0, 2) | |
| DSCloseBtn.BackgroundTransparency = 1 | |
| DSCloseBtn.Text = "✕" | |
| DSCloseBtn.TextColor3 = Color3.fromRGB(255, 71, 87) | |
| DSCloseBtn.TextSize = 14 | |
| DSCloseBtn.Font = Enum.Font.GothamBold | |
| DSCloseBtn.Parent = DataStorePopup | |
| local SlotContainer = Instance.new("ScrollingFrame") | |
| SlotContainer.Size = UDim2.new(1, -16, 1, -45) | |
| SlotContainer.Position = UDim2.new(0, 8, 0, 38) | |
| SlotContainer.BackgroundTransparency = 1 | |
| SlotContainer.BorderSizePixel = 0 | |
| SlotContainer.CanvasSize = UDim2.new(0, 0, 0, 5 * 40 + 10) | |
| SlotContainer.ScrollBarThickness = 4 | |
| SlotContainer.Parent = DataStorePopup | |
| local UIListLayout = Instance.new("UIListLayout") | |
| UIListLayout.SortOrder = Enum.SortOrder.LayoutOrder | |
| UIListLayout.Padding = UDim.new(0, 6) | |
| UIListLayout.Parent = SlotContainer | |
| local slotRows = {} | |
| local function getSlotFilePath(slotIndex) | |
| return folderName .. "/slot_" .. slotIndex .. ".json" | |
| end | |
| local function refreshSlotUI() | |
| if not hasFileSupport then return end | |
| for i = 1, 5 do | |
| local filePath = getSlotFilePath(i) | |
| local hasData = false | |
| pcall(function() | |
| if isfile and isfile(filePath) then | |
| local content = readfile(filePath) | |
| if content and content ~= "" then | |
| hasData = true | |
| end | |
| end | |
| end) | |
| if slotRows[i] then | |
| if hasData then | |
| slotRows[i].label.Text = "Slot " .. i .. " [Saved]" | |
| slotRows[i].label.TextColor3 = Color3.fromRGB(120, 224, 143) | |
| else | |
| slotRows[i].label.Text = "Slot " .. i .. " [Empty]" | |
| slotRows[i].label.TextColor3 = Color3.fromRGB(180, 185, 200) | |
| end | |
| end | |
| end | |
| end | |
| for i = 1, 5 do | |
| local row = Instance.new("Frame") | |
| row.Size = UDim2.new(1, 0, 0, 34) | |
| row.BackgroundColor3 = Color3.fromRGB(20, 22, 30) | |
| row.LayoutOrder = i | |
| row.Parent = SlotContainer | |
| Instance.new("UICorner", row).CornerRadius = UDim.new(0, 6) | |
| local label = Instance.new("TextLabel") | |
| label.Size = UDim2.new(0, 75, 1, 0) | |
| label.Position = UDim2.new(0, 6, 0, 0) | |
| label.BackgroundTransparency = 1 | |
| label.Text = "Slot " .. i .. " [Empty]" | |
| label.TextColor3 = Color3.fromRGB(180, 185, 200) | |
| label.Font = Enum.Font.GothamBold | |
| label.TextSize = 11 | |
| label.TextXAlignment = Enum.TextXAlignment.Left | |
| label.Parent = row | |
| local saveBtn = Instance.new("TextButton") | |
| saveBtn.Size = UDim2.new(0, 48, 0, 24) | |
| saveBtn.Position = UDim2.new(1, -156, 0.5, -12) | |
| saveBtn.BackgroundColor3 = Color3.fromRGB(0, 206, 201) | |
| saveBtn.Text = "Save" | |
| saveBtn.TextColor3 = Color3.fromRGB(255, 255, 255) | |
| saveBtn.Font = Enum.Font.GothamBold | |
| saveBtn.TextSize = 10 | |
| saveBtn.Parent = row | |
| Instance.new("UICorner", saveBtn).CornerRadius = UDim.new(0, 4) | |
| local loadBtn = Instance.new("TextButton") | |
| loadBtn.Size = UDim2.new(0, 48, 0, 24) | |
| loadBtn.Position = UDim2.new(1, -104, 0.5, -12) | |
| loadBtn.BackgroundColor3 = Color3.fromRGB(46, 204, 113) | |
| loadBtn.Text = "Load" | |
| loadBtn.TextColor3 = Color3.fromRGB(255, 255, 255) | |
| loadBtn.Font = Enum.Font.GothamBold | |
| loadBtn.TextSize = 10 | |
| loadBtn.Parent = row | |
| Instance.new("UICorner", loadBtn).CornerRadius = UDim.new(0, 4) | |
| local delBtn = Instance.new("TextButton") | |
| delBtn.Size = UDim2.new(0, 48, 0, 24) | |
| delBtn.Position = UDim2.new(1, -52, 0.5, -12) | |
| delBtn.BackgroundColor3 = Color3.fromRGB(214, 48, 49) | |
| delBtn.Text = "Del" | |
| delBtn.TextColor3 = Color3.fromRGB(255, 255, 255) | |
| delBtn.Font = Enum.Font.GothamBold | |
| delBtn.TextSize = 10 | |
| delBtn.Parent = row | |
| Instance.new("UICorner", delBtn).CornerRadius = UDim.new(0, 4) | |
| slotRows[i] = {label = label} | |
| saveBtn.Activated:Connect(function() | |
| if not hasFileSupport then return end | |
| if #pointsData == 0 then return end | |
| local viewport = workspace.CurrentCamera.ViewportSize | |
| local exportTable = {} | |
| for _, data in ipairs(pointsData) do | |
| if data.frame and data.frame.Parent then | |
| local posX = data.frame.AbsolutePosition.X + (data.frame.AbsoluteSize.X / 2) | |
| local posY = data.frame.AbsolutePosition.Y + (data.frame.AbsoluteSize.Y / 2) | |
| table.insert(exportTable, {scaleX = posX / viewport.X, scaleY = posY / viewport.Y, delay = data.delay}) | |
| end | |
| end | |
| pcall(function() | |
| local jsonString = HttpService:JSONEncode(exportTable) | |
| writefile(getSlotFilePath(i), jsonString) | |
| JsonInbox.Text = jsonString | |
| end) | |
| refreshSlotUI() | |
| end) | |
| loadBtn.Activated:Connect(function() | |
| if isRunning or not hasFileSupport then return end | |
| local filePath = getSlotFilePath(i) | |
| pcall(function() | |
| if isfile and isfile(filePath) then | |
| local rawText = readfile(filePath) | |
| if not rawText or rawText == "" then return end | |
| local decodedData = HttpService:JSONDecode(rawText) | |
| if type(decodedData) == "table" and #decodedData > 0 then | |
| for _, data in ipairs(pointsData) do | |
| if data.frame then data.frame:Destroy() end | |
| end | |
| safeClear(pointsData) | |
| setMode("None") | |
| local viewport = workspace.CurrentCamera.ViewportSize | |
| for _, item in ipairs(decodedData) do | |
| local px, py | |
| if item.scaleX and item.scaleY then | |
| px = item.scaleX * viewport.X | |
| py = item.scaleY * viewport.Y | |
| elseif item.x and item.y then | |
| px = item.x | |
| py = item.y | |
| end | |
| if px and py then | |
| createWaypoint(px, py, item.delay or 0.1) | |
| end | |
| end | |
| JsonInbox.Text = rawText | |
| end | |
| end | |
| end) | |
| DataStorePopup.Visible = false | |
| end) | |
| delBtn.Activated:Connect(function() | |
| if not hasFileSupport then return end | |
| local filePath = getSlotFilePath(i) | |
| pcall(function() | |
| if delfile and isfile and isfile(filePath) then | |
| delfile(filePath) | |
| end | |
| end) | |
| refreshSlotUI() | |
| end) | |
| end | |
| -- 3. CENTRALIZED DRAG & INPUT SYSTEM (Gộp InputChanged) | |
| table.insert(connections, Header.InputBegan:Connect(function(input) | |
| if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then | |
| draggingUI = true; dragStartUI = input.Position; startPosUI = MainFrame.Position | |
| end | |
| end)) | |
| table.insert(connections, UserInputService.InputChanged:Connect(function(input) | |
| if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then | |
| if draggingUI then | |
| local delta = input.Position - dragStartUI | |
| MainFrame.Position = UDim2.new(startPosUI.X.Scale, startPosUI.X.Offset + delta.X, startPosUI.Y.Scale, startPosUI.Y.Offset + delta.Y) | |
| elseif draggingPoint and activeDot and currentMode == "Move" then | |
| local delta = input.Position - dragStartPoint | |
| local newX = math.clamp(activeDot.Position.X.Offset + delta.X, 20, workspace.CurrentCamera.ViewportSize.X - 20) | |
| local newY = math.clamp(activeDot.Position.Y.Offset + delta.Y, 20, workspace.CurrentCamera.ViewportSize.Y - 20) | |
| activeDot.Position = UDim2.new(0, newX, 0, newY) | |
| dragStartPoint = input.Position | |
| end | |
| end | |
| end)) | |
| table.insert(connections, UserInputService.InputEnded:Connect(function(input) | |
| if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then | |
| draggingUI = false | |
| draggingPoint = false | |
| activeDot = nil | |
| end | |
| end)) | |
| MinimizeBtn.Activated:Connect(function() | |
| isMinimized = not isMinimized | |
| if isMinimized then | |
| MinimizeBtn.Text = "🗖" | |
| MainFrame.Size = UDim2.new(0, 310, 0, 38) | |
| BtnContainer.Visible = false | |
| StatusLabel.Visible = false | |
| JsonInbox.Visible = false | |
| TimePopup.Visible = false | |
| LoadPopup.Visible = false | |
| DataStorePopup.Visible = false | |
| else | |
| MinimizeBtn.Text = "🗕" | |
| MainFrame.Size = UDim2.new(0, 310, 0, 480) | |
| BtnContainer.Visible = true | |
| StatusLabel.Visible = true | |
| JsonInbox.Visible = true | |
| end | |
| end) | |
| local function isClickingOnMacroUI(clickPos) | |
| local frames = {MainFrame, TimePopup, LoadPopup, DataStorePopup} | |
| for _, frame in ipairs(frames) do | |
| if frame.Visible then | |
| local pos, size = frame.AbsolutePosition, frame.AbsoluteSize | |
| if clickPos.X >= pos.X and clickPos.X <= (pos.X + size.X) and clickPos.Y >= pos.Y and clickPos.Y <= (pos.Y + size.Y) then | |
| return true | |
| end | |
| end | |
| end | |
| return false | |
| end | |
| -- DEFINITION OF setMode | |
| function setMode(mode) | |
| if isRunning then return end | |
| currentMode = (currentMode == mode) and "None" or mode | |
| BtnAdd.BackgroundColor3 = (currentMode == "Add") and Color3.fromRGB(162, 155, 254) or Color3.fromRGB(108, 92, 231) | |
| BtnMove.BackgroundColor3 = (currentMode == "Move") and Color3.fromRGB(250, 177, 160) or Color3.fromRGB(225, 112, 85) | |
| BtnTime.BackgroundColor3 = (currentMode == "Time") and Color3.fromRGB(116, 185, 255) or Color3.fromRGB(9, 132, 227) | |
| BtnDelOne.BackgroundColor3 = (currentMode == "DeleteOne") and Color3.fromRGB(255, 107, 107) or Color3.fromRGB(214, 48, 49) | |
| TimePopup.Visible = false | |
| LoadPopup.Visible = false | |
| DataStorePopup.Visible = false | |
| if currentMode == "None" then StatusLabel.Text = "Status: Resting" | |
| elseif currentMode == "Add" then StatusLabel.Text = "Status: Click screen to place point" | |
| elseif currentMode == "Move" then StatusLabel.Text = "Status: Drag points to move" | |
| elseif currentMode == "Time" then StatusLabel.Text = "Status: Click point to edit time" | |
| elseif currentMode == "DeleteOne" then StatusLabel.Text = "Status: Click point to DELETE" end | |
| end | |
| local function refreshWaypointIndices() | |
| for index, data in ipairs(pointsData) do | |
| data.id = index | |
| if data.numLabel then | |
| data.numLabel.Text = tostring(index) | |
| end | |
| end | |
| end | |
| -- 4. WAYPOINT CREATION (Gọn nhẹ hóa UI waypoint) | |
| function createWaypoint(x, y, customDelay) | |
| if #pointsData >= MAX_CLICKS then return end | |
| local id = #pointsData + 1 | |
| local pointDelay = customDelay or 0.1 | |
| local Dot = Instance.new("Frame") | |
| Dot.Size = UDim2.new(0, 30, 0, 30) | |
| Dot.Position = UDim2.new(0, x, 0, y) | |
| Dot.AnchorPoint = Vector2.new(0.5, 0.5) | |
| Dot.BackgroundColor3 = Color3.fromRGB(255, 159, 67) | |
| Dot.BorderSizePixel = 0 | |
| Dot.ZIndex = 50 | |
| Dot.Parent = PointsFolder | |
| Instance.new("UICorner", Dot).CornerRadius = UDim.new(1, 0) | |
| local NumLabel = Instance.new("TextLabel"); NumLabel.Size = UDim2.new(1, 0, 1, 0); NumLabel.BackgroundTransparency = 1; NumLabel.Text = tostring(id); NumLabel.TextColor3 = Color3.new(1,1,1); NumLabel.Font = Enum.Font.GothamBold; NumLabel.TextSize = 14; NumLabel.Parent = Dot | |
| local TimeLabel = Instance.new("TextLabel"); TimeLabel.Size = UDim2.new(0, 40, 0, 16); TimeLabel.Position = UDim2.new(0.5, -20, 1, 1); TimeLabel.BackgroundTransparency = 1; TimeLabel.Text = tostring(pointDelay).."s"; TimeLabel.TextColor3 = Color3.fromRGB(85, 239, 196); TimeLabel.Font = Enum.Font.GothamBold; TimeLabel.TextSize = 10; TimeLabel.Parent = Dot | |
| local data = {id = id, frame = Dot, delay = pointDelay, timeLabel = TimeLabel, numLabel = NumLabel} | |
| table.insert(pointsData, data) | |
| table.insert(connections, Dot.InputBegan:Connect(function(input) | |
| if isRunning or not Dot.Parent then return end | |
| if currentMode == "Move" and (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) then | |
| draggingPoint = true | |
| dragStartPoint = input.Position | |
| activeDot = Dot | |
| elseif currentMode == "Time" and (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) then | |
| currentlyEditingPoint = data | |
| TimeInput.Text = tostring(data.delay) | |
| TimePopup.Visible = true | |
| elseif currentMode == "DeleteOne" and (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) then | |
| Dot:Destroy() | |
| for i, v in ipairs(pointsData) do | |
| if v == data then | |
| table.remove(pointsData, i) | |
| break | |
| end | |
| end | |
| refreshWaypointIndices() | |
| end | |
| end)) | |
| end | |
| PopupSave.Activated:Connect(function() | |
| if currentlyEditingPoint and currentlyEditingPoint.frame and currentlyEditingPoint.frame.Parent then | |
| local newTime = tonumber(TimeInput.Text) | |
| if newTime and newTime >= 0.01 then | |
| currentlyEditingPoint.delay = newTime | |
| currentlyEditingPoint.timeLabel.Text = tostring(newTime) .. "s" | |
| end | |
| end | |
| TimePopup.Visible = false | |
| end) | |
| table.insert(connections, UserInputService.InputBegan:Connect(function(input) | |
| if currentMode == "Add" and (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) then | |
| if not isClickingOnMacroUI(input.Position) then createWaypoint(input.Position.X, input.Position.Y) end | |
| end | |
| end)) | |
| BtnAdd.Activated:Connect(function() setMode("Add") end) | |
| BtnMove.Activated:Connect(function() setMode("Move") end) | |
| BtnTime.Activated:Connect(function() setMode("Time") end) | |
| BtnDelOne.Activated:Connect(function() setMode("DeleteOne") end) | |
| -- SAVE SCRIPT | |
| BtnSave.Activated:Connect(function() | |
| if #pointsData == 0 then return end | |
| local viewport = workspace.CurrentCamera.ViewportSize | |
| local exportTable = {} | |
| for _, data in ipairs(pointsData) do | |
| if data.frame and data.frame.Parent then | |
| local posX = data.frame.AbsolutePosition.X + (data.frame.AbsoluteSize.X / 2) | |
| local posY = data.frame.AbsolutePosition.Y + (data.frame.AbsoluteSize.Y / 2) | |
| table.insert(exportTable, {scaleX = posX / viewport.X, scaleY = posY / viewport.Y, delay = data.delay}) | |
| end | |
| end | |
| pcall(function() | |
| JsonInbox.Text = HttpService:JSONEncode(exportTable) | |
| end) | |
| end) | |
| -- LOAD SCRIPT | |
| BtnLoad.Activated:Connect(function() | |
| if isRunning then return end | |
| TimePopup.Visible = false | |
| DataStorePopup.Visible = false | |
| LoadPopup.Visible = not LoadPopup.Visible | |
| end) | |
| BtnDataStore.Activated:Connect(function() | |
| if isRunning or not hasFileSupport then return end | |
| TimePopup.Visible = false | |
| LoadPopup.Visible = false | |
| refreshSlotUI() | |
| DataStorePopup.Visible = not DataStorePopup.Visible | |
| end) | |
| DSCloseBtn.Activated:Connect(function() | |
| DataStorePopup.Visible = false | |
| end) | |
| PopupLoadBtn.Activated:Connect(function() | |
| local rawText = LoadInput.Text | |
| if rawText == "" then | |
| LoadPopup.Visible = false | |
| return | |
| end | |
| pcall(function() | |
| local decodedData = HttpService:JSONDecode(rawText) | |
| if type(decodedData) == "table" and #decodedData > 0 then | |
| for _, data in ipairs(pointsData) do | |
| if data.frame then data.frame:Destroy() end | |
| end | |
| safeClear(pointsData) | |
| setMode("None") | |
| local viewport = workspace.CurrentCamera.ViewportSize | |
| for _, item in ipairs(decodedData) do | |
| local px, py | |
| if item.scaleX and item.scaleY then | |
| px = item.scaleX * viewport.X | |
| py = item.scaleY * viewport.Y | |
| elseif item.x and item.y then | |
| px = item.x | |
| py = item.y | |
| end | |
| if px and py then | |
| createWaypoint(px, py, item.delay or 0.1) | |
| end | |
| end | |
| JsonInbox.Text = rawText | |
| end | |
| end) | |
| LoadPopup.Visible = false | |
| end) | |
| BtnClear.Activated:Connect(function() | |
| if isRunning then return end | |
| for _, data in ipairs(pointsData) do | |
| if data.frame then data.frame:Destroy() end | |
| end | |
| safeClear(pointsData) | |
| setMode("None") | |
| JsonInbox.Text = "" | |
| end) | |
| -- 5. RUN MACRO (ĐÃ GỌP INPUT & BỎ MOUSEMOVEABS / BỎ CHẠY SONG SONG MOUSE+TOUCH) | |
| BtnStart.Activated:Connect(function() | |
| if #pointsData == 0 then return end | |
| if isRunning then | |
| isRunning = false | |
| if runThread then | |
| task.cancel(runThread) | |
| runThread = nil | |
| end | |
| BtnStart.Text = "▶️ START MACRO" | |
| BtnStart.BackgroundColor3 = Color3.fromRGB(0, 184, 148) | |
| setMode("None") | |
| return | |
| end | |
| isRunning = true | |
| currentMode = "Run" | |
| TimePopup.Visible = false | |
| LoadPopup.Visible = false | |
| DataStorePopup.Visible = false | |
| BtnStart.Text = "⏹️ STOP MACRO" | |
| BtnStart.BackgroundColor3 = Color3.fromRGB(214, 48, 49) | |
| runThread = task.spawn(function() | |
| local isMobile = UserInputService.TouchEnabled and not UserInputService.KeyboardEnabled | |
| while isRunning and ScreenGui and ScreenGui.Parent do | |
| for _, data in ipairs(pointsData) do | |
| if not isRunning or not ScreenGui or not ScreenGui.Parent then break end | |
| if data.frame and data.frame.Parent then | |
| pcall(function() data.frame.BackgroundColor3 = Color3.fromRGB(255, 71, 87) end) | |
| local clickX = data.frame.AbsolutePosition.X + (data.frame.AbsoluteSize.X / 2) | |
| local clickY = data.frame.AbsolutePosition.Y + (data.frame.AbsoluteSize.Y / 2) | |
| -- THỰC THI CLICK ĐƠN LẺ KHÔNG DÙNG MOUSEMOVEABS & KHÔNG CHẠY SONG SONG | |
| pcall(function() | |
| if isMobile then | |
| VirtualInputManager:SendTouchEvent(data.id, Enum.UserInputState.Begin, clickX, clickY) | |
| task.wait(0.03) | |
| VirtualInputManager:SendTouchEvent(data.id, Enum.UserInputState.End, clickX, clickY) | |
| else | |
| VirtualInputManager:SendMouseButtonEvent(clickX, clickY, 0, true, game, 1) | |
| task.wait(0.03) | |
| VirtualInputManager:SendMouseButtonEvent(clickX, clickY, 0, false, game, 1) | |
| end | |
| end) | |
| pcall(function() data.frame.BackgroundColor3 = Color3.fromRGB(255, 159, 67) end) | |
| -- TỐI ƯU HÓA STATUS UPDATE (Giảm tải gọi liên tục mỗi 0.1s) | |
| local remainingTime = data.delay | |
| local lastSec = -1 | |
| while remainingTime > 0 and isRunning and ScreenGui and ScreenGui.Parent do | |
| local currentSec = math.ceil(remainingTime) | |
| if not isMinimized and currentSec ~= lastSec then | |
| lastSec = currentSec | |
| pcall(function() | |
| StatusLabel.Text = string.format("Run [%d] - %.1fs", data.id, math.max(0, remainingTime)) | |
| end) | |
| end | |
| local step = math.min(0.2, remainingTime) | |
| task.wait(step) | |
| remainingTime = remainingTime - step | |
| end | |
| end | |
| end | |
| task.wait(0.01) | |
| end | |
| isRunning = false | |
| runThread = nil | |
| pcall(function() | |
| StatusLabel.Text = "Status: Resting" | |
| BtnStart.Text = "▶️ START MACRO" | |
| BtnStart.BackgroundColor3 = Color3.fromRGB(0, 184, 148) | |
| end) | |
| end) | |
| end) | |
| -- CLEANUP ON CLOSE | |
| CloseBtn.Activated:Connect(function() | |
| isRunning = false | |
| if runThread then | |
| task.cancel(runThread) | |
| runThread = nil | |
| end | |
| for _, conn in ipairs(connections) do | |
| if conn.Connected then conn:Disconnect() end | |
| end | |
| safeClear(connections) | |
| for _, data in ipairs(pointsData) do | |
| if data.frame then data.frame:Destroy() end | |
| end | |
| safeClear(pointsData) | |
| ScreenGui:Destroy() | |
| end) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment