local Players = game:GetService("Players") local UserInputService = game:GetService("UserInputService") local Camera = workspace.CurrentCamera local RunService = game:GetService("RunService") local player = Players.LocalPlayer local isHolding = false local anchorPart = nil local visualBall = nil local laserPart = nil local handPart = nil local radius = 0 local theta = 0 local phi = 0 local targetTheta = 0 local targetPhi = 0 local startTheta = 0 local startPhi = 0 local sensitivity = 0.015 local maxSpeed = 250 local isFloor = false local isWall = false local isFlying = false -- НОВОЕ: флаг полёта local function getRoot() local char = player.Character if char then return char:FindFirstChild("HumanoidRootPart"), char:FindFirstChild("Humanoid") end return nil, nil end local function createVisualBall() if visualBall then return end visualBall = Instance.new("Part") visualBall.Name = "FlingBall" visualBall.Shape = Enum.PartType.Ball visualBall.Size = Vector3.new(0.5, 0.5, 0.5) visualBall.Material = Enum.Material.Neon visualBall.Color = Color3.fromRGB(0, 255, 0) visualBall.Anchored = true visualBall.CanCollide = false visualBall.CanQuery = false visualBall.Parent = workspace end local function createHand() if handPart then return end handPart = Instance.new("Part") handPart.Name = "FlingHand" handPart.Size = Vector3.new(1, 1, 1) handPart.Transparency = 1 handPart.CanCollide = false handPart.CanQuery = false handPart.Anchored = false handPart.Massless = true handPart.Parent = workspace end local function updateHand() local rootPart = getRoot() if rootPart and handPart then handPart.CFrame = rootPart.CFrame * CFrame.new(0, 0, -2) end end local function updateLaser(endPos) if not laserPart then laserPart = Instance.new("Part") laserPart.Name = "FlingLaser" laserPart.Anchored = true laserPart.CanCollide = false laserPart.CanQuery = false laserPart.CanTouch = false laserPart.Material = Enum.Material.Neon laserPart.Color = Color3.fromRGB(50, 255, 50) laserPart.Transparency = 0.5 laserPart.Size = Vector3.new(0.2, 0.2, 1) laserPart.Parent = workspace end local startPos = handPart.Position local distance = (endPos - startPos).Magnitude if distance < 0.1 then distance = 0.1 end laserPart.Size = Vector3.new(0.2, 0.2, distance) laserPart.CFrame = CFrame.lookAt(startPos, endPos) * CFrame.new(0, 0, -distance / 2) end local function destroyLaser() if laserPart then laserPart:Destroy() laserPart = nil end end local function getSurface() local screenCenter = Camera.ViewportSize / 2 local unitRay = Camera:ViewportPointToRay(screenCenter.X, screenCenter.Y) local raycastParams = RaycastParams.new() raycastParams.FilterDescendantsInstances = {player.Character, laserPart, handPart, anchorPart, visualBall} raycastParams.FilterType = Enum.RaycastFilterType.Exclude local result = workspace:Raycast(unitRay.Origin, unitRay.Direction * 50, raycastParams) if result then updateLaser(result.Position) return result.Position, result.Normal, true end updateLaser(unitRay.Origin + unitRay.Direction * 50) return unitRay.Origin + unitRay.Direction * 50, nil, false end local function getSphericalFromPosition(pos, origin) local offset = pos - origin local r = offset.Magnitude if r < 0.01 then r = 0.01 end local t = math.atan2(offset.Z, offset.X) local p = math.acos(math.clamp(offset.Y / r, -1, 1)) return r, t, p end local function getPositionFromSpherical(origin, r, t, p) local x = r * math.sin(p) * math.cos(t) local y = r * math.cos(p) local z = r * math.sin(p) * math.sin(t) return origin + Vector3.new(x, y, z) end local function tryGrab() local rootPart, humanoid = getRoot() if not rootPart or not humanoid then return end local hitPos, hitNormal, didHit = getSurface() if not didHit or not hitNormal then return end if anchorPart then anchorPart:Destroy() end anchorPart = Instance.new("Part") anchorPart.Name = "CatapultAnchor" anchorPart.Size = Vector3.new(0.2, 0.2, 0.2) anchorPart.Transparency = 1 anchorPart.CanCollide = false anchorPart.CanQuery = false anchorPart.Anchored = true anchorPart.Position = hitPos + (hitNormal * 5) anchorPart.Parent = workspace if math.abs(hitNormal.Y) > 0.7 then isFloor = true isWall = false else isFloor = false isWall = true end rootPart.Anchored = true rootPart.CanCollide = false radius, theta, phi = getSphericalFromPosition(rootPart.Position, anchorPart.Position) startTheta = theta startPhi = phi targetTheta = theta targetPhi = phi end local function doFling() local rootPart, humanoid = getRoot() if not rootPart then return end rootPart.Anchored = false rootPart.CanCollide = true if anchorPart then anchorPart:Destroy() anchorPart = nil end local angleDiff = math.abs(targetTheta - startTheta) + math.abs(targetPhi - startPhi) local speed = maxSpeed if angleDiff < 0.1 then speed = 80 end local impulseDir = Vector3.new(0, 0, 0) if isFloor then local forwardDir = rootPart.CFrame.LookVector forwardDir = Vector3.new(forwardDir.X, 0, forwardDir.Z).Unit if forwardDir.Magnitude < 0.1 then local camLook = Camera.CFrame.LookVector forwardDir = Vector3.new(camLook.X, 0, camLook.Z).Unit end impulseDir = forwardDir * speed impulseDir = impulseDir + Vector3.new(0, 30, 0) elseif isWall then impulseDir = Vector3.new(0, speed, 0) end rootPart.AssemblyLinearVelocity = impulseDir rootPart.AssemblyAngularVelocity = Vector3.new(0, 0, 0) -- Включаем режим полёта isFlying = true task.delay(2, function() isFlying = false end) radius = 0 theta = 0 phi = 0 isFloor = false isWall = false end UserInputService.InputBegan:Connect(function(input, gameProcessed) if gameProcessed then return end if input.UserInputType == Enum.UserInputType.MouseButton1 then if not isHolding then isHolding = true createHand() createVisualBall() tryGrab() end end end) UserInputService.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 and isHolding then isHolding = false destroyLaser() if visualBall then visualBall:Destroy() visualBall = nil end doFling() end end) UserInputService.InputChanged:Connect(function(input) if isHolding and input.UserInputType == Enum.UserInputType.MouseMovement then if isFloor then targetTheta = targetTheta - (input.Delta.X * sensitivity) targetPhi = phi elseif isWall then targetTheta = theta targetPhi = targetPhi + (input.Delta.Y * sensitivity) end targetPhi = math.clamp(targetPhi, 0.05, math.pi - 0.05) end end) RunService.RenderStepped:Connect(function(dt) updateHand() -- ЛОГИКА ПОЛЁТА (управление в воздухе) if isFlying then local rootPart = getRoot() if rootPart then local moveDir = Vector3.new(0, 0, 0) if UserInputService:IsKeyDown(Enum.KeyCode.W) then moveDir = moveDir + Camera.CFrame.LookVector end if UserInputService:IsKeyDown(Enum.KeyCode.S) then moveDir = moveDir - Camera.CFrame.LookVector end if UserInputService:IsKeyDown(Enum.KeyCode.A) then moveDir = moveDir - Camera.CFrame.RightVector end if UserInputService:IsKeyDown(Enum.KeyCode.D) then moveDir = moveDir + Camera.CFrame.RightVector end if moveDir.Magnitude > 0 then -- Добавляем силу, чтобы можно было немного менять траекторию rootPart:ApplyImpulse(moveDir.Unit * 20) end end end if visualBall and handPart then local screenCenter = Camera.ViewportSize / 2 local unitRay = Camera:ViewportPointToRay(screenCenter.X, screenCenter.Y) local raycastParams = RaycastParams.new() raycastParams.FilterDescendantsInstances = {player.Character, laserPart, handPart, anchorPart, visualBall} raycastParams.FilterType = Enum.RaycastFilterType.Exclude local result = workspace:Raycast(unitRay.Origin, unitRay.Direction * 50, raycastParams) local endPos = unitRay.Origin + unitRay.Direction * 50 if result then endPos = result.Position visualBall.Color = Color3.fromRGB(0, 255, 0) else visualBall.Color = Color3.fromRGB(255, 0, 0) end visualBall.CFrame = CFrame.new(endPos) if isHolding then updateLaser(endPos) end end if isHolding and anchorPart then local rootPart, humanoid = getRoot() if rootPart and radius > 0 then theta = theta + (targetTheta - theta) * 0.9 phi = phi + (targetPhi - phi) * 0.9 local targetPos = getPositionFromSpherical(anchorPart.Position, radius, theta, phi) if targetPos.Y < anchorPart.Position.Y then targetPos = Vector3.new(targetPos.X, anchorPart.Position.Y, targetPos.Z) end local lookDir = (targetPos - rootPart.Position) if lookDir.Magnitude > 0.01 then lookDir = lookDir.Unit else lookDir = (anchorPart.Position - rootPart.Position).Unit end rootPart.CFrame = CFrame.new(targetPos, targetPos + lookDir) end end end)