--[[ VERSIÓN MEJORADA Y CORREGIDA ]] local Players = game:GetService("Players") local CoreGui = game:GetService("CoreGui") local UserInputService = game:GetService("UserInputService") local TweenService = game:GetService("TweenService") local RunService = game:GetService("RunService") local Workspace = workspace local StarterGui = game:GetService("StarterGui") local LocalPlayer = Players.LocalPlayer local Camera = Workspace.CurrentCamera local Mouse = LocalPlayer:GetMouse() if not Camera or not LocalPlayer or not Mouse then return end local TWEEN_INFO_FAST = TweenInfo.new(0.20, Enum.EasingStyle.Quad, Enum.EasingDirection.Out) local TWEEN_INFO_MED = TweenInfo.new(0.30, Enum.EasingStyle.Back, Enum.EasingDirection.Out) local TWEEN_INFO_SLOW = TweenInfo.new(0.75, Enum.EasingStyle.Quart, Enum.EasingDirection.Out) local THEME = { bg = Color3.fromRGB(28, 28, 30), panel = Color3.fromRGB(35, 35, 38), miniPanel = Color3.fromRGB(15, 15, 17), accent = Color3.fromRGB(0, 160, 255), subtleAccent = Color3.fromRGB(16, 120, 200), danger = Color3.fromRGB(255, 80, 80), text = Color3.fromRGB(240, 240, 240), muted = Color3.fromRGB(170, 170, 180) } local isMobile = UserInputService.TouchEnabled and not UserInputService.KeyboardEnabled local scale = isMobile and 0.75 or 1.0 local SIZES = { MainW = 320 * scale, MainH = 460 * scale, MainX = 60 * scale, MainY = 80 * scale, TitleH = 44 * scale, BtnSize = 36 * scale, BtnPadding = 10 * scale, CornerS = 8 * scale, CornerM = 10 * scale, CornerL = 14 * scale, SearchH = 36 * scale, Padding = 14 * scale, ListTop = 104 * scale, ListBottom = 170 * scale, BottomBtnH = 44 * scale, BottomBtnY = 56 * scale, EntryH = 56 * scale, EntryPad = 12 * scale, AvatarSize = 44 * scale, AvatarPad = 10 * scale, AvatarY = 6 * scale, MiniH = 64 * scale, MiniIconSize = 32 * scale, MiniSideMargin = 12 * scale, MiniBetweenPadding = 12 * scale, MiniMinWidth = 120 * scale, MiniMaxWidth = 420 * scale, } local FONTS = { Title = math.floor(17 * scale), Btn = math.floor(18 * scale), MiniIcon = math.floor(18 * scale), Search = math.floor(15 * scale), State = math.floor(13 * scale), Name = math.floor(15 * scale), Sub = math.floor(13 * scale), ViewBtn = math.floor(14 * scale), BottomBtn = math.floor(16 * scale), } local existing = CoreGui:FindFirstChild("CamaraEspiaUI") if existing then existing:Destroy() end local isMinimized = false local dragging, dragStart, startPos local searchDebounce = false local spyConnection = nil local spyTargetPart = nil local spiedPlayer = nil local hiddenGuisBySpy = {} local isSpying = false local cameraCanClip = false local touchId = nil local orbit = { enabled = false, distance = 12, minDistance = 1.2, maxDistance = 60, smoothing = 6, } local DEFAULT_ORBIT_MAX_DISTANCE = orbit.maxDistance local isManualCameraActive = false local manualJustActivated = false local isTransitioning = false local cameraAngle = Vector2.new(0, 0) local orbitRadius = orbit.distance local zoomDelta = 0 local isMouseDown = false local CAMERA_ROTATION_SENSITIVITY = 0.015 local CAMERA_ZOOM_SPEED = 3 local lastTouchScale = nil local PINCH_ZOOM_SENSITIVITY = 10 local isPlaceholderActive = false local ctrlLockActive = false local prevCameraSaved = false local prevCameraOffset = nil local prevCameraType = nil local prevCameraSubject = nil local initialCameraAngle = Vector2.new(0, 0) local hasInitialAngle = false local npcCache = {} local lastNPCRefresh = 0 local NPC_CACHE_TIMEOUT = 2 local function getRootPart(character) if not character then return nil end return character:FindFirstChild("HumanoidRootPart") or character:FindFirstChild("Torso") or character:FindFirstChild("UpperTorso") or character:FindFirstChild("Head") end local function safeName(player) if not player then return "Jugador" end local displayName = player.DisplayName and player.DisplayName ~= "" and player.DisplayName or player.Name return displayName or "Jugador" end local function isPartPassable(part) if not part or not part:IsA("BasePart") then return true end if part.Anchored == false then return true end if part.CanCollide == false then return true end return false end local function getCameraPositionWithCollision(targetPos, desiredPos, ignoreInstances) if cameraCanClip then return desiredPos, false end local rayParams = RaycastParams.new() rayParams.FilterType = Enum.RaycastFilterType.Blacklist rayParams.FilterDescendantsInstances = ignoreInstances or {} local direction = desiredPos - targetPos local ignored = {} local attempts = 0 while true do attempts = attempts + 1 local filterList = {} if ignoreInstances then for _, v in ipairs(ignoreInstances) do table.insert(filterList, v) end end for _, v in ipairs(ignored) do table.insert(filterList, v) end rayParams.FilterDescendantsInstances = filterList local result = Workspace:Raycast(targetPos, direction, rayParams) if not result then return desiredPos, false end local part = result.Instance if isPartPassable(part) then table.insert(ignored, part) if attempts > 60 then local offset = result.Normal * 0.5 return result.Position + offset, true end else local offset = result.Normal * 0.5 return result.Position + offset, true end end end local function tweenGui(instance, props, info) if instance and instance.Parent then pcall(function() TweenService:Create(instance, info or TWEEN_INFO_FAST, props):Play() end) end end local ScreenGui = Instance.new("ScreenGui") ScreenGui.Name = "CamaraEspiaUI" ScreenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling ScreenGui.Parent = CoreGui ScreenGui.ResetOnSpawn = false ScreenGui.Enabled = true local SpyFadeOverlay = Instance.new("Frame", ScreenGui) SpyFadeOverlay.Name = "SpyFadeOverlay" SpyFadeOverlay.BackgroundColor3 = Color3.fromRGB(0, 0, 0) SpyFadeOverlay.BackgroundTransparency = 1 SpyFadeOverlay.BorderSizePixel = 0 SpyFadeOverlay.Size = UDim2.new(1, 0, 1, 0) SpyFadeOverlay.ZIndex = 3 SpyFadeOverlay.Visible = false local MainFrame = Instance.new("Frame", ScreenGui) MainFrame.Name = "MainFrame" MainFrame.Size = UDim2.new(0, SIZES.MainW, 0, SIZES.MainH) MainFrame.Position = UDim2.new(0, SIZES.MainX, 0, SIZES.MainY) MainFrame.BackgroundColor3 = THEME.bg MainFrame.BorderSizePixel = 0 MainFrame.ClipsDescendants = true MainFrame.Active = true MainFrame.ZIndex = 2 Instance.new("UICorner", MainFrame).CornerRadius = UDim.new(0, SIZES.CornerL) local mainStroke = Instance.new("UIStroke", MainFrame) mainStroke.Color = Color3.fromRGB(40, 40, 45) mainStroke.Transparency = 0.6 local MiniCover = Instance.new("Frame", MainFrame) MiniCover.Name = "MiniCover" MiniCover.Size = UDim2.new(1, 0, 1, 0) MiniCover.Position = UDim2.new(0, 0, 0, 0) MiniCover.BackgroundColor3 = THEME.miniPanel MiniCover.BorderSizePixel = 0 MiniCover.Visible = false Instance.new("UICorner", MiniCover).CornerRadius = UDim.new(0, SIZES.CornerL) MiniCover.ZIndex = 1 local TitleBar = Instance.new("Frame", MainFrame) TitleBar.Name = "TitleBar" TitleBar.Size = UDim2.new(1, 0, 0, SIZES.TitleH) TitleBar.Position = UDim2.new(0, 0, 0, 0) TitleBar.BackgroundColor3 = THEME.panel TitleBar.Active = true Instance.new("UICorner", TitleBar).CornerRadius = UDim.new(0, SIZES.CornerL) local TitleContainer = Instance.new("Frame", TitleBar) TitleContainer.Size = UDim2.new(1, -(SIZES.Padding), 1, 0) TitleContainer.Position = UDim2.new(0, SIZES.Padding / 2, 0, 0) TitleContainer.BackgroundTransparency = 1 local UIListLayout_Title = Instance.new("UIListLayout", TitleContainer) UIListLayout_Title.FillDirection = Enum.FillDirection.Horizontal UIListLayout_Title.VerticalAlignment = Enum.VerticalAlignment.Center UIListLayout_Title.HorizontalAlignment = Enum.HorizontalAlignment.Right UIListLayout_Title.SortOrder = Enum.SortOrder.LayoutOrder UIListLayout_Title.Padding = UDim.new(0, SIZES.BtnPadding) local TitleLabel = Instance.new("TextLabel", TitleContainer) TitleLabel.BackgroundTransparency = 1 TitleLabel.Size = UDim2.new(1, -(SIZES.BtnSize * 2 + SIZES.BtnPadding * 2), 1, 0) TitleLabel.Text = "Cámara Espía" TitleLabel.TextColor3 = THEME.text TitleLabel.Font = Enum.Font.GothamBold TitleLabel.TextSize = FONTS.Title TitleLabel.TextXAlignment = Enum.TextXAlignment.Left TitleLabel.LayoutOrder = 1 local ToggleCameraButton = Instance.new("TextButton", TitleContainer) ToggleCameraButton.Name = "ToggleCameraButton" ToggleCameraButton.Size = UDim2.new(0, SIZES.BtnSize, 0, SIZES.BtnSize) ToggleCameraButton.BackgroundColor3 = THEME.subtleAccent ToggleCameraButton.Text = "🔒" ToggleCameraButton.Font = Enum.Font.GothamBold ToggleCameraButton.TextSize = FONTS.Btn * 1.2 ToggleCameraButton.BorderSizePixel = 0 ToggleCameraButton.Visible = false ToggleCameraButton.LayoutOrder = 2 Instance.new("UICorner", ToggleCameraButton).CornerRadius = UDim.new(0, SIZES.CornerS) local MaximizeButton = Instance.new("TextButton", TitleContainer) MaximizeButton.Size = UDim2.new(0, SIZES.BtnSize, 0, SIZES.BtnSize) MaximizeButton.BackgroundColor3 = THEME.accent MaximizeButton.Text = "—" MaximizeButton.Font = Enum.Font.GothamBold MaximizeButton.TextSize = FONTS.Btn MaximizeButton.BorderSizePixel = 0 MaximizeButton.LayoutOrder = 3 Instance.new("UICorner", MaximizeButton).CornerRadius = UDim.new(0, SIZES.CornerS) local MiniControls = Instance.new("Frame", MainFrame) MiniControls.Name = "MiniControls" MiniControls.BackgroundTransparency = 1 MiniControls.Visible = false MiniControls.ClipsDescendants = false MiniControls.ZIndex = 4 local MiniLayout = Instance.new("UIListLayout", MiniControls) MiniLayout.FillDirection = Enum.FillDirection.Horizontal MiniLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center MiniLayout.VerticalAlignment = Enum.VerticalAlignment.Center MiniLayout.SortOrder = Enum.SortOrder.LayoutOrder MiniLayout.Padding = UDim.new(0, SIZES.MiniBetweenPadding) local MaximizeMiniButton = Instance.new("TextButton", MiniControls) MaximizeMiniButton.Size = UDim2.new(0, SIZES.MiniIconSize, 0, SIZES.MiniIconSize) MaximizeMiniButton.BackgroundColor3 = THEME.accent MaximizeMiniButton.Text = "+" MaximizeMiniButton.Font = Enum.Font.GothamBold MaximizeMiniButton.TextSize = FONTS.MiniIcon MaximizeMiniButton.TextColor3 = Color3.fromRGB(255, 255, 255) MaximizeMiniButton.LayoutOrder = 1 Instance.new("UICorner", MaximizeMiniButton).CornerRadius = UDim.new(0, SIZES.CornerM) MaximizeMiniButton.ZIndex = 5 local MiniManualButton = Instance.new("TextButton", MiniControls) MiniManualButton.Size = UDim2.new(0, SIZES.MiniIconSize, 0, SIZES.MiniIconSize) MiniManualButton.BackgroundColor3 = THEME.subtleAccent MiniManualButton.Text = "🔒" MiniManualButton.Font = Enum.Font.GothamBold MiniManualButton.TextSize = FONTS.MiniIcon MiniManualButton.TextColor3 = Color3.fromRGB(255, 255, 255) MiniManualButton.LayoutOrder = 2 MiniManualButton.BorderSizePixel = 0 Instance.new("UICorner", MiniManualButton).CornerRadius = UDim.new(0, SIZES.CornerM) MiniManualButton.ZIndex = 5 local MiniBackButton = Instance.new("TextButton", MiniControls) MiniBackButton.Size = UDim2.new(0, SIZES.MiniIconSize, 0, SIZES.MiniIconSize) MiniBackButton.BackgroundColor3 = THEME.danger MiniBackButton.Text = "" MiniBackButton.Font = Enum.Font.GothamBold MiniBackButton.TextSize = FONTS.MiniIcon MiniBackButton.TextColor3 = Color3.fromRGB(255, 255, 255) MiniBackButton.LayoutOrder = 3 MiniBackButton.BorderSizePixel = 0 Instance.new("UICorner", MiniBackButton).CornerRadius = UDim.new(0, SIZES.CornerM) MiniBackButton.ZIndex = 5 local MiniBackSquare = Instance.new("Frame", MiniBackButton) local sqSize = SIZES.MiniIconSize - 26 MiniBackSquare.Size = UDim2.new(0, sqSize, 0, sqSize) MiniBackSquare.Position = UDim2.new(0.5, -sqSize/2, 0.5, -sqSize/2) MiniBackSquare.BackgroundTransparency = 1 MiniBackSquare.BorderSizePixel = 0 MiniBackSquare.ZIndex = 6 local MiniBackStroke = Instance.new("UIStroke", MiniBackSquare) MiniBackStroke.Color = Color3.fromRGB(255, 255, 255) MiniBackStroke.Thickness = 2.5 MiniBackStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border local SearchBox = Instance.new("TextBox", MainFrame) SearchBox.Size = UDim2.new(1, -(SIZES.Padding * 2), 0, SIZES.SearchH) SearchBox.Position = UDim2.new(0, SIZES.Padding, 0, SIZES.TitleH + 12 * scale) SearchBox.PlaceholderText = "Buscar jugador o NPCs..." SearchBox.PlaceholderColor3 = THEME.muted SearchBox.BackgroundColor3 = Color3.fromRGB(38, 38, 40) SearchBox.TextColor3 = THEME.text SearchBox.TextSize = FONTS.Search SearchBox.Text = "" SearchBox.ClearTextOnFocus = false Instance.new("UICorner", SearchBox).CornerRadius = UDim.new(0, SIZES.CornerS) local PlayerFrame = Instance.new("ScrollingFrame", MainFrame) PlayerFrame.Size = UDim2.new(1, -(SIZES.Padding * 2), 1, -SIZES.ListBottom) PlayerFrame.Position = UDim2.new(0, SIZES.Padding, 0, SIZES.ListTop) PlayerFrame.BackgroundColor3 = Color3.fromRGB(28, 28, 30) PlayerFrame.ScrollBarThickness = 6 * scale PlayerFrame.AutomaticCanvasSize = Enum.AutomaticSize.Y PlayerFrame.ScrollBarImageColor3 = THEME.accent PlayerFrame.ClipsDescendants = true PlayerFrame.ScrollingDirection = Enum.ScrollingDirection.Y Instance.new("UICorner", PlayerFrame).CornerRadius = UDim.new(0, 6 * scale) local PlayerLayout = Instance.new("UIListLayout", PlayerFrame) PlayerLayout.Padding = UDim.new(0, SIZES.CornerS) PlayerLayout.SortOrder = Enum.SortOrder.LayoutOrder PlayerLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center local BackToPlayerButton = Instance.new("TextButton", MainFrame) BackToPlayerButton.Size = UDim2.new(1, -(SIZES.Padding * 2), 0, SIZES.BottomBtnH) BackToPlayerButton.Position = UDim2.new(0, SIZES.Padding, 1, -SIZES.BottomBtnY) BackToPlayerButton.BackgroundColor3 = THEME.danger BackToPlayerButton.Text = "Volver a mi cámara" BackToPlayerButton.Font = Enum.Font.GothamBold BackToPlayerButton.TextSize = FONTS.BottomBtn BackToPlayerButton.BorderSizePixel = 0 Instance.new("UICorner", BackToPlayerButton).CornerRadius = UDim.new(0, SIZES.CornerM) local StateLabel = Instance.new("TextLabel", MainFrame) StateLabel.Size = UDim2.new(1, -(SIZES.Padding * 2), 0, 20 * scale) StateLabel.Position = UDim2.new(0, SIZES.Padding, 0, SIZES.TitleH + 12 * scale + SIZES.SearchH + 4 * scale) StateLabel.BackgroundTransparency = 1 StateLabel.TextColor3 = THEME.muted StateLabel.Font = Enum.Font.Gotham StateLabel.TextSize = FONTS.State StateLabel.Visible = false local function cleanupSpyMode() for gui, _ in pairs(hiddenGuisBySpy) do if gui and gui.Parent then pcall(function() gui.Enabled = true end) end end hiddenGuisBySpy = {} end local function setSpyMode(enabled) if enabled == isSpying then return end isSpying = enabled if enabled then hiddenGuisBySpy = {} cameraCanClip = false pcall(function() StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.All, false) StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, false) end) for _, gui in ipairs(LocalPlayer.PlayerGui:GetChildren()) do if gui:IsA("ScreenGui") and gui.Name ~= ScreenGui.Name and gui.Enabled == true then hiddenGuisBySpy[gui] = true gui.Enabled = false end end else cameraCanClip = false pcall(function() StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.All, true) StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, true) end) cleanupSpyMode() end if not isMinimized then ToggleCameraButton.Visible = isSpying end end local function stopSpy() if spyConnection then spyConnection:Disconnect() spyConnection = nil end isTransitioning = false spyTargetPart = nil spiedPlayer = nil orbit.enabled = false isMouseDown = false lastTouchScale = nil touchId = nil ctrlLockActive = false pcall(function() UserInputService.MouseBehavior = Enum.MouseBehavior.Default pcall(function() UserInputService.MouseIconEnabled = true end) end) end local function syncManualIcons() local icon = isManualCameraActive and "🎮" or "🔒" if ToggleCameraButton and ToggleCameraButton.Parent then ToggleCameraButton.Text = icon end if MiniManualButton and MiniManualButton.Parent then MiniManualButton.Text = icon end local color = isManualCameraActive and THEME.accent or THEME.subtleAccent if ToggleCameraButton and ToggleCameraButton.Parent then ToggleCameraButton.BackgroundColor3 = color end if MiniManualButton and MiniManualButton.Parent then MiniManualButton.BackgroundColor3 = color end end local function initManualAnglesFromCurrentCamera(targetPos) local camPos = Camera.CFrame.Position local toTarget = (camPos - targetPos) local dir = toTarget.Unit local rotY = math.atan2(dir.X, -dir.Z) local rotX = math.asin(math.clamp(dir.Y, -0.999, 0.999)) cameraAngle = Vector2.new(rotY, rotX) local rotation = CFrame.Angles(0, rotY, 0) * CFrame.Angles(rotX, 0, 0) local correctPos = targetPos + (rotation.LookVector * orbitRadius * -1) Camera.CFrame = CFrame.lookAt(correctPos, targetPos) end local function toggleManualCamera(enabled) isManualCameraActive = enabled syncManualIcons() if enabled then if spyTargetPart and spyTargetPart.Parent then local tPos = spyTargetPart.Position + Vector3.new(0, 1.5, 0) local realDist = (Camera.CFrame.Position - tPos).Magnitude orbitRadius = math.clamp(realDist, orbit.minDistance, orbit.maxDistance) initManualAnglesFromCurrentCamera(tPos) else orbitRadius = orbit.distance end orbit.maxDistance = math.max(DEFAULT_ORBIT_MAX_DISTANCE, 1000000) lastTouchScale = nil manualJustActivated = true else orbit.maxDistance = DEFAULT_ORBIT_MAX_DISTANCE orbit.distance = orbitRadius manualJustActivated = false pcall(function() UserInputService.MouseBehavior = Enum.MouseBehavior.Default pcall(function() UserInputService.MouseIconEnabled = true end) end) end end local function captureInitialCameraAngle() if not hasInitialAngle then local camPos = Camera.CFrame.Position local forward = Camera.CFrame.LookVector local rotY = math.atan2(forward.X, -forward.Z) local rotX = math.asin(math.clamp(forward.Y, -0.999, 0.999)) initialCameraAngle = Vector2.new(rotY, rotX) hasInitialAngle = true end end local function startSpyOnCharacter(charModel) if not charModel or not charModel.Parent then return end if not isSpying then captureInitialCameraAngle() end if not prevCameraSaved then local localRoot = getRootPart(LocalPlayer.Character) if localRoot then prevCameraType = Camera.CameraType prevCameraSubject = Camera.CameraSubject prevCameraOffset = localRoot.CFrame:ToObjectSpace(Camera.CFrame) prevCameraSaved = true end end local savedRadius if isManualCameraActive then savedRadius = orbitRadius elseif spyTargetPart and spyTargetPart.Parent then local tPos = spyTargetPart.Position + Vector3.new(0, 1.5, 0) savedRadius = (Camera.CFrame.Position - tPos).Magnitude else local localRoot = getRootPart(LocalPlayer.Character) savedRadius = localRoot and (Camera.CFrame.Position - localRoot.Position).Magnitude or orbit.distance end savedRadius = math.clamp(savedRadius, orbit.minDistance, DEFAULT_ORBIT_MAX_DISTANCE) stopSpy() local root = getRootPart(charModel) if not root then return end spyTargetPart = root spiedPlayer = Players:GetPlayerFromCharacter(charModel) orbit.distance = savedRadius orbitRadius = savedRadius orbit.maxDistance = isManualCameraActive and math.max(DEFAULT_ORBIT_MAX_DISTANCE, 1000000) or DEFAULT_ORBIT_MAX_DISTANCE orbit.enabled = true Camera.CameraType = Enum.CameraType.Scriptable setSpyMode(true) if isManualCameraActive then local tPos = root.Position + Vector3.new(0, 1.5, 0) local startCF = Camera.CFrame local duration = 1 local elapsed = 0 isTransitioning = true local animConn animConn = RunService.RenderStepped:Connect(function(dt) pcall(function() elapsed = elapsed + dt local t = math.clamp(elapsed / duration, 0, 1) local e = 1 - (1 - t)^3 local behindOffset = -root.CFrame.LookVector * orbitRadius + Vector3.new(0, 3, 0) local desiredPos = tPos + behindOffset local goalCF = CFrame.lookAt(desiredPos, tPos) Camera.CFrame = startCF:Lerp(goalCF, e) if elapsed >= duration then if animConn then animConn:Disconnect() animConn = nil end isTransitioning = false local finalDir = (Camera.CFrame.Position - tPos).Unit cameraAngle = Vector2.new( math.atan2(finalDir.X, -finalDir.Z), math.asin(math.clamp(finalDir.Y, -0.999, 0.999)) ) initialCameraAngle = cameraAngle end end) end) end spyConnection = RunService.RenderStepped:Connect(function(dt) pcall(function() if isTransitioning then return end if not spyTargetPart or not spyTargetPart.Parent then if spiedPlayer and spiedPlayer.Character then local newRoot = getRootPart(spiedPlayer.Character) if newRoot then spyTargetPart = newRoot else resetCamera() return end else resetCamera() return end end local tPos = spyTargetPart.Position + Vector3.new(0, 1.5, 0) local ignoreList = {} if spyTargetPart.Parent then table.insert(ignoreList, spyTargetPart.Parent) end if LocalPlayer and LocalPlayer.Character then table.insert(ignoreList, LocalPlayer.Character) end local targetCFrame if isManualCameraActive then orbitRadius = math.max(orbit.minDistance, orbitRadius - zoomDelta) zoomDelta = 0 local rotation = CFrame.Angles(0, cameraAngle.X, 0) * CFrame.Angles(cameraAngle.Y, 0, 0) local desiredPos = tPos + (rotation.LookVector * orbitRadius * -1) local camPosCollided, _ = getCameraPositionWithCollision(tPos, desiredPos, ignoreList) targetCFrame = CFrame.lookAt(camPosCollided, tPos) else orbitRadius = orbit.distance local desiredPos = tPos - spyTargetPart.CFrame.LookVector * orbit.distance + Vector3.new(0, 3, 0) local minHeight = math.max(2.5, tPos.Y + 1) if desiredPos.Y < minHeight then desiredPos = Vector3.new(desiredPos.X, minHeight, desiredPos.Z) end local camPosCollided, _ = getCameraPositionWithCollision(tPos, desiredPos, ignoreList) targetCFrame = CFrame.lookAt(camPosCollided, tPos) end if manualJustActivated then Camera.CFrame = targetCFrame manualJustActivated = false else local smooth = isManualCameraActive and 10 or orbit.smoothing local alpha = math.clamp(1 - math.exp(-dt * smooth), 0, 1) Camera.CFrame = Camera.CFrame:Lerp(targetCFrame, alpha) end end) end) end function resetCamera() if not isSpying then return end local hadSpy = isSpying if spyConnection then spyConnection:Disconnect() spyConnection = nil end local root = getRootPart(LocalPlayer.Character) if prevCameraSaved and root and prevCameraOffset then Camera.CameraType = Enum.CameraType.Scriptable local startCFrame = Camera.CFrame local duration = 1.1 local elapsed = 0 local resetConn resetConn = RunService.RenderStepped:Connect(function(dt) pcall(function() elapsed = elapsed + dt local t = math.clamp(elapsed / duration, 0, 1) local eased if t < 0.5 then eased = 4 * t * t * t else local f = (2 * t) - 2 eased = 0.5 * f * f * f + 1 end local currentRoot = getRootPart(LocalPlayer.Character) if currentRoot then local endCFrame = currentRoot.CFrame * prevCameraOffset Camera.CFrame = startCFrame:Lerp(endCFrame, eased) end if elapsed >= duration then if resetConn then resetConn:Disconnect() resetConn = nil end if prevCameraType then Camera.CameraType = prevCameraType else Camera.CameraType = Enum.CameraType.Custom end if prevCameraType == Enum.CameraType.Custom and prevCameraSubject then pcall(function() Camera.CameraSubject = prevCameraSubject end) elseif prevCameraType == Enum.CameraType.Custom then local humanoid = LocalPlayer.Character and LocalPlayer.Character:FindFirstChildOfClass("Humanoid") if humanoid then pcall(function() Camera.CameraSubject = humanoid end) end end prevCameraSaved = false prevCameraOffset = nil prevCameraType = nil prevCameraSubject = nil end end) end) elseif root then Camera.CameraType = Enum.CameraType.Scriptable local startCFrame = Camera.CFrame local duration = 1.1 local elapsed = 0 local resetConn resetConn = RunService.RenderStepped:Connect(function(dt) pcall(function() elapsed = elapsed + dt local t = math.clamp(elapsed / duration, 0, 1) local eased if t < 0.5 then eased = 4 * t * t * t else local f = (2 * t) - 2 eased = 0.5 * f * f * f + 1 end local currentRoot = getRootPart(LocalPlayer.Character) if currentRoot then local desiredPos = currentRoot.Position + currentRoot.CFrame.LookVector * -6 + Vector3.new(0, 3, 0) local endCFrame = CFrame.lookAt(desiredPos, currentRoot.Position) Camera.CFrame = startCFrame:Lerp(endCFrame, eased) end if elapsed >= duration then if resetConn then resetConn:Disconnect() resetConn = nil end Camera.CameraType = Enum.CameraType.Custom local humanoid = LocalPlayer.Character:FindFirstChildOfClass("Humanoid") if humanoid then Camera.CameraSubject = humanoid end end end) end) else Camera.CameraType = Enum.CameraType.Custom end if hadSpy then setSpyMode(false) end stopSpy() hasInitialAngle = false initialCameraAngle = Vector2.new(0, 0) orbit.maxDistance = DEFAULT_ORBIT_MAX_DISTANCE syncManualIcons() end BackToPlayerButton.MouseButton1Click:Connect(function() if not isSpying then return end tweenGui(BackToPlayerButton, {BackgroundColor3 = THEME.subtleAccent}, TWEEN_INFO_FAST) task.delay(0.12, function() tweenGui(BackToPlayerButton, {BackgroundColor3 = THEME.danger}, TWEEN_INFO_FAST) end) resetCamera() end) MiniBackButton.MouseButton1Click:Connect(function() if not isSpying then return end tweenGui(MiniBackButton, {BackgroundTransparency = 0.5}, TWEEN_INFO_FAST) task.delay(0.12, function() tweenGui(MiniBackButton, {BackgroundTransparency = 0}, TWEEN_INFO_FAST) end) resetCamera() end) MiniManualButton.MouseButton1Click:Connect(function() if not isSpying then return end toggleManualCamera(not isManualCameraActive) end) ToggleCameraButton.MouseButton1Click:Connect(function() if not isSpying then return end toggleManualCamera(not isManualCameraActive) end) local function createNPCEntry(model) local entry = Instance.new("TextButton") entry.Name = "NpcBtn_" .. model:GetFullName() entry.Parent = PlayerFrame entry.Size = UDim2.new(1, -SIZES.EntryPad, 0, SIZES.EntryH) entry.BackgroundColor3 = Color3.fromRGB(38, 38, 40) entry.BorderSizePixel = 0 entry.AutoButtonColor = true entry.Text = "" entry.LayoutOrder = #PlayerFrame:GetChildren() Instance.new("UICorner", entry).CornerRadius = UDim.new(0, SIZES.CornerM) local left = Instance.new("Frame", entry) left.BackgroundTransparency = 1 left.Position = UDim2.new(0, SIZES.AvatarPad, 0, SIZES.AvatarY) left.Size = UDim2.new(0, SIZES.AvatarSize, 1, -(SIZES.AvatarY * 2)) local avatar = Instance.new("ImageLabel", left) avatar.Size = UDim2.new(1, 0, 1, 0) avatar.BackgroundColor3 = Color3.fromRGB(50, 50, 50) avatar.BorderSizePixel = 0 avatar.Image = "rbxassetid://1322223555" avatar.ScaleType = Enum.ScaleType.Crop Instance.new("UICorner", avatar).CornerRadius = UDim.new(0, SIZES.CornerS) local nameX = SIZES.AvatarPad + SIZES.AvatarSize + 10 * scale local btnW = 90 * scale local btnPadding = SIZES.AvatarPad local btnTotalSpace = btnW + btnPadding local nameLabel = Instance.new("TextLabel", entry) nameLabel.BackgroundTransparency = 1 nameLabel.Position = UDim2.new(0, nameX, 0, 8 * scale) nameLabel.Size = UDim2.new(1, -(nameX + btnTotalSpace + 5 * scale), 0, FONTS.Name + 5 * scale) nameLabel.TextXAlignment = Enum.TextXAlignment.Left nameLabel.Text = model.Name nameLabel.TextColor3 = THEME.text nameLabel.Font = Enum.Font.GothamSemibold nameLabel.TextSize = FONTS.Name nameLabel.TextTruncate = Enum.TextTruncate.AtEnd local subLabel = Instance.new("TextLabel", entry) subLabel.BackgroundTransparency = 1 subLabel.Position = UDim2.new(0, nameX, 0, (8 * scale) + FONTS.Name + (2 * scale)) subLabel.Size = UDim2.new(1, -(nameX + btnTotalSpace + 5 * scale), 0, FONTS.Sub + 5 * scale) subLabel.TextXAlignment = Enum.TextXAlignment.Left subLabel.Text = "NPC" subLabel.TextColor3 = THEME.muted subLabel.Font = Enum.Font.Gotham subLabel.TextSize = FONTS.Sub subLabel.TextTruncate = Enum.TextTruncate.AtEnd local viewBtn = Instance.new("TextButton", entry) viewBtn.Size = UDim2.new(0, btnW, 0, SIZES.BtnSize) viewBtn.Position = UDim2.new(1, -(btnW + btnPadding), 0.5, -(SIZES.BtnSize / 2)) viewBtn.BackgroundColor3 = THEME.accent viewBtn.Text = "Ver" viewBtn.TextColor3 = Color3.fromRGB(255, 255, 255) viewBtn.Font = Enum.Font.GothamBold viewBtn.TextSize = FONTS.ViewBtn viewBtn.BorderSizePixel = 0 Instance.new("UICorner", viewBtn).CornerRadius = UDim.new(0, SIZES.CornerS) entry.MouseEnter:Connect(function() tweenGui(entry, {BackgroundColor3 = Color3.fromRGB(45, 45, 47)}, TWEEN_INFO_FAST) end) entry.MouseLeave:Connect(function() tweenGui(entry, {BackgroundColor3 = Color3.fromRGB(38, 38, 40)}, TWEEN_INFO_FAST) end) viewBtn.MouseButton1Click:Connect(function() tweenGui(viewBtn, {BackgroundTransparency = 0.5}, TWEEN_INFO_FAST) task.delay(0.12, function() tweenGui(viewBtn, {BackgroundTransparency = 0}, TWEEN_INFO_FAST) end) startSpyOnCharacter(model) end) return entry end local function createPlayerEntry(player) local entry = Instance.new("TextButton") entry.Name = "PlayerBtn_" .. player.UserId entry.Parent = PlayerFrame entry.Size = UDim2.new(1, -SIZES.EntryPad, 0, SIZES.EntryH) entry.BackgroundColor3 = Color3.fromRGB(38, 38, 40) entry.BorderSizePixel = 0 entry.AutoButtonColor = true entry.Text = "" entry.LayoutOrder = #PlayerFrame:GetChildren() Instance.new("UICorner", entry).CornerRadius = UDim.new(0, SIZES.CornerM) local left = Instance.new("Frame", entry) left.BackgroundTransparency = 1 left.Position = UDim2.new(0, SIZES.AvatarPad, 0, SIZES.AvatarY) left.Size = UDim2.new(0, SIZES.AvatarSize, 1, -(SIZES.AvatarY * 2)) local avatar = Instance.new("ImageLabel", left) avatar.Size = UDim2.new(1, 0, 1, 0) avatar.BackgroundColor3 = Color3.fromRGB(50, 50, 50) avatar.BorderSizePixel = 0 avatar.Image = "rbxthumb://type=AvatarHeadShot&id=" .. tostring(player.UserId) .. "&w=150&h=150" avatar.ScaleType = Enum.ScaleType.Crop Instance.new("UICorner", avatar).CornerRadius = UDim.new(0, SIZES.CornerS) local nameX = SIZES.AvatarPad + SIZES.AvatarSize + 10 * scale local btnW = 90 * scale local btnPadding = SIZES.AvatarPad local btnTotalSpace = btnW + btnPadding local nameLabel = Instance.new("TextLabel", entry) nameLabel.BackgroundTransparency = 1 nameLabel.Position = UDim2.new(0, nameX, 0, 8 * scale) nameLabel.Size = UDim2.new(1, -(nameX + btnTotalSpace + 5 * scale), 0, FONTS.Name + 5 * scale) nameLabel.TextXAlignment = Enum.TextXAlignment.Left nameLabel.Text = safeName(player) nameLabel.TextColor3 = THEME.text nameLabel.Font = Enum.Font.GothamSemibold nameLabel.TextSize = FONTS.Name nameLabel.TextTruncate = Enum.TextTruncate.AtEnd local subLabel = Instance.new("TextLabel", entry) subLabel.BackgroundTransparency = 1 subLabel.Position = UDim2.new(0, nameX, 0, (8 * scale) + FONTS.Name + (2 * scale)) subLabel.Size = UDim2.new(1, -(nameX + btnTotalSpace + 5 * scale), 0, FONTS.Sub + 5 * scale) subLabel.TextXAlignment = Enum.TextXAlignment.Left subLabel.Text = "@" .. player.Name subLabel.TextColor3 = THEME.muted subLabel.Font = Enum.Font.Gotham subLabel.TextSize = FONTS.Sub subLabel.TextTruncate = Enum.TextTruncate.AtEnd local viewBtn = Instance.new("TextButton", entry) viewBtn.Size = UDim2.new(0, btnW, 0, SIZES.BtnSize) viewBtn.Position = UDim2.new(1, -(btnW + btnPadding), 0.5, -(SIZES.BtnSize / 2)) viewBtn.BackgroundColor3 = THEME.accent viewBtn.Text = "Ver" viewBtn.TextColor3 = Color3.fromRGB(255, 255, 255) viewBtn.Font = Enum.Font.GothamBold viewBtn.TextSize = FONTS.ViewBtn viewBtn.BorderSizePixel = 0 Instance.new("UICorner", viewBtn).CornerRadius = UDim.new(0, SIZES.CornerS) entry.MouseEnter:Connect(function() tweenGui(entry, {BackgroundColor3 = Color3.fromRGB(45, 45, 47)}, TWEEN_INFO_FAST) end) entry.MouseLeave:Connect(function() tweenGui(entry, {BackgroundColor3 = Color3.fromRGB(38, 38, 40)}, TWEEN_INFO_FAST) end) viewBtn.MouseButton1Click:Connect(function() tweenGui(viewBtn, {BackgroundTransparency = 0.5}, TWEEN_INFO_FAST) task.delay(0.12, function() tweenGui(viewBtn, {BackgroundTransparency = 0}, TWEEN_INFO_FAST) end) startSpyOnCharacter(player.Character) end) return entry end local function refreshPlayers(filter) filter = (filter or ""):lower() for _, child in ipairs(PlayerFrame:GetChildren()) do if child:IsA("TextButton") and (child.Name:match("^PlayerBtn_") or child.Name:match("^NpcBtn_")) then child:Destroy() end end local visibleCount = 0 for _, player in ipairs(Players:GetPlayers()) do if player == LocalPlayer then continue end local display = (player.DisplayName or player.Name):lower() local username = (player.Name or ""):lower() if filter == "" or display:find(filter, 1, true) or username:find(filter, 1, true) then visibleCount = visibleCount + 1 local entry = createPlayerEntry(player) entry.LayoutOrder = visibleCount entry.BackgroundTransparency = 1 tweenGui(entry, {BackgroundTransparency = 0}, TWEEN_INFO_MED) end end local npcCount = 0 local processedNPCs = {} local currentTime = tick() if currentTime - lastNPCRefresh > NPC_CACHE_TIMEOUT then npcCache = {} lastNPCRefresh = currentTime end for _, descendant in ipairs(Workspace:GetDescendants()) do if descendant:IsA("Humanoid") then local model = descendant.Parent if not processedNPCs[model] then local rootPart = model:FindFirstChild("HumanoidRootPart") if rootPart and descendant.Health > 0 and not Players:GetPlayerFromCharacter(model) then local npcName = model.Name:lower() if filter == "" or npcName:find(filter, 1, true) then processedNPCs[model] = true npcCount = npcCount + 1 local entry = createNPCEntry(model) entry.LayoutOrder = visibleCount + npcCount + 1000 entry.BackgroundTransparency = 1 tweenGui(entry, {BackgroundTransparency = 0}, TWEEN_INFO_MED) end end end end end if visibleCount == 0 and npcCount == 0 then StateLabel.Text = "No se encontraron resultados" StateLabel.Visible = true tweenGui(StateLabel, {TextTransparency = 0}, TWEEN_INFO_FAST) else tweenGui(StateLabel, {TextTransparency = 1}, TWEEN_INFO_FAST) task.delay(0.25, function() StateLabel.Visible = false end) end end PlayerLayout:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(function() local y = PlayerLayout.AbsoluteContentSize.Y PlayerFrame.CanvasSize = UDim2.new(0, 0, 0, y + 10 * scale) end) Players.PlayerAdded:Connect(function(player) local filterText = isPlaceholderActive and "" or SearchBox.Text refreshPlayers(filterText) end) Players.PlayerRemoving:Connect(function(player) if spiedPlayer and player == spiedPlayer then resetCamera() end local filterText = isPlaceholderActive and "" or SearchBox.Text refreshPlayers(filterText) end) SearchBox:GetPropertyChangedSignal("Text"):Connect(function() if SearchBox.Text ~= SearchBox.PlaceholderText and SearchBox.Text ~= "" then isPlaceholderActive = false end if searchDebounce then return end searchDebounce = true task.delay(0.12, function() local filterText = (SearchBox.Text == SearchBox.PlaceholderText or SearchBox.Text == "") and "" or SearchBox.Text refreshPlayers(filterText) searchDebounce = false end) end) SearchBox.Focused:Connect(function() if isPlaceholderActive then SearchBox.Text = "" end end) SearchBox.FocusLost:Connect(function() if SearchBox.Text == "" then isPlaceholderActive = true SearchBox.Text = SearchBox.PlaceholderText refreshPlayers("") end end) refreshPlayers("") local function computeMiniWidth() local children = MiniControls:GetChildren() local btnCount = 0 for _, c in ipairs(children) do if c:IsA("GuiObject") and c.Visible then btnCount = btnCount + 1 end end if btnCount == 0 then return SIZES.MiniMinWidth end local totalIconsWidth = btnCount * SIZES.MiniIconSize local totalPadding = math.max(0, btnCount - 1) * SIZES.MiniBetweenPadding local width = SIZES.MiniSideMargin * 2 + totalIconsWidth + totalPadding width = math.clamp(width, SIZES.MiniMinWidth, SIZES.MiniMaxWidth) return width end local function updateMiniButtons() if isMinimized then MiniManualButton.Visible = isSpying else MiniManualButton.Visible = false end MaximizeMiniButton.Visible = true MiniBackButton.Visible = true syncManualIcons() if isMinimized then local newW = computeMiniWidth() local miniInnerW = newW - (SIZES.MiniSideMargin * 2) tweenGui(MiniControls, {Size = UDim2.new(0, miniInnerW, 0, SIZES.MiniH)}, TWEEN_INFO_MED) tweenGui(MiniControls, {Position = UDim2.new(0.5, -miniInnerW / 2, 0.5, -(SIZES.MiniH / 2))}, TWEEN_INFO_MED) tweenGui(MainFrame, {Size = UDim2.new(0, newW, 0, SIZES.MiniH)}, TWEEN_INFO_MED) end end spawn(function() while true do updateMiniButtons() task.wait(0.35) end end) local function hideContentNow() SearchBox.Visible = false PlayerFrame.Visible = false BackToPlayerButton.Visible = false StateLabel.Visible = false TitleLabel.Visible = false end local function showContentNow() SearchBox.Visible = true PlayerFrame.Visible = true BackToPlayerButton.Visible = true TitleLabel.Visible = true end local function minimizeUI() if isMinimized then return end isMinimized = true hideContentNow() MiniCover.Visible = true MiniCover.BackgroundTransparency = 0 MiniCover.ZIndex = 2 tweenGui(TitleBar, {BackgroundColor3 = THEME.miniPanel}, TWEEN_INFO_FAST) tweenGui(TitleLabel, {TextTransparency = 1}, TWEEN_INFO_FAST) tweenGui(SearchBox, {BackgroundTransparency = 1, TextTransparency = 1}, TWEEN_INFO_FAST) tweenGui(PlayerFrame, {BackgroundTransparency = 1}, TWEEN_INFO_FAST) tweenGui(BackToPlayerButton, {BackgroundTransparency = 1, TextTransparency = 1}, TWEEN_INFO_FAST) updateMiniButtons() task.delay(0.22, function() MaximizeButton.Visible = false ToggleCameraButton.Visible = false MiniControls.Visible = true MiniControls.ZIndex = 4 MiniCover.ZIndex = 1 end) end local function maximizeUI() if not isMinimized then return end isMinimized = false showContentNow() MiniCover.Visible = false TitleLabel.TextTransparency = 1 SearchBox.BackgroundTransparency = 1 SearchBox.TextTransparency = 1 PlayerFrame.BackgroundTransparency = 1 BackToPlayerButton.BackgroundTransparency = 1 BackToPlayerButton.TextTransparency = 1 tweenGui(MainFrame, {Size = UDim2.new(0, SIZES.MainW, 0, SIZES.MainH)}, TWEEN_INFO_MED) MaximizeButton.Visible = true ToggleCameraButton.Visible = isSpying MiniControls.Visible = false tweenGui(TitleBar, {BackgroundColor3 = THEME.panel}, TWEEN_INFO_FAST) tweenGui(TitleLabel, {TextTransparency = 0}, TWEEN_INFO_FAST) tweenGui(SearchBox, {BackgroundTransparency = 0, TextTransparency = 0}, TWEEN_INFO_FAST) tweenGui(PlayerFrame, {BackgroundTransparency = 0}, TWEEN_INFO_FAST) tweenGui(BackToPlayerButton, {BackgroundTransparency = 0, TextTransparency = 0}, TWEEN_INFO_FAST) end MaximizeButton.MouseButton1Click:Connect(function() if not isMinimized then minimizeUI() MaximizeButton.Text = "+" else maximizeUI() MaximizeButton.Text = "—" end end) MaximizeMiniButton.MouseButton1Click:Connect(function() maximizeUI() MaximizeButton.Text = "—" end) local function beginDrag(input) if input.UserInputType ~= Enum.UserInputType.MouseButton1 and input.UserInputType ~= Enum.UserInputType.Touch then return end dragging = true dragStart = input.Position startPos = MainFrame.Position input.Changed:Connect(function() if input.UserInputState == Enum.UserInputState.End then dragging = false end end) end local function updateDrag(input) if not dragging then return end local delta = input.Position - dragStart local newX = startPos.X.Offset + delta.X local newY = startPos.Y.Offset + delta.Y local screenW, screenH = Workspace.CurrentCamera.ViewportSize.X, Workspace.CurrentCamera.ViewportSize.Y local fw, fh = MainFrame.AbsoluteSize.X, MainFrame.AbsoluteSize.Y newX = math.clamp(newX, -fw + 40, screenW - 40) newY = math.clamp(newY, -fh + 24, screenH - 24) MainFrame.Position = UDim2.new(0, newX, 0, newY) end TitleBar.InputBegan:Connect(beginDrag) TitleBar.InputChanged:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then updateDrag(input) end end) MiniControls.InputBegan:Connect(beginDrag) MiniControls.InputChanged:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then updateDrag(input) end end) UserInputService.InputChanged:Connect(function(input) if dragging and (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch) then updateDrag(input) end end) UserInputService.InputBegan:Connect(function(input, gameProcessed) pcall(function() if gameProcessed then return end if (input.KeyCode == Enum.KeyCode.LeftControl or input.KeyCode == Enum.KeyCode.RightControl) and isSpying and isManualCameraActive then ctrlLockActive = not ctrlLockActive if ctrlLockActive then isMouseDown = true pcall(function() UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter end) pcall(function() UserInputService.MouseIconEnabled = false end) else isMouseDown = false pcall(function() UserInputService.MouseBehavior = Enum.MouseBehavior.Default end) pcall(function() UserInputService.MouseIconEnabled = true end) end return end if not isSpying or not isManualCameraActive then return end local isControlInput = input.UserInputType == Enum.UserInputType.MouseButton2 or input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.Gamepad1 if isControlInput and not isMouseDown then if input.UserInputType == Enum.UserInputType.MouseButton2 then isMouseDown = true UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter elseif input.UserInputType == Enum.UserInputType.Touch then isMouseDown = true touchId = input.UserInputType elseif input.UserInputType == Enum.UserInputType.Gamepad1 and input.KeyCode == Enum.KeyCode.Thumbstick2 then isMouseDown = true end end end) end) UserInputService.InputEnded:Connect(function(input, gameProcessed) pcall(function() if not isSpying or not isManualCameraActive then return end local isControlInput = input.UserInputType == Enum.UserInputType.MouseButton2 or input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.Gamepad1 if isControlInput and isMouseDown and not ctrlLockActive then if input.UserInputType == Enum.UserInputType.MouseButton2 then isMouseDown = false UserInputService.MouseBehavior = Enum.MouseBehavior.Default elseif input.UserInputType == Enum.UserInputType.Touch then isMouseDown = false touchId = nil elseif input.UserInputType == Enum.UserInputType.Gamepad1 and input.KeyCode == Enum.KeyCode.Thumbstick2 then isMouseDown = false end end end) end) UserInputService.InputChanged:Connect(function(input, gameProcessed) pcall(function() if not isSpying or not isManualCameraActive then return end if isMouseDown then if input.UserInputType == Enum.UserInputType.MouseMovement then local delta = input.Delta * CAMERA_ROTATION_SENSITIVITY cameraAngle = cameraAngle + Vector2.new(-delta.X, -delta.Y) elseif input.UserInputType == Enum.UserInputType.Touch and touchId then local delta = input.Delta * CAMERA_ROTATION_SENSITIVITY * (isMobile and 1 or 2) cameraAngle = cameraAngle + Vector2.new(-delta.X, -delta.Y) elseif input.UserInputType == Enum.UserInputType.Gamepad1 and input.KeyCode == Enum.KeyCode.Thumbstick2 then local delta = Vector2.new(-input.Position.X, input.Position.Y) * CAMERA_ROTATION_SENSITIVITY * 10 cameraAngle = cameraAngle + delta end cameraAngle = Vector2.new(cameraAngle.X, math.clamp(cameraAngle.Y, -math.pi/2 + 0.01, math.pi/2 - 0.01)) end if input.UserInputType == Enum.UserInputType.MouseWheel then local mousePos = UserInputService:GetMouseLocation() local absPos = MainFrame.AbsolutePosition local absSize = MainFrame.AbsoluteSize local overUI = mousePos.X >= absPos.X and mousePos.X <= absPos.X + absSize.X and mousePos.Y >= absPos.Y and mousePos.Y <= absPos.Y + absSize.Y if not overUI then zoomDelta = zoomDelta + input.Position.Z * CAMERA_ZOOM_SPEED end end if input.UserInputType == Enum.UserInputType.Gamepad1 then if input.KeyCode == Enum.KeyCode.ButtonL1 or input.KeyCode == Enum.KeyCode.ButtonL2 then if input.UserInputState == Enum.UserInputState.Begin then zoomDelta = zoomDelta - CAMERA_ZOOM_SPEED * 2 end elseif input.KeyCode == Enum.KeyCode.ButtonR1 or input.KeyCode == Enum.KeyCode.ButtonR2 then if input.UserInputState == Enum.UserInputState.Begin then zoomDelta = zoomDelta + CAMERA_ZOOM_SPEED * 2 end end end end) end) UserInputService.TouchPinch:Connect(function(touchPositions, scale, velocity, state) pcall(function() if not isSpying or not isManualCameraActive then return end if state == Enum.UserInputState.Begin then lastTouchScale = scale elseif state == Enum.UserInputState.Change then if lastTouchScale then local difference = scale - lastTouchScale zoomDelta = zoomDelta + difference * PINCH_ZOOM_SENSITIVITY lastTouchScale = scale end elseif state == Enum.UserInputState.End then lastTouchScale = nil end end) end) UserInputService.InputBegan:Connect(function(input, gameProcessed) if gameProcessed then return end if input.KeyCode == Enum.KeyCode.Escape then if isMinimized then maximizeUI() else minimizeUI() end end end) MainFrame.Position = UDim2.new(0, SIZES.MainX, 0, -500) tweenGui(MainFrame, {Position = UDim2.new(0, SIZES.MainX, 0, SIZES.MainY)}, TWEEN_INFO_SLOW) tweenGui(MainFrame, {Size = UDim2.new(0, SIZES.MainW, 0, SIZES.MainH)}, TWEEN_INFO_SLOW)