Загрузка данных


local sensitivity_pc = 0.8
local sensitivity_mobile = 1.5
local first_person_key = Enum.KeyCode.F

local camera_height = 1.5
local forward_offset = 0

local max_angl = math.rad(89)
local mouse_cursor = false
local smooth = 0.15

-- FOV
local first_person_fov = 90
local third_person_fov = 80

-- Покачивание камеры
local walk_bob_speed = 8
local walk_bob_amount = 0.02
local run_bob_speed = 12
local run_bob_amount = 0.04
local side_bob_amount = 0.015

-- 3-е лицо
local third_person_distance = 8
local third_person_height = 2

local uis = game:GetService("UserInputService")
local runService = game:GetService("RunService")

local player = game.Players.LocalPlayer
local camera = workspace.CurrentCamera

local currentPitch = 0
local currentYaw = 0
local targetPitch = 0
local targetYaw = 0

local draggingTouch = false
local lastTouch = Vector2.new(0, 0)

local firstPerson = true

local sensitivity = uis.TouchEnabled and sensitivity_mobile or sensitivity_pc

local hiddenParts = {}

local crosshairGui
local crosshair


--------------------------------------------------
-- СКРЫТИЕ ЧАСТИ
--------------------------------------------------

local function hidePart(part)
	if not part or not part.Parent then
		return
	end

	if part:IsA("BasePart") then
		if hiddenParts[part] == nil then
			hiddenParts[part] = part.LocalTransparencyModifier
		end

		part.LocalTransparencyModifier = 1
	end
end


--------------------------------------------------
-- ПЕРВОЕ ЛИЦО
-- ГОЛОВА скрывается
-- ТОРС НЕ СКРЫВАЕТСЯ
--------------------------------------------------

local function hideFirstPersonBody(character)

	-- Возвращаем всё, что было скрыто раньше
	for object in pairs(hiddenParts) do
		if object and object.Parent then
			if object:IsA("BasePart") then
				object.LocalTransparencyModifier = 0
			end
		end
	end

	hiddenParts = {}

	-- Скрываем только голову
	local head = character:FindFirstChild("Head")

	if head then
		hidePart(head)

		for _, object in ipairs(head:GetDescendants()) do
			if object:IsA("BasePart") then
				hidePart(object)
			end
		end
	end

	-- ВАЖНО:
	-- ТОРС НИКОГДА НЕ СКРЫВАЕМ

	for _, torsoName in ipairs({
		"Torso",
		"UpperTorso",
		"LowerTorso"
	}) do

		local torso = character:FindFirstChild(torsoName)

		if torso and torso:IsA("BasePart") then
			torso.LocalTransparencyModifier = 0
		end
	end
end


--------------------------------------------------
-- ПОКАЗАТЬ ВСЁ
--------------------------------------------------

local function showHiddenParts()

	for object, oldValue in pairs(hiddenParts) do

		if object and object.Parent then

			if object:IsA("BasePart") then
				object.LocalTransparencyModifier = oldValue
			end

		end
	end

	hiddenParts = {}

end


--------------------------------------------------
-- ПРИНУДИТЕЛЬНО ПОКАЗЫВАЕМ ТОРС
--------------------------------------------------

local function showTorso(character)

	for _, torsoName in ipairs({
		"Torso",
		"UpperTorso",
		"LowerTorso"
	}) do

		local torso = character:FindFirstChild(torsoName)

		if torso and torso:IsA("BasePart") then
			torso.LocalTransparencyModifier = 0
		end

	end

end


--------------------------------------------------
-- ПРИЦЕЛ
--------------------------------------------------

local function createCrosshair()

	if crosshairGui then
		crosshairGui:Destroy()
	end

	crosshairGui = Instance.new("ScreenGui")
	crosshairGui.Name = "CameraCrosshair"
	crosshairGui.ResetOnSpawn = false
	crosshairGui.IgnoreGuiInset = true
	crosshairGui.Parent = player:WaitForChild("PlayerGui")

	crosshair = Instance.new("TextLabel")
	crosshair.Name = "Crosshair"

	crosshair.AnchorPoint = Vector2.new(0.5, 0.5)
	crosshair.Position = UDim2.fromScale(0.5, 0.5)

	crosshair.Size = UDim2.fromOffset(24, 24)

	crosshair.BackgroundTransparency = 1

	crosshair.Text = "+"
	crosshair.TextColor3 = Color3.new(1, 1, 1)
	crosshair.TextSize = 18
	crosshair.Font = Enum.Font.GothamBold

	crosshair.Visible = firstPerson
	crosshair.Parent = crosshairGui

end


--------------------------------------------------
-- ПЕРЕКЛЮЧЕНИЕ 1 / 3 ЛИЦО
--------------------------------------------------

local function toggleViewMode()

	local character = player.Character

	if not character then
		return
	end

	firstPerson = not firstPerson

	if firstPerson then

		hideFirstPersonBody(character)

		showTorso(character)

		camera.FieldOfView = first_person_fov

	else

		showHiddenParts()

		showTorso(character)

		camera.FieldOfView = third_person_fov

	end

	if crosshair then
		crosshair.Visible = firstPerson
	end

end


--------------------------------------------------
-- КАМЕРА
--------------------------------------------------

local function updateCamera()

	local character = player.Character

	if not character then
		return
	end

	local hrp = character:FindFirstChild("HumanoidRootPart")
	local humanoid = character:FindFirstChildOfClass("Humanoid")

	if not hrp or not humanoid then
		return
	end


	--------------------------------------------------
	-- ТОРС ВСЕГДА ВИДЕН
	--------------------------------------------------

	showTorso(character)


	--------------------------------------------------
	-- ПЛАВНОСТЬ
	--------------------------------------------------

	currentYaw =
		currentYaw
		+ (targetYaw - currentYaw) * smooth

	currentPitch =
		currentPitch
		+ (targetPitch - currentPitch) * smooth


	local rotation =
		CFrame.Angles(0, currentYaw, 0)
		* CFrame.Angles(currentPitch, 0, 0)


	--------------------------------------------------
	-- ПЕРВОЕ ЛИЦО
	--------------------------------------------------

	if firstPerson then

		-- НЕ даём Humanoid самому
		-- крутить персонажа
		humanoid.AutoRotate = false


		local bob = CFrame.new()


		--------------------------------------------------
		-- ПОКАЧИВАНИЕ ПРИ ХОДЬБЕ
		--------------------------------------------------

		if humanoid.MoveDirection.Magnitude > 0.05
			and humanoid.FloorMaterial ~= Enum.Material.Air then

			local running = humanoid.WalkSpeed > 16

			local speed =
				running and run_bob_speed
				or walk_bob_speed

			local amount =
				running and run_bob_amount
				or walk_bob_amount

			local t = os.clock() * speed

			local x =
				math.cos(t * 0.5)
				* side_bob_amount

			local y =
				math.abs(math.sin(t))
				* amount

			bob =
				CFrame.new(x, y, 0)
				* CFrame.Angles(
					0,
					0,
					math.sin(t * 0.5) * 0.008
				)

		end


		--------------------------------------------------
		-- ПОЗИЦИЯ КАМЕРЫ
		--------------------------------------------------

		local cameraPosition =
			hrp.Position
			+ Vector3.new(
				0,
				camera_height,
				forward_offset
			)


		camera.CFrame =
			CFrame.new(cameraPosition)
			* rotation
			* bob


		camera.FieldOfView = first_person_fov


	--------------------------------------------------
	-- ТРЕТЬЕ ЛИЦО
	--------------------------------------------------

	else

		humanoid.AutoRotate = true

		local cameraDirection =
			rotation.LookVector

		local cameraPosition =
			hrp.Position
			- cameraDirection
			* third_person_distance
			+ Vector3.new(
				0,
				third_person_height,
				0
			)


		camera.CFrame =
			CFrame.lookAt(
				cameraPosition,
				hrp.Position
				+ Vector3.new(0, 1.5, 0)
			)


		camera.FieldOfView = third_person_fov

	end


	--------------------------------------------------
	-- ПРИЦЕЛ
	--------------------------------------------------

	if crosshair then
		crosshair.Visible = firstPerson
	end

end


--------------------------------------------------
-- НАСТРОЙКА КАМЕРЫ
--------------------------------------------------

local function setupCamera(character)

	camera.CameraType =
		Enum.CameraType.Scriptable

	camera.FieldOfView =
		first_person_fov

	uis.MouseIconEnabled =
		mouse_cursor

	firstPerson = true


	local humanoid =
		character:FindFirstChildOfClass("Humanoid")

	if humanoid then
		humanoid.AutoRotate = false
	end


	hideFirstPersonBody(character)
	showTorso(character)

	createCrosshair()


	--------------------------------------------------
	-- КНОПКА НА ТЕЛЕФОНЕ
	--------------------------------------------------

	if uis.TouchEnabled then

		local playerGui =
			player:WaitForChild("PlayerGui")

		local oldGui =
			playerGui:FindFirstChild(
				"MobileViewToggle"
			)

		if oldGui then
			oldGui:Destroy()
		end


		local gui =
			Instance.new("ScreenGui")

		gui.Name =
			"MobileViewToggle"

		gui.ResetOnSpawn =
			false

		gui.Parent =
			playerGui


		local button =
			Instance.new("TextButton")

		button.Name =
			"ToggleButton"

		button.Size =
			UDim2.fromOffset(30, 30)

		button.Position =
			UDim2.fromOffset(20, 20)

		button.BackgroundColor3 =
			Color3.new(
				0.2,
				0.2,
				0.2
			)

		button.BackgroundTransparency =
			0.3

		button.BorderSizePixel =
			0

		button.Text =
			"1"

		button.TextColor3 =
			Color3.new(1, 1, 1)

		button.TextScaled =
			true

		button.Parent =
			gui


		local corner =
			Instance.new("UICorner")

		corner.CornerRadius =
			UDim.new(0, 10)

		corner.Parent =
			button


		button.MouseButton1Click:Connect(
			function()

				toggleViewMode()

				button.Text =
					firstPerson
					and "1"
					or "3"

			end
		)

	end

end


--------------------------------------------------
-- ПЕРСОНАЖ ПОЯВИЛСЯ
--------------------------------------------------

local function onCharacterAdded(character)

	character:WaitForChild(
		"HumanoidRootPart"
	)

	currentPitch = 0
	currentYaw = 0

	targetPitch = 0
	targetYaw = 0


	setupCamera(character)


	uis.MouseBehavior =
		Enum.MouseBehavior.LockCurrentPosition


	--------------------------------------------------
	-- ЕСЛИ ДОБАВИЛСЯ АКСЕССУАР
	--------------------------------------------------

	character.ChildAdded:Connect(
		function(child)

			if firstPerson
				and child:IsA("Accessory") then

				task.wait()

				if character.Parent then
					hideFirstPersonBody(character)
					showTorso(character)
				end

			end

		end
	)

end


--------------------------------------------------
-- МЫШЬ / TOUCH
--------------------------------------------------

uis.InputChanged:Connect(
	function(input, gameProcessed)

		if gameProcessed then
			return
		end


		--------------------------------------------------
		-- МЫШЬ
		--------------------------------------------------

		if input.UserInputType
			== Enum.UserInputType.MouseMovement then

			local delta =
				input.Delta


			targetYaw =
				targetYaw
				- delta.X
				* sensitivity
				* 0.005


			targetPitch =
				targetPitch
				- delta.Y
				* sensitivity
				* 0.005


			targetPitch =
				math.clamp(
					targetPitch,
					-max_angl,
					max_angl
				)


		--------------------------------------------------
		-- ТЕЛЕФОН
		--------------------------------------------------

		elseif input.UserInputType
			== Enum.UserInputType.Touch
			and draggingTouch then


			local position =
				Vector2.new(
					input.Position.X,
					input.Position.Y
				)


			local delta =
				position - lastTouch


			lastTouch =
				position


			targetYaw =
				targetYaw
				- delta.X
				* sensitivity
				* 0.005


			targetPitch =
				targetPitch
				- delta.Y
				* sensitivity
				* 0.005


			targetPitch =
				math.clamp(
					targetPitch,
					-max_angl,
					max_angl
				)

		end

	end
)


--------------------------------------------------
-- НАЖАТИЕ
--------------------------------------------------

uis.InputBegan:Connect(
	function(input, gameProcessed)

		if gameProcessed then
			return
		end


		--------------------------------------------------
		-- ПКМ
		--------------------------------------------------

		if input.UserInputType
			== Enum.UserInputType.MouseButton2 then


			if uis.MouseBehavior
				== Enum.MouseBehavior.LockCurrentPosition then

				uis.MouseBehavior =
					Enum.MouseBehavior.Default

			else

				uis.MouseBehavior =
					Enum.MouseBehavior.LockCurrentPosition

			end


		--------------------------------------------------
		-- TOUCH
		--------------------------------------------------

		elseif input.UserInputType
			== Enum.UserInputType.Touch then


			draggingTouch =
				true


			lastTouch =
				Vector2.new(
					input.Position.X,
					input.Position.Y
				)


		--------------------------------------------------
		-- F = ПЕРЕКЛЮЧЕНИЕ
		--------------------------------------------------

		elseif input.KeyCode
			== first_person_key
			and first_person_key ~= 0 then


			toggleViewMode()

		end

	end
)


--------------------------------------------------
-- ОТПУСКАНИЕ TOUCH
--------------------------------------------------

uis.InputEnded:Connect(
	function(input)

		if input.UserInputType
			== Enum.UserInputType.Touch then

			draggingTouch =
				false

		end

	end
)


--------------------------------------------------
-- RENDER
--------------------------------------------------

runService:BindToRenderStep(
	"CustomCamera",
	Enum.RenderPriority.Camera.Value + 1,
	updateCamera
)


--------------------------------------------------
-- ЗАПУСК
--------------------------------------------------

if player.Character then
	onCharacterAdded(
		player.Character
	)
end


player.CharacterAdded:Connect(
	onCharacterAdded
)


--------------------------------------------------
-- ВЫХОД
--------------------------------------------------

player.AncestryChanged:Connect(
	function()

		if not player.Parent then

			showHiddenParts()

			uis.MouseBehavior =
				Enum.MouseBehavior.Default

		end

	end
)