-- VARIABLES GLOBALES local player = game.Players.LocalPlayer local camera = workspace.CurrentCamera local holding, target, holdDistance, anchoredWhileHolding = false, nil, 10, false local run, UIS = game:GetService("RunService"), game:GetService("UserInputService") -- Fuerzas físicas local vel = Instance.new("BodyVelocity") vel.MaxForce = Vector3.new(1, 1, 1) * 1e5 vel.P = 12500 local gyro = Instance.new("BodyGyro") gyro.MaxTorque = Vector3.new(1, 1, 1) * 1e6 gyro.P = 3000 local originalRotation = nil -- Contorno visual (SelectionBox) local SelectionBox = Instance.new("SelectionBox") SelectionBox.Name = "Contorno" SelectionBox.LineThickness = 0.08 SelectionBox.Parent = game:GetService("CoreGui") -- Imán local imanActivo, radioIman, fuerzaIman = false, 30, 200 local objetosIman, gravityBackup = {}, {} local actualRadio = radioIman -- Círculo visual avanzado (se usará también para representar el radio sobre la UI) local usingDrawing = Drawing and Drawing.new and typeof(Drawing.new)=="function" local imancircle, circleGui, circleImg local circleColor = Color3.fromRGB(0,170,255) local surfacePos, surfaceNormal = Vector3.zero, Vector3.new(0,1,0) if usingDrawing then imancircle = Drawing.new("Circle") imancircle.Visible, imancircle.Transparency, imancircle.Color, imancircle.Thickness, imancircle.Filled = false, 1, circleColor, 2, false else circleGui = Instance.new("BillboardGui") circleGui.Name, circleGui.Size, circleGui.SizeOffset, circleGui.AlwaysOnTop = "ImanCircleGui", UDim2.new(2,0,2,0), Vector2.new(0,0), true circleGui.Parent = workspace circleImg = Instance.new("ImageLabel") circleImg.BackgroundTransparency, circleImg.Image, circleImg.ImageColor3 = 1, "rbxassetid://13523341990", circleColor circleImg.AnchorPoint, circleImg.Position, circleImg.Size = Vector2.new(0.5,0.5), UDim2.fromScale(0.5,0.5), UDim2.fromScale(1,1) circleImg.Parent, circleGui.Enabled = circleGui, false end -- Controles móviles / PC local holdLeft, holdRight, holdQ, holdE = false, false, false, false local usingGamepad = UIS.GamepadEnabled UIS.GamepadConnected:Connect(function() usingGamepad = true end) UIS.GamepadDisconnected:Connect(function() usingGamepad = false end) -- Virtual mouse (pixel coordinates) local virtualMousePos = nil -- Vector2 in pixels; nil means not set yet local virtualMouseCenter = Vector2.new(0.5, 0.45) -- center fraction (x,y) relative to screen (center a bit arriba) local virtualMouseSize = 60 -- pixels diameter for visual (reduced size) local virtualMouseEnabled = UIS.TouchEnabled -- enable virtual mouse only on touch devices -- GUI references to update states from anywhere local GUIrefs = {} -- Utilidades local function esParteDePersonaje(obj) for _, plr in ipairs(game.Players:GetPlayers()) do if plr.Character and obj:IsDescendantOf(plr.Character) then return true end end return false end local function tieneSoldaduraNoAnclada(obj) for _, c in ipairs(obj:GetConnectedParts()) do if c ~= obj and not c.Anchored then return true end end return false end local function restaurarGravedad() for obj, oldGravity in pairs(gravityBackup) do if obj and obj:IsDescendantOf(workspace) then obj.CustomPhysicalProperties = oldGravity end end gravityBackup = {} end local function lerpVec3(a, b, t) return a + (b - a) * t end -- Screen size helpers local function getScreenSize() local vs = camera.ViewportSize return vs.X, vs.Y end local function computeVirtualCenterPixels() local sx, sy = getScreenSize() return Vector2.new(sx * virtualMouseCenter.X, sy * virtualMouseCenter.Y) end -- Initialize virtualMousePos to center if touch device if virtualMouseEnabled then virtualMousePos = computeVirtualCenterPixels() end -- Estado de ocultado de la GUI local guiHidden = false -- getInputPosition: prioritize virtual mouse on touch devices, otherwise use real mouse (player:GetMouse for accurate viewport coords) local function getInputPosition() -- If touch & virtual mouse enabled -> use virtual position if UIS.TouchEnabled and virtualMouseEnabled and virtualMousePos then return virtualMousePos.X, virtualMousePos.Y end -- For PC use player's mouse.X/Y (more preciso que GetMouseLocation en algunos casos) local m = player:GetMouse() if m and m.X and m.Y then return m.X, m.Y end -- Fallback local pos = UIS:GetMouseLocation() return pos.X, pos.Y end -- Raycast auxiliar desde pantalla local function raycastFromScreen(x, y, maxDist) -- Usa ScreenPointToRay con coordenadas en píxeles local ray = camera:ScreenPointToRay(x, y) local dist = maxDist or 1000 return workspace:FindPartOnRayWithIgnoreList(Ray.new(ray.Origin, ray.Direction * dist), {player.Character}) end -- Actualizar apariencia de botones según estados local function updateButtonStates() if GUIrefs.magnetBtn and GUIrefs.grabBtn then -- Imán: azul brillante cuando activo, gris oscuro cuando no if imanActivo then GUIrefs.magnetBtn.BackgroundColor3 = Color3.fromRGB(25,130,255) GUIrefs.magnetBtn.TextColor3 = Color3.fromRGB(255,255,255) GUIrefs.magnetBtn.Text = "Imán ✓" else GUIrefs.magnetBtn.BackgroundColor3 = Color3.fromRGB(30,30,30) GUIrefs.magnetBtn.TextColor3 = Color3.fromRGB(240,240,240) GUIrefs.magnetBtn.Text = "Imán" end -- Agarrar: verde cuando sostienes algo if holding then GUIrefs.grabBtn.BackgroundColor3 = Color3.fromRGB(45,195,100) GUIrefs.grabBtn.Text = "Soltar" else GUIrefs.grabBtn.BackgroundColor3 = Color3.fromRGB(30,30,30) GUIrefs.grabBtn.Text = "Agarrar" end end end -- Visual del virtual mouse y overlay del radio del imán en UI local radiusScreenScale = 2.0 -- factor arbitario para convertir radioIman (unidades world) a píxeles en la UI (ajustable) local function updateVMVisuals() local screenX, screenY = getScreenSize() if not GUIrefs.screenGui then return end -- Virtual mouse frame if GUIrefs.vm and virtualMousePos then local px = math.clamp(virtualMousePos.X - virtualMouseSize/2, 0, screenX - virtualMouseSize) local py = math.clamp(virtualMousePos.Y - virtualMouseSize/2, 0, screenY - virtualMouseSize) GUIrefs.vm.Position = UDim2.new(0, px, 0, py) end -- Imán overlay on UI: center at virtualMousePos (or mouse pos if not touch) if GUIrefs.vmCircle then local cx, cy = getInputPosition() -- Compute pixel radius local radPx = math.clamp(math.floor(radioIman * radiusScreenScale), 8, math.min(screenX, screenY)) GUIrefs.vmCircle.Size = UDim2.new(0, radPx*2, 0, radPx*2) GUIrefs.vmCircle.Position = UDim2.new(0, cx - radPx, 0, cy - radPx) GUIrefs.vmCircle.ImageColor3 = imanActivo and Color3.fromRGB(60,170,255) or Color3.fromRGB(80,80,80) GUIrefs.vmCircle.Visible = true end end -- RenderStepped: resaltar, controlar círculo del imán en world y actualizar UI visuals run.RenderStepped:Connect(function() -- Resalte / selección if guiHidden then -- hide contours while GUI is hidden if SelectionBox and SelectionBox.Parent then SelectionBox.Parent = nil end else -- ensure SelectionBox is parented when GUI visible if SelectionBox and not SelectionBox.Parent then SelectionBox.Parent = game:GetService("CoreGui") end if imanActivo and not holding then SelectionBox.Adornee = nil else if holding and target then SelectionBox.Color3, SelectionBox.Adornee = Color3.fromRGB(0,255,0), target else local sx, sy = getInputPosition() local hit, pos = raycastFromScreen(sx, sy, 1000) if hit and hit:IsA("BasePart") and not hit.Anchored and not esParteDePersonaje(hit) then SelectionBox.Color3, SelectionBox.Adornee = Color3.fromRGB(0,170,255), hit else SelectionBox.Adornee = nil end end end end -- Actualizar imán en world: ahora su centro usa getInputPosition (virtual mouse en móvil o mouse en PC) if imanActivo then local sx, sy = getInputPosition() local ray = camera:ScreenPointToRay(sx, sy) local hit, pos, norm = workspace:FindPartOnRayWithIgnoreList(Ray.new(ray.Origin, ray.Direction * 1000), {player.Character}) if hit then surfacePos, surfaceNormal = lerpVec3(surfacePos,pos,0.35), lerpVec3(surfaceNormal,norm,0.35) else local defPos = ray.Origin + ray.Direction * 15 surfacePos, surfaceNormal = lerpVec3(surfacePos,defPos,0.15), lerpVec3(surfaceNormal,Vector3.new(0,1,0),0.15) end actualRadio = actualRadio + (radioIman - actualRadio) * 0.25 if usingDrawing then local screenPos = camera:WorldToViewportPoint(surfacePos) imancircle.Visible, imancircle.Position, imancircle.Radius = true, Vector2.new(screenPos.X, screenPos.Y), actualRadio else circleGui.Enabled, circleGui.Size = true, UDim2.new(0,actualRadio*2,0,actualRadio*2) circleGui.CFrame = CFrame.new(surfacePos, surfacePos + camera.CFrame.LookVector) * CFrame.fromMatrix(Vector3.zero, surfaceNormal:Cross(Vector3.new(0,1,0)).Magnitude>0.01 and surfaceNormal:Cross(Vector3.new(0,1,0)).Unit or Vector3.new(1,0,0), surfaceNormal, -surfaceNormal:Cross(Vector3.new(1,0,0)).Unit) circleGui.Position = surfacePos end else if usingDrawing then imancircle.Visible = false elseif circleGui then circleGui.Enabled = false end end -- actualizar VM visuals y estado de botones updateVMVisuals() updateButtonStates() end) -- Heartbeat: movimiento del objeto, imán, ajuste de distancias run.Heartbeat:Connect(function() -- Telequinesis normal if holding and target then if not anchoredWhileHolding then local grabPos if usingGamepad then grabPos = camera.CFrame.Position + camera.CFrame.LookVector.Unit * holdDistance else local sx, sy = getInputPosition() local ray = camera:ScreenPointToRay(sx, sy) grabPos = ray.Origin + ray.Direction.Unit * holdDistance end vel.Velocity = (grabPos - target.Position) * 5 if originalRotation then gyro.CFrame = originalRotation + target.Position end else vel.Velocity = Vector3.zero end end -- Bloque imán if imanActivo then local centro, nuevosAtraidos = surfacePos, {} local nearby = workspace:GetPartBoundsInBox(CFrame.new(centro), Vector3.new(radioIman*2, radioIman*2, radioIman*2)) for _, obj in ipairs(nearby) do if obj:IsA("BasePart") and not obj.Anchored and not esParteDePersonaje(obj) and not tieneSoldaduraNoAnclada(obj) and obj.Transparency < 1 and obj.CanCollide and obj ~= target then local distancia = (obj.Position - centro).Magnitude if distancia <= radioIman then local bv = obj:FindFirstChild("ImanBV") or Instance.new("BodyVelocity") bv.Name, bv.MaxForce, bv.P = "ImanBV", Vector3.new(1,1,1)*1e5, 15000 local dir = (centro - obj.Position) * Vector3.new(1,0,1) if dir.Magnitude > 0 then bv.Velocity = dir.Unit * fuerzaIman else bv.Velocity = Vector3.zero end bv.Parent = obj if not gravityBackup[obj] then gravityBackup[obj] = obj.CustomPhysicalProperties obj.CustomPhysicalProperties = PhysicalProperties.new(0, 0.3, 0.5, 1, 1) end nuevosAtraidos[obj] = true end end end for obj in pairs(objetosIman) do if not nuevosAtraidos[obj] then if obj and obj:FindFirstChild("ImanBV") then obj.ImanBV:Destroy() end if gravityBackup[obj] then obj.CustomPhysicalProperties = gravityBackup[obj]; gravityBackup[obj] = nil end end end objetosIman = nuevosAtraidos else for obj in pairs(objetosIman) do if obj and obj:FindFirstChild("ImanBV") then obj.ImanBV:Destroy() end end restaurarGravedad() objetosIman = {} end -- Ajuste distancia o radio con botones móviles (holdLeft / holdRight) o teclado (Q/E) if imanActivo then if holdLeft or holdQ then radioIman = math.max(5, radioIman - 1.3) elseif holdRight or holdE then radioIman = math.min(200, radioIman + 1.3) end else if holdLeft or holdQ then holdDistance = math.max(2, holdDistance - 0.4) elseif holdRight or holdE then holdDistance = math.min(1000, holdDistance + 0.4) end end end) -- Acciones principales (se usan desde los botones móviles y controles PC) function agarrarOSoltar() if imanActivo then pcall(function() game.StarterGui:SetCore("SendNotification", { Title = "🧲 Imán activo", Text = "Suelta el imán para agarrar objetos", Duration = 2 }) end) return end if holding then originalRotation = nil vel.Parent, gyro.Parent, anchoredWhileHolding, target, holding = nil, nil, false, nil, false updateButtonStates() else local sx, sy = getInputPosition() local part, pos = raycastFromScreen(sx, sy, 1000) if part and part:IsA("BasePart") and not part.Anchored and not esParteDePersonaje(part) then target, anchoredWhileHolding = part, false originalRotation = part.CFrame - part.Position gyro.CFrame = originalRotation + part.Position gyro.Parent = part vel.Velocity, vel.Parent = Vector3.zero, part if pos then holdDistance = (camera.CFrame.Position - pos).Magnitude else holdDistance = (camera.CFrame.Position - part.Position).Magnitude end holding = true updateButtonStates() end end end function lanzar() if imanActivo then pcall(function() game.StarterGui:SetCore("SendNotification", { Title = "🧲 Imán activo", Text = "Desactiva el imán para lanzar objetos", Duration = 2 }) end) return end if holding and target then if anchoredWhileHolding then target.Anchored, anchoredWhileHolding = false, false end vel.Parent, gyro.Parent, holding = nil, nil, false originalRotation = nil local impulse = Instance.new("BodyVelocity") impulse.Velocity = camera.CFrame.LookVector * 1000 impulse.MaxForce, impulse.P, impulse.Parent = Vector3.new(1,1,1)*1e6, 12500, target game:GetService("Debris"):AddItem(impulse, 0.5) target = nil updateButtonStates() end end function alternarAnclado() if imanActivo then pcall(function() game.StarterGui:SetCore("SendNotification", { Title = "🧲 Imán activo", Text = "Desactiva el imán para anclar objetos", Duration = 2 }) end) return end if holding and target then anchoredWhileHolding = not anchoredWhileHolding target.Anchored = anchoredWhileHolding if anchoredWhileHolding then vel.Parent = nil gyro.Parent = nil target.AssemblyLinearVelocity = Vector3.zero target.AssemblyAngularVelocity = Vector3.zero local humanoidRoot = player.Character and player.Character:FindFirstChild("HumanoidRootPart") if humanoidRoot and (humanoidRoot.Position - target.Position).Magnitude < 5 then humanoidRoot.Velocity = Vector3.zero humanoidRoot.AssemblyLinearVelocity = Vector3.zero humanoidRoot.CFrame = humanoidRoot.CFrame + Vector3.new(0, 3, 0) end else vel.Parent = target gyro.Parent = target end pcall(function() game.StarterGui:SetCore("SendNotification", { Title = anchoredWhileHolding and "📌 Anclado" or "📎 Desanclado", Text = "Toca anclar para alternar", Duration = 2 }) end) end end local function toggleIman() if holding then pcall(function() game.StarterGui:SetCore("SendNotification", { Title = "❌ No disponible", Text = "Suelta el objeto para activar el imán", Duration = 2 }) end) return end imanActivo = not imanActivo if not imanActivo then restaurarGravedad() end updateButtonStates() pcall(function() game.StarterGui:SetCore("SendNotification", { Title = imanActivo and "🧲 Modo Imán ACTIVADO" or "🧲 Modo Imán DESACTIVADO", Text = "Atrae solo piezas sueltas en el círculo azul", Duration = 2 }) end) end local function mostrarPanelMovil() pcall(function() game.StarterGui:SetCore("SendNotification", { Title = "Ayuda Móvil", Text = "• Agarrar → Botón 'Agarrar'\n• Lanzar → Botón 'Lanzar'\n• Anclar → Botón 'Anclar'\n• Imán → Botón 'Imán'\n• Mantén - / + → Acercar/Alejar\n• Mueve el botón ' ? ' tocándolo y arrastrándolo\n• Botón 'ocultar' oculta la interfaz excepto el propio botón", Duration = 8 }) end) end -- CREAR INTERFAZ MÓVIL MEJORADA -- Cambios aplicados: -- 1) Orden de la barra (izquierda -> derecha): Anclar | + | - | Imán | Agarrar | Lanzar -- 2) Solo se permite mover 1 widget a la vez (draggingWidget lock). -- 3) Al mover el botón de ocultar NO se mueve el botón "?" (son independientes). local draggingWidget = nil -- nil or "help"/"hide"/"vm" local function crearGUI_Movil() local playerGui = player:WaitForChild("PlayerGui") local screenGui = Instance.new("ScreenGui") screenGui.Name = "LevitatoMobileGui" screenGui.ResetOnSpawn = false screenGui.Parent = playerGui GUIrefs.screenGui = screenGui -- Base: barra inferior semi-transparente con botones redondeados y spacing local bottomBar = Instance.new("Frame") bottomBar.Name = "BottomBar" bottomBar.AnchorPoint = Vector2.new(0.5, 1) bottomBar.Size = UDim2.new(0.9, 0, 0, 90) bottomBar.Position = UDim2.new(0.5, 0, 1, -20) bottomBar.BackgroundColor3 = Color3.fromRGB(20,20,20) bottomBar.BackgroundTransparency = 0.45 bottomBar.BorderSizePixel = 0 bottomBar.Parent = screenGui GUIrefs.bottomBar = bottomBar local corner = Instance.new("UICorner", bottomBar) corner.CornerRadius = UDim.new(0, 18) local stroke = Instance.new("UIStroke", bottomBar) stroke.Color = Color3.fromRGB(80,80,80) stroke.Transparency = 0.6 stroke.Thickness = 2 local layout = Instance.new("UIListLayout", bottomBar) layout.FillDirection = Enum.FillDirection.Horizontal layout.HorizontalAlignment = Enum.HorizontalAlignment.Center layout.VerticalAlignment = Enum.VerticalAlignment.Center layout.Padding = UDim.new(0, 12) -- Helper to make transparent rounded buttons local function makeButton(name, text, size) local b = Instance.new("TextButton") b.Name = name b.Text = text b.Font = Enum.Font.GothamBold b.TextScaled = true b.TextColor3 = Color3.fromRGB(240,240,240) b.BackgroundColor3 = Color3.fromRGB(30,30,30) b.BackgroundTransparency = 0.55 b.BorderSizePixel = 0 b.Size = size or UDim2.new(0,120,0,60) b.AutoButtonColor = false local uic = Instance.new("UICorner", b) uic.CornerRadius = UDim.new(0,14) local vstroke = Instance.new("UIStroke", b) vstroke.Color = Color3.fromRGB(110,110,110) vstroke.Transparency = 0.6 vstroke.Thickness = 1.5 return b end -- Crear botones en el ORDEN EXACTO solicitado: -- Anclar | + | - | Imán | Agarrar | Lanzar local anchorBtn = makeButton("AnchorBtn", "Anclar", UDim2.new(0,110,0,62)) anchorBtn.Parent = bottomBar local zoomInBtn = makeButton("ZoomInBtn", "+", UDim2.new(0,56,0,56)) zoomInBtn.Parent = bottomBar local zoomOutBtn = makeButton("ZoomOutBtn", "-", UDim2.new(0,56,0,56)) zoomOutBtn.Parent = bottomBar local magnetBtn = makeButton("MagnetBtn", "Imán", UDim2.new(0,110,0,62)) magnetBtn.Parent = bottomBar local grabBtn = makeButton("GrabBtn", "Agarrar", UDim2.new(0,110,0,62)) grabBtn.Parent = bottomBar local throwBtn = makeButton("ThrowBtn", "Lanzar", UDim2.new(0,110,0,62)) throwBtn.Parent = bottomBar -- Guardar referencias GUIrefs.anchorBtn = anchorBtn GUIrefs.zoomInBtn = zoomInBtn GUIrefs.zoomOutBtn = zoomOutBtn GUIrefs.magnetBtn = magnetBtn GUIrefs.grabBtn = grabBtn GUIrefs.throwBtn = throwBtn -- Botón ayuda flotante y movible (con transparencia y border redondeado) local helpBtn = Instance.new("TextButton") helpBtn.Name = "HelpBtn" helpBtn.Text = "?" helpBtn.Font = Enum.Font.GothamBlack helpBtn.TextScaled = true helpBtn.TextColor3 = Color3.fromRGB(255,255,255) helpBtn.BackgroundColor3 = Color3.fromRGB(20,20,20) helpBtn.BackgroundTransparency = 0.5 helpBtn.Size = UDim2.new(0,68,0,68) helpBtn.AnchorPoint = Vector2.new(0.5,0) helpBtn.Position = UDim2.new(0.86, 0, 0.03, 0) helpBtn.Parent = screenGui GUIrefs.helpBtn = helpBtn local helpCorner = Instance.new("UICorner", helpBtn) helpCorner.CornerRadius = UDim.new(0, 18) local helpStroke = Instance.new("UIStroke", helpBtn) helpStroke.Color = Color3.fromRGB(100,100,100) helpStroke.Transparency = 0.5 helpStroke.Thickness = 2 -- Botón ocultar (movible e independiente) local hideBtn = Instance.new("TextButton") hideBtn.Name = "HideBtn" hideBtn.Text = "⦿" hideBtn.Font = Enum.Font.GothamBold hideBtn.TextScaled = true hideBtn.TextColor3 = Color3.fromRGB(255,255,255) hideBtn.BackgroundColor3 = Color3.fromRGB(20,20,20) hideBtn.BackgroundTransparency = 0.5 hideBtn.Size = UDim2.new(0,48,0,48) hideBtn.AnchorPoint = Vector2.new(0,0) hideBtn.Position = UDim2.new(0.95, -36, 0.03, 18) hideBtn.Parent = screenGui GUIrefs.hideBtn = hideBtn local hideCorner = Instance.new("UICorner", hideBtn) hideCorner.CornerRadius = UDim.new(0, 12) local hideStroke = Instance.new("UIStroke", hideBtn) hideStroke.Color = Color3.fromRGB(100,100,100) hideStroke.Transparency = 0.5 hideStroke.Thickness = 2 -- Virtual mouse visual (circle) local vm = Instance.new("Frame") vm.Name = "VirtualMouse" vm.AnchorPoint = Vector2.new(0, 0) vm.Size = UDim2.new(0, virtualMouseSize, 0, virtualMouseSize) vm.BackgroundTransparency = 0.6 vm.BackgroundColor3 = Color3.fromRGB(10,10,10) vm.Parent = screenGui GUIrefs.vm = vm local vmCorner = Instance.new("UICorner", vm) vmCorner.CornerRadius = UDim.new(1,0) local vmStroke = Instance.new("UIStroke", vm) vmStroke.Color = Color3.fromRGB(60,160,255) vmStroke.Transparency = 0.4 vmStroke.Thickness = 2 -- Inner dot (reduced to match smaller VM) local vmDot = Instance.new("Frame") vmDot.Name = "Dot" vmDot.AnchorPoint = Vector2.new(0.5, 0.5) vmDot.Size = UDim2.new(0, 12, 0, 12) vmDot.Position = UDim2.fromScale(0.5, 0.5) vmDot.BackgroundColor3 = Color3.fromRGB(60,160,255) vmDot.BackgroundTransparency = 0 vmDot.Parent = vm local vmDotCorner = Instance.new("UICorner", vmDot) vmDotCorner.CornerRadius = UDim.new(1,0) -- Overlay circle image to represent imán radius on screen (so UI circle matches virtual mouse) local vmCircle = Instance.new("ImageLabel") vmCircle.Name = "VirtualMagnetCircle" vmCircle.BackgroundTransparency = 1 vmCircle.Image = "rbxassetid://13523341990" vmCircle.ImageColor3 = circleColor vmCircle.AnchorPoint = Vector2.new(0,0) vmCircle.Size = UDim2.new(0, 100, 0, 100) vmCircle.Position = UDim2.new(0, 0, 0, 0) vmCircle.ZIndex = 2 vmCircle.Visible = false vmCircle.Parent = screenGui GUIrefs.vmCircle = vmCircle -- Position vm according to virtualMousePos in pixels or center local function placeVMAtPixels(p) local sx, sy = getScreenSize() vm.Position = UDim2.new(0, math.clamp(p.X - virtualMouseSize/2, 0, sx - virtualMouseSize), 0, math.clamp(p.Y - virtualMouseSize/2, 0, sy - virtualMouseSize)) end if virtualMousePos then placeVMAtPixels(virtualMousePos) else local center = computeVirtualCenterPixels() virtualMousePos = center placeVMAtPixels(center) end -- Conexiones de botones magnetBtn.MouseButton1Click:Connect(function() toggleIman() end) grabBtn.MouseButton1Click:Connect(function() agarrarOSoltar() end) throwBtn.MouseButton1Click:Connect(function() lanzar() end) anchorBtn.MouseButton1Click:Connect(function() alternarAnclado() end) helpBtn.MouseButton1Click:Connect(function() mostrarPanelMovil() end) -- Conexiones de zoom (mantener comportamiento previo) zoomInBtn.MouseButton1Down:Connect(function() holdRight = true end) zoomInBtn.MouseButton1Up:Connect(function() holdRight = false end) zoomInBtn.MouseLeave:Connect(function() holdRight = false end) zoomOutBtn.MouseButton1Down:Connect(function() holdLeft = true end) zoomOutBtn.MouseButton1Up:Connect(function() holdLeft = false end) zoomOutBtn.MouseLeave:Connect(function() holdLeft = false end) -- Hacer que los widgets movibles respeten un único dedo/input para arrastrar (lock) -- Además: helpBtn y hideBtn ahora se mueven de forma independiente; solo 1 widget puede moverse al mismo tiempo. do -- Help drag do local dragging = false local dragInput = nil helpBtn.InputBegan:Connect(function(input) if draggingWidget ~= nil then return end if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = true dragInput = input draggingWidget = "help" input.Changed:Connect(function() if input.UserInputState == Enum.UserInputState.End then dragging = false dragInput = nil if draggingWidget == "help" then draggingWidget = nil end end end) end end) UIS.InputChanged:Connect(function(input) if dragging and dragInput and input == dragInput and (input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseMovement) then local sx, sy = getScreenSize() local mousePos = input.Position or UIS:GetMouseLocation() if mousePos then local newX = math.clamp(mousePos.X / sx, 0.02, 0.98) local newY = math.clamp(mousePos.Y / sy, 0.02, 0.98) helpBtn.Position = UDim2.new(newX, 0, newY, 0) end end end) end -- Hide drag (independiente; no mueve helpBtn) do local dragging = false local dragInput = nil hideBtn.InputBegan:Connect(function(input) if draggingWidget ~= nil then return end if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = true dragInput = input draggingWidget = "hide" input.Changed:Connect(function() if input.UserInputState == Enum.UserInputState.End then dragging = false dragInput = nil if draggingWidget == "hide" then draggingWidget = nil end end end) end end) UIS.InputChanged:Connect(function(input) if dragging and dragInput and input == dragInput and (input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseMovement) then local sx, sy = getScreenSize() local mousePos = input.Position or UIS:GetMouseLocation() if mousePos then local newX = math.clamp(mousePos.X / sx, 0.02, 0.98) local newY = math.clamp(mousePos.Y / sy, 0.02, 0.98) hideBtn.Position = UDim2.new(newX, 0, newY, 0) end end end) end -- VM drag do local draggingVM = false local currentInput = nil local function updateVMPositionFromInput(inputPos) local sx, sy = getScreenSize() local x = math.clamp(inputPos.X, 1, sx-1) local y = math.clamp(inputPos.Y, 1, sy-1) virtualMousePos = Vector2.new(x, y) placeVMAtPixels(virtualMousePos) end vm.InputBegan:Connect(function(input) if draggingWidget ~= nil then return end if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then draggingVM = true currentInput = input draggingWidget = "vm" local pos = input.Position or UIS:GetMouseLocation() if pos then updateVMPositionFromInput(pos) end input.Changed:Connect(function() if input.UserInputState == Enum.UserInputState.End then draggingVM = false currentInput = nil if draggingWidget == "vm" then draggingWidget = nil end end end) end end) UIS.InputChanged:Connect(function(input) if draggingVM and currentInput and input == currentInput and (input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseMovement) then local pos = input.Position or UIS:GetMouseLocation() if pos then updateVMPositionFromInput(pos) end end end) end end -- Botón ocultar: oculta todo excepto este botón (y puede moverse) hideBtn.MouseButton1Click:Connect(function() guiHidden = not guiHidden if guiHidden then -- hide bottom bar, vm, vmCircle, helpBtn, and selection contours; keep hideBtn visible if GUIrefs.bottomBar then GUIrefs.bottomBar.Visible = false end if GUIrefs.vm then GUIrefs.vm.Visible = false end if GUIrefs.vmCircle then GUIrefs.vmCircle.Visible = false end if GUIrefs.helpBtn then GUIrefs.helpBtn.Visible = false end -- Remove SelectionBox from CoreGui to hide contours if SelectionBox and SelectionBox.Parent then SelectionBox.Parent = nil end hideBtn.Text = "◤" -- make hideBtn more visible hideBtn.BackgroundTransparency = 0 hideBtn.BackgroundColor3 = Color3.fromRGB(30,30,30) else -- show everything again if GUIrefs.bottomBar then GUIrefs.bottomBar.Visible = true end if GUIrefs.vm then GUIrefs.vm.Visible = true end -- vmCircle visibility will be set in updateVMVisuals() if GUIrefs.helpBtn then GUIrefs.helpBtn.Visible = true end -- Restore SelectionBox parent so contours reappear if SelectionBox and not SelectionBox.Parent then SelectionBox.Parent = game:GetService("CoreGui") end hideBtn.Text = "⦿" hideBtn.BackgroundTransparency = 0.5 end end) -- Virtual mouse tap on vm: quick grab (opcional convenience) vm.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.Touch then -- quick tap => grab/soltar, only if not dragging any widget if draggingWidget == nil then agarrarOSoltar() end end end) -- Mostrar solo si el dispositivo tiene touch screenGui.Enabled = UIS.TouchEnabled end -- Crear la GUI móvil al inicio (si corresponde) crearGUI_Movil() -- RE-INCORPORAR controles de PC y gamepad para que funcione en ambos dispositivos UIS.InputBegan:Connect(function(input, gpe) if gpe then return end if input.UserInputType == Enum.UserInputType.Keyboard then if input.KeyCode == Enum.KeyCode.Q then holdQ = true elseif input.KeyCode == Enum.KeyCode.E then holdE = true elseif input.KeyCode == Enum.KeyCode.R then alternarAnclado() elseif input.KeyCode == Enum.KeyCode.F then lanzar() elseif input.KeyCode == Enum.KeyCode.T then if holding then pcall(function() game.StarterGui:SetCore("SendNotification", { Title = "❌ No disponible", Text = "Suelta el objeto para activar el imán", Duration = 2 }) end) return end imanActivo = not imanActivo if not imanActivo then restaurarGravedad() end updateButtonStates() pcall(function() game.StarterGui:SetCore("SendNotification", { Title = imanActivo and "🧲 Modo Imán ACTIVADO" or "🧲 Modo Imán DESACTIVADO", Text = "Atrae solo piezas sueltas en el círculo azul", Duration = 2 }) end) elseif input.KeyCode == Enum.KeyCode.LeftControl or input.KeyCode == Enum.KeyCode.RightControl then -- Mostrar panel PC (útil si en PC) pcall(function() game.StarterGui:SetCore("SendNotification", { Title = "Ayuda PC", Text = "⌨️ Controles PC:\n• Click Izquierdo → Agarrar / Soltar\n• F → Lanzar\n• Q/E (mantener) → Acercar/Alejar -- o cambiar área del imán\n• R → Anclar/Desanclar\n• T → Modo Imán\n• Ctrl → Mostrar este panel", Duration = 8 }) end) end end if input.UserInputType == Enum.UserInputType.Gamepad1 and usingGamepad then if input.KeyCode == Enum.KeyCode.ButtonX then agarrarOSoltar() elseif input.KeyCode == Enum.KeyCode.ButtonB then lanzar() elseif input.KeyCode == Enum.KeyCode.ButtonY then alternarAnclado() elseif input.KeyCode == Enum.KeyCode.DPadLeft then holdLeft = true elseif input.KeyCode == Enum.KeyCode.DPadRight then holdRight = true elseif input.KeyCode == Enum.KeyCode.DPadDown then pcall(function() game.StarterGui:SetCore("SendNotification", { Title = "Ayuda Mando", Text = "🎮 Controles Mando:\n• X → Agarrar/Soltar\n• B → Lanzar\n• ⬅/➡ (mantener) → Acercar/Alejar -- o cambiar área del imán\n• Y → Anclar/Desanclar\n• ⬇ → Mostrar este panel\n• LB → Modo Imán (sólo piezas sueltas en el círculo azul)", Duration = 9 }) end) elseif input.KeyCode == Enum.KeyCode.ButtonL1 then if holding then pcall(function() game.StarterGui:SetCore("SendNotification", { Title = "❌ No disponible", Text = "Suelta el objeto para activar el imán", Duration = 2 }) end) return end imanActivo = not imanActivo if not imanActivo then restaurarGravedad() end updateButtonStates() pcall(function() game.StarterGui:SetCore("SendNotification", { Title = imanActivo and "🧲 Modo Imán ACTIVADO" or "🧲 Modo Imán DESACTIVADO", Text = "Atrae solo piezas sueltas en el círculo azul", Duration = 2 }) end) end end end) UIS.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.Keyboard then if input.KeyCode == Enum.KeyCode.Q then holdQ = false elseif input.KeyCode == Enum.KeyCode.E then holdE = false end elseif input.UserInputType == Enum.UserInputType.Gamepad1 then if input.KeyCode == Enum.KeyCode.DPadLeft then holdLeft = false elseif input.KeyCode == Enum.KeyCode.DPadRight then holdRight = false end end end) -- Mantener soporte del mouse (PC): click izquierdo para agarrar / soltar local mouse = player:GetMouse() mouse.Button1Down:Connect(function() -- Only use real mouse when not on touch virtual mouse (i.e., PC mode will use this) if not UIS.TouchEnabled then agarrarOSoltar() end end) -- Inicial update de estados UI updateButtonStates() -- FIN DEL SCRIPT