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


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

local player = Players.LocalPlayer

--------------------------------------------------
-- SETTINGS
--------------------------------------------------

-- Walking
local WALK_BOB_AMOUNT = 0.035
local WALK_BOB_SPEED = 8

-- Sprint
local SPRINT_BOB_AMOUNT = 0.065
local SPRINT_BOB_SPEED = 12

-- Head tilt
local HEAD_TILT_AMOUNT = 1.2

-- Breathing
local BREATH_AMOUNT = 0.008
local BREATH_SPEED = 1.5

-- Landing
local 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_AMOUNT = 0.025
local LOW_HEALTH_SPEED = 5

-- Fatigue
local MAX_RUN_TIME = 12
local MAX_FATIGUE = 1

-- Smoothness
local EFFECT_SMOOTH = 10

--------------------------------------------------
-- VARIABLES
--------------------------------------------------

local character
local humanoid
local rootPart

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

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

local wasAirborne = false
local fallStartY = nil

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

local lastHealth = 100

--------------------------------------------------
-- CHARACTER SETUP
--------------------------------------------------

local function setupCharacter(char)

	character = char

	humanoid =
		char:WaitForChild("Humanoid")

	rootPart =
		char:WaitForChild("HumanoidRootPart")

	lastHealth = humanoid.Health

	bobTime = 0
	impactTime = 0
	lowHealthTime = 0

	shake = 0
	runTime = 0
	fatigue = 0

	wasAirborne = false
	fallStartY = nil

	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)

--------------------------------------------------
-- 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

		--------------------------------------------------
		-- 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
					)

				--------------------------------------------------
				-- HEAVY LANDING
				--------------------------------------------------

				if fallDistance >= MIN_HEAVY_FALL then

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

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

					shake =
						math.max(
							shake,
							landingStrength
						)

				else

					shake =
						math.max(
							shake,
							LANDING_SHAKE
						)

				end

				impactTime = 0

			end

			fallStartY = nil

		end

		wasAirborne = airborne

		--------------------------------------------------
		-- DETECT SPRINT
		--------------------------------------------------

		-- Works with the separate Sprint script.
		-- Sprint normally changes WalkSpeed.

		local isSprinting =
			moving
			and humanoid.WalkSpeed > 16

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

		if isSprinting and not airborne 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 =
					SPRINT_BOB_SPEED

				bobAmount =
					SPRINT_BOB_AMOUNT
					*
					(
						1
						+
						fatigue * 0.35
					)

			else

				bobSpeed =
					WALK_BOB_SPEED

				bobAmount =
					WALK_BOB_AMOUNT

			end

			bobTime +=
				deltaTime * bobSpeed

			--------------------------------------------------
			-- VERTICAL BOB
			--------------------------------------------------

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

			--------------------------------------------------
			-- HORIZONTAL BOB
			--------------------------------------------------

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

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

			--------------------------------------------------
			-- HEAD TILT
			--------------------------------------------------

			local tilt =
				math.sin(
					bobTime
				)
				*
				HEAD_TILT_AMOUNT

			targetRotation =
				Vector3.new(
					math.sin(bobTime * 2)
					* 0.25,
					0,
					tilt
				)

		else

			--------------------------------------------------
			-- BREATHING
			--------------------------------------------------

			bobTime +=
				deltaTime
				*
				BREATH_SPEED

			local breathing =
				math.sin(
					bobTime
				)
				*
				BREATH_AMOUNT

			local fatigueBreathing =
				1
				+
				fatigue * 0.5

			targetOffset =
				Vector3.new(
					0,
					breathing
					*
					fatigueBreathing,
					0
				)

			targetRotation =
				Vector3.zero

		end

		--------------------------------------------------
		-- SMOOTH EFFECTS
		--------------------------------------------------

		local smooth =
			math.clamp(
				deltaTime
				*
				EFFECT_SMOOTH,
				0,
				1
			)

		currentOffset =
			currentOffset:Lerp(
				targetOffset,
				smooth
			)

		currentRotation =
			currentRotation:Lerp(
				targetRotation,
				smooth
			)

		--------------------------------------------------
		-- DAMAGE / LANDING SHAKE
		--------------------------------------------------

		if shake > 0 then

			shake =
				math.max(
					shake
					-
					deltaTime * 1.8,
					0
				)

			impactTime +=
				deltaTime * 35

			local shakeX =
				math.sin(
					impactTime * 1.7
				)
				*
				shake

			local shakeY =
				math.cos(
					impactTime * 2.3
				)
				*
				shake

			local shakeZ =
				math.sin(
					impactTime * 2.8
				)
				*
				shake

			currentOffset +=
				Vector3.new(
					shakeX,
					shakeY,
					shakeZ
				)

			currentRotation +=
				Vector3.new(
					math.rad(shakeY * 30),
					math.rad(shakeX * 20),
					math.rad(shakeZ * 30)
				)

		end

		--------------------------------------------------
		-- LOW HEALTH
		--------------------------------------------------

		local healthRatio =
			humanoid.Health
			/
			math.max(
				humanoid.MaxHealth,
				1
			)

		if healthRatio
			<= LOW_HEALTH_THRESHOLD then

			local danger =
				1
				-
				math.clamp(
					healthRatio
					/
					LOW_HEALTH_THRESHOLD,
					0,
					1
				)

			lowHealthTime +=
				deltaTime
				*
				LOW_HEALTH_SPEED

			local pulse =
				math.sin(
					lowHealthTime
				)
				*
				LOW_HEALTH_AMOUNT
				*
				danger

			currentOffset +=
				Vector3.new(
					pulse * 0.5,
					pulse,
					0
				)

			currentRotation +=
				Vector3.new(
					0,
					0,
					math.rad(
						pulse * 15
					)
				)

		else

			lowHealthTime = 0

		end

		--------------------------------------------------
		-- APPLY OVER EXISTING CAMERA
		--------------------------------------------------

		-- IMPORTANT:
		-- Your First Person script controls the camera.
		-- This script only adds effects AFTER it.

		camera.CFrame =
			camera.CFrame
			*
			CFrame.new(
				currentOffset
			)
			*
			CFrame.Angles(
				math.rad(
					currentRotation.X
				),
				math.rad(
					currentRotation.Y
				),
				math.rad(
					currentRotation.Z
				)
			)

	end
)