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


local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")

local player = Players.LocalPlayer

-- Camera settings
local NORMAL_FOV = 70
local SPRINT_FOV = 78

-- Sprint
local SPRINT_SPEED = 20
local SPRINT_SHAKE = 0.32
local SPRINT_SHAKE_SPEED = 32

-- Walking / running
local WALK_BOB_AMOUNT = 0.035
local RUN_BOB_AMOUNT = 0.065

local WALK_BOB_SPEED = 8
local RUN_BOB_SPEED = 12

-- Breathing
local BREATH_AMOUNT = 0.008
local BREATH_SPEED = 1.5

-- Landing
local NORMAL_LANDING_SHAKE = 0.12
local HEAVY_LANDING_SHAKE = 0.30
local MIN_HEAVY_FALL = 5
local MAX_HEAVY_FALL = 25

-- Damage
local DAMAGE_SHAKE = 0.18

-- Low health
local LOW_HEALTH_THRESHOLD = 0.35
local LOW_HEALTH_SHAKE = 0.025
local LOW_HEALTH_SPEED = 5

-- Fatigue
local MAX_RUN_TIME = 12
local MAX_FATIGUE = 1

-- Smoothness
local EFFECT_SMOOTH = 10
local FOV_SMOOTH = 7

-- Head tilt
local HEAD_TILT_AMOUNT = 1.2

local character
local humanoid
local rootPart

local bobTime = 0
local lowHealthTime = 0
local impactTime = 0
local sprintShakeTime = 0

local shake = 0
local fatigue = 0
local runTime = 0

local lastHealth = 100
local wasAirborne = false
local fallStartY = nil

local sprinting = false

local currentOffset = Vector3.zero
local currentRotation = Vector3.zero

--------------------------------------------------
-- CHARACTER
--------------------------------------------------

local function setupCharacter(char)
	character = char
	humanoid = char:WaitForChild("Humanoid")
	rootPart = char:WaitForChild("HumanoidRootPart")

	lastHealth = humanoid.Health
	wasAirborne = false
	fallStartY = nil

	bobTime = 0
	lowHealthTime = 0
	impactTime = 0
	sprintShakeTime = 0

	shake = 0
	fatigue = 0
	runTime = 0

	currentOffset = Vector3.zero
	currentRotation = Vector3.zero

	humanoid.HealthChanged:Connect(function(health)
		if health < lastHealth then
			shake = math.max(shake, DAMAGE_SHAKE)
			impactTime = 0
		end

		lastHealth = health
	end)
end

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

player.CharacterAdded:Connect(setupCharacter)

--------------------------------------------------
-- PC SPRINT
--------------------------------------------------

UserInputService.InputBegan:Connect(function(input, processed)
	if processed then
		return
	end

	if input.KeyCode == Enum.KeyCode.R then
		sprinting = true
	end
end)

UserInputService.InputEnded:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.R then
		sprinting = false
	end
end)

--------------------------------------------------
-- MOBILE SPRINT BUTTON
--------------------------------------------------

local playerGui = player:WaitForChild("PlayerGui")

local sprintGui = Instance.new("ScreenGui")
sprintGui.Name = "SprintGui"
sprintGui.ResetOnSpawn = false
sprintGui.IgnoreGuiInset = true
sprintGui.Parent = playerGui

local sprintButton = Instance.new("TextButton")
sprintButton.Name = "SprintButton"
sprintButton.Size = UDim2.fromOffset(90, 90)
sprintButton.Position = UDim2.new(1, -120, 1, -145)
sprintButton.AnchorPoint = Vector2.new(0.5, 0.5)

sprintButton.BackgroundTransparency = 0.25
sprintButton.BackgroundColor3 = Color3.fromRGB(30, 30, 30)

sprintButton.Text = "SPRINT"
sprintButton.TextColor3 = Color3.new(1, 1, 1)
sprintButton.TextScaled = true
sprintButton.Font = Enum.Font.GothamBold

sprintButton.Parent = sprintGui

local corner = Instance.new("UICorner")
corner.CornerRadius = UDim.new(1, 0)
corner.Parent = sprintButton

local stroke = Instance.new("UIStroke")
stroke.Thickness = 2
stroke.Transparency = 0.35
stroke.Parent = sprintButton

sprintButton.MouseButton1Down:Connect(function()
	sprinting = true
end)

sprintButton.MouseButton1Up:Connect(function()
	sprinting = false
end)

sprintButton.MouseLeave:Connect(function()
	sprinting = false
end)

--------------------------------------------------
-- CAMERA EFFECTS
--------------------------------------------------

RunService:BindToRenderStep(
	"AdvancedCameraEffects",
	Enum.RenderPriority.Camera.Value + 5,
	function(deltaTime)

		local camera = workspace.CurrentCamera

		if not character or not humanoid or not rootPart then
			return
		end

		if humanoid.Health <= 0 then
			return
		end

		--------------------------------------------------
		-- MOVEMENT
		--------------------------------------------------

		local velocity = Vector3.new(
			rootPart.AssemblyLinearVelocity.X,
			0,
			rootPart.AssemblyLinearVelocity.Z
		)

		local speed = velocity.Magnitude
		local moving = speed > 1

		local isSprinting =
			sprinting
			and moving
			and not (
				humanoid:GetState() == Enum.HumanoidStateType.Jumping
				or humanoid:GetState() == Enum.HumanoidStateType.Freefall
			)

		--------------------------------------------------
		-- STATE
		--------------------------------------------------

		local state = humanoid:GetState()

		local airborne =
			state == Enum.HumanoidStateType.Jumping
			or state == Enum.HumanoidStateType.Freefall

		--------------------------------------------------
		-- FALL DETECTION
		--------------------------------------------------

		if airborne and not wasAirborne then
			fallStartY = rootPart.Position.Y
		end

		if not airborne and wasAirborne then

			if fallStartY then

				local fallDistance =
					math.max(
						0,
						fallStartY - rootPart.Position.Y
					)

				if fallDistance >= MIN_HEAVY_FALL then

					local strength =
						math.clamp(
							(fallDistance - MIN_HEAVY_FALL)
								/ (MAX_HEAVY_FALL - MIN_HEAVY_FALL),
							0,
							1
						)

					local landingStrength =
						NORMAL_LANDING_SHAKE
						+ strength
							* (HEAVY_LANDING_SHAKE - NORMAL_LANDING_SHAKE)

					shake = math.max(
						shake,
						landingStrength
					)

					impactTime = 0

				else
					shake = math.max(
						shake,
						NORMAL_LANDING_SHAKE
					)
				end
			end

			fallStartY = nil
		end

		wasAirborne = airborne

		--------------------------------------------------
		-- FATIGUE
		--------------------------------------------------

		if isSprinting then

			runTime = math.min(
				runTime + deltaTime,
				MAX_RUN_TIME
			)

			fatigue =
				math.clamp(
					runTime / MAX_RUN_TIME,
					0,
					MAX_FATIGUE
				)

		else

			runTime = math.max(
				runTime - deltaTime * 2,
				0
			)

			fatigue =
				math.clamp(
					runTime / MAX_RUN_TIME,
					0,
					MAX_FATIGUE
				)
		end

		--------------------------------------------------
		-- BOB
		--------------------------------------------------

		local targetOffset = Vector3.zero
		local targetRotation = Vector3.zero

		if moving and not airborne then

			local bobSpeed
			local bobAmount

			if isSprinting then
				bobSpeed = RUN_BOB_SPEED
				bobAmount =
					RUN_BOB_AMOUNT
					* (1 + fatigue * 0.35)
			else
				bobSpeed = WALK_BOB_SPEED
				bobAmount = WALK_BOB_AMOUNT
			end

			bobTime += deltaTime * bobSpeed

			local vertical =
				math.sin(bobTime * 2)
				* bobAmount

			local horizontal =
				math.cos(bobTime)
				* bobAmount
				* 0.35

			targetOffset = Vector3.new(
				horizontal,
				vertical,
				0
			)

			local headTilt =
				math.sin(bobTime)
				* HEAD_TILT