function BaseCamera.ApplyVRTransform(C)if not VRService.VREnabled then return end local K=C.humanoidRootPart and C.humanoidRootPart:FindFirstChild("\082\111\111\116\074\111\105\110\116")if not K then return end local V=game.Workspace.CurrentCamera.CameraSubject local U=V and V:IsA("\086\101\104\105\099\108\101\083\101\097\116")if C.inFirstPerson and not U then local C=VRService:GetUserCFrame(Enum.UserCFrame.Head)local V=C-C.p K.C0=CFrame.new(V:vectorToObjectSpace(C.p))*CFrame.new(0,0,0,-1,0,0,0,0,1,0,1,0)else K.C0=CFrame.new(0,0,0,-1,0,0,0,0,1,0,1,0)end end function BaseCamera.IsInFirstPerson(C)return C.inFirstPerson end function BaseCamera.ShouldUseVRRotation(C)if not VRService.VREnabled then return false end if not C.VRRotationIntensityAvailable and tick()-C.lastVRRotationIntensityCheckTime<1 then return false end local K,V=pcall(function()return StarterGui:GetCore("\086\082\082\111\116\097\116\105\111\110\073\110\116\101\110\115\105\116\121")end)C.VRRotationIntensityAvailable=K and V~=nil C.lastVRRotationIntensityCheckTime=tick()C.shouldUseVRRotation=K and(V~=nil and V~="\083\109\111\111\116\104")return C.shouldUseVRRotation end function BaseCamera.GetVRRotationInput(C)local K=ZERO_VECTOR2 local V,U=pcall(function()return StarterGui:GetCore("\086\082\082\111\116\097\116\105\111\110\073\110\116\101\110\115\105\116\121")end)if not V then return end local M=C.GamepadPanningCamera or ZERO_VECTOR2 local h=tick()-C.lastVRRotationTime>=C:GetRepeatDelayValue(U)if math.abs(M.x)>=C:GetActivateValue()then if h or not C.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2]then local V=1 if M.x<0 then V=-1 end K=K+C:GetRotateAmountValue(U)*V C.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2]=true end elseif math.abs(M.x)<C:GetActivateValue()-.1 then C.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2]=nil end if C.turningLeft then if h or not C.vrRotateKeyCooldown[Enum.KeyCode.Left]then K=K-C:GetRotateAmountValue(U)C.vrRotateKeyCooldown[Enum.KeyCode.Left]=true end else C.vrRotateKeyCooldown[Enum.KeyCode.Left]=nil end if C.turningRight then if h or not C.vrRotateKeyCooldown[Enum.KeyCode.Right]then K=K+C:GetRotateAmountValue(U)C.vrRotateKeyCooldown[Enum.KeyCode.Right]=true end else C.vrRotateKeyCooldown[Enum.KeyCode.Right]=nil end if K~=ZERO_VECTOR2 then C.lastVRRotationTime=tick()end return K end function BaseCamera.CancelCameraFreeze(K,C)if not C then K.cameraTranslationConstraints=Vector3.new(K.cameraTranslationConstraints.x,1,K.cameraTranslationConstraints.z)end if K.cameraFrozen then K.trackingHumanoid=nil K.cameraFrozen=false end end function BaseCamera.StartCameraFreeze(V,C,K)if not V.cameraFrozen then V.humanoidJumpOrigin=C V.trackingHumanoid=K V.cameraTranslationConstraints=Vector3.new(V.cameraTranslationConstraints.x,0,V.cameraTranslationConstraints.z)V.cameraFrozen=true end end function BaseCamera.RescaleCameraOffset(K,C)K.headHeightR15=R15_HEAD_OFFSET*C end function BaseCamera.OnHumanoidSubjectChildAdded(K,C)if C.Name=="\066\111\100\121\072\101\105\103\104\116\083\099\097\108\101"and C:IsA("\078\117\109\098\101\114\086\097\108\117\101")then if K.heightScaleChangedConn then K.heightScaleChangedConn:Disconnect()end K.heightScaleChangedConn=C.Changed:Connect(function(C)K:RescaleCameraOffset(C)end)K:RescaleCameraOffset(C.Value)end end function BaseCamera.OnHumanoidSubjectChildRemoved(K,C)if C.Name=="\066\111\100\121\072\101\105\103\104\116\083\099\097\108\101"then K:RescaleCameraOffset(1)if K.heightScaleChangedConn then K.heightScaleChangedConn:Disconnect()K.heightScaleChangedConn=nil end end end function BaseCamera.OnNewCameraSubject(C)if C.subjectStateChangedConn then C.subjectStateChangedConn:Disconnect()C.subjectStateChangedConn=nil end if C.humanoidChildAddedConn then C.humanoidChildAddedConn:Disconnect()C.humanoidChildAddedConn=nil end if C.humanoidChildRemovedConn then C.humanoidChildRemovedConn:Disconnect()C.humanoidChildRemovedConn=nil end if C.heightScaleChangedConn then C.heightScaleChangedConn:Disconnect()C.heightScaleChangedConn=nil end local K=workspace.CurrentCamera and workspace.CurrentCamera.CameraSubject if C.trackingHumanoid~=K then C:CancelCameraFreeze()end if K and K:IsA("\072\117\109\097\110\111\105\100")then C.humanoidChildAddedConn=K.ChildAdded:Connect(function(K)C:OnHumanoidSubjectChildAdded(K)end)C.humanoidChildRemovedConn=K.ChildRemoved:Connect(function(K)C:OnHumanoidSubjectChildRemoved(K)end)for K,V in pairs(K:GetChildren())do C:OnHumanoidSubjectChildAdded(V)end C.subjectStateChangedConn=K.StateChanged:Connect(function(V,U)if VRService.VREnabled and(U==Enum.HumanoidStateType.Jumping and not C.inFirstPerson)then C:StartCameraFreeze(C:GetSubjectPosition(),K)elseif U~=Enum.HumanoidStateType.Jumping and U~=Enum.HumanoidStateType.Freefall then C:CancelCameraFreeze(true)end end)end end function BaseCamera.GetVRFocus(V,C,K)local U=V.LastCameraFocus or C if not V.cameraFrozen then V.cameraTranslationConstraints=Vector3.new(V.cameraTranslationConstraints.x,math.min(1,V.cameraTranslationConstraints.y+.42*K),V.cameraTranslationConstraints.z)end local M if V.cameraFrozen and(V.humanoidJumpOrigin and V.humanoidJumpOrigin.y>U.y)then M=CFrame.new(Vector3.new(C.x,math.min(V.humanoidJumpOrigin.y,U.y+5*K),C.z))else M=CFrame.new((Vector3.new(C.x,U.y,C.z)):lerp(C,V.cameraTranslationConstraints.y))end if V.cameraFrozen then if V.inFirstPerson then V:CancelCameraFreeze()end if V.humanoidJumpOrigin and C.y<V.humanoidJumpOrigin.y-.5 then V:CancelCameraFreeze()end end return M end function BaseCamera.GetRotateAmountValue(K,C)C=C or StarterGui:GetCore("\086\082\082\111\116\097\116\105\111\110\073\110\116\101\110\115\105\116\121")if C then if C=="\076\111\119"then return VR_LOW_INTENSITY_ROTATION elseif C=="\072\105\103\104"then return VR_HIGH_INTENSITY_ROTATION end end return ZERO_VECTOR2 end function BaseCamera.GetRepeatDelayValue(K,C)C=C or StarterGui:GetCore("\086\082\082\111\116\097\116\105\111\110\073\110\116\101\110\115\105\116\121")if C then if C=="\076\111\119"then return VR_LOW_INTENSITY_REPEAT elseif C=="\072\105\103\104"then return VR_HIGH_INTENSITY_REPEAT end end return 0 end function BaseCamera.Test(C)print("\066\097\115\101\067\097\109\101\114\097\058\084\101\115\116\040\041")end function BaseCamera.Update(K,C)warn("\066\097\115\101\067\097\109\101\114\097\058\085\112\100\097\116\101\040\041 \084\104\105\115 \105\115 \097 \118\105\114\116\117\097\108 \102\117\110\099\116\105\111\110 \116\104\097\116 \115\104\111\117\108\100 \110\101\118\101\114 \098\101 \103\101\116\116\105\110\103 \099\097\108\108\101\100\046")return game.Workspace.CurrentCamera.CFrame,game.Workspace.CurrentCamera.Focus end return BaseCamera


function BaseCamera:ApplyVRTransform()
	if not VRService.VREnabled then
		return
	end

	--we only want this to happen in first person VR
	local rootJoint = self.humanoidRootPart and self.humanoidRootPart:FindFirstChild("RootJoint")
	if not rootJoint then
		return
	end

	local cameraSubject = game.Workspace.CurrentCamera.CameraSubject
	local isInVehicle = cameraSubject and cameraSubject:IsA("VehicleSeat")

	if self.inFirstPerson and not isInVehicle then
		local vrFrame = VRService:GetUserCFrame(Enum.UserCFrame.Head)
		local vrRotation = vrFrame - vrFrame.p
		rootJoint.C0 = CFrame.new(vrRotation:vectorToObjectSpace(vrFrame.p)) * CFrame.new(0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0)
	else
		rootJoint.C0 = CFrame.new(0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0)
	end
end

function BaseCamera:IsInFirstPerson()
	return self.inFirstPerson
end

function BaseCamera:ShouldUseVRRotation()
	if not VRService.VREnabled then
		return false
	end

	if not self.VRRotationIntensityAvailable and tick() - self.lastVRRotationIntensityCheckTime < 1 then
		return false
	end

	local success, vrRotationIntensity = pcall(function() return StarterGui:GetCore("VRRotationIntensity") end)
	self.VRRotationIntensityAvailable = success and vrRotationIntensity ~= nil
	self.lastVRRotationIntensityCheckTime = tick()

	self.shouldUseVRRotation = success and vrRotationIntensity ~= nil and vrRotationIntensity ~= "Smooth"

	return self.shouldUseVRRotation
end

function BaseCamera:GetVRRotationInput()
	local vrRotateSum = ZERO_VECTOR2
	local success, vrRotationIntensity = pcall(function() return StarterGui:GetCore("VRRotationIntensity") end)

	if not success then
		return
	end

	local vrGamepadRotation = self.GamepadPanningCamera or ZERO_VECTOR2
	local delayExpired = (tick() - self.lastVRRotationTime) >= self:GetRepeatDelayValue(vrRotationIntensity)

	if math.abs(vrGamepadRotation.x) >= self:GetActivateValue() then
		if (delayExpired or not self.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2]) then
			local sign = 1
			if vrGamepadRotation.x < 0 then
				sign = -1
			end
			vrRotateSum = vrRotateSum + self:GetRotateAmountValue(vrRotationIntensity) * sign
			self.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2] = true
		end
	elseif math.abs(vrGamepadRotation.x) < self:GetActivateValue() - 0.1 then
		self.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2] = nil
	end
	if self.turningLeft then
		if delayExpired or not self.vrRotateKeyCooldown[Enum.KeyCode.Left] then
			vrRotateSum = vrRotateSum - self:GetRotateAmountValue(vrRotationIntensity)
			self.vrRotateKeyCooldown[Enum.KeyCode.Left] = true
		end
	else
		self.vrRotateKeyCooldown[Enum.KeyCode.Left] = nil
	end
	if self.turningRight then
		if (delayExpired or not self.vrRotateKeyCooldown[Enum.KeyCode.Right]) then
			vrRotateSum = vrRotateSum + self:GetRotateAmountValue(vrRotationIntensity)
			self.vrRotateKeyCooldown[Enum.KeyCode.Right] = true
		end
	else
		self.vrRotateKeyCooldown[Enum.KeyCode.Right] = nil
	end

	if vrRotateSum ~= ZERO_VECTOR2 then
		self.lastVRRotationTime = tick()
	end

	return vrRotateSum
end

function BaseCamera:CancelCameraFreeze(keepConstraints)
	if not keepConstraints then
		self.cameraTranslationConstraints = Vector3.new(self.cameraTranslationConstraints.x, 1, self.cameraTranslationConstraints.z)
	end
	if self.cameraFrozen then
		self.trackingHumanoid = nil
		self.cameraFrozen = false
	end
end

function BaseCamera:StartCameraFreeze(subjectPosition, humanoidToTrack)
	if not self.cameraFrozen then
		self.humanoidJumpOrigin = subjectPosition
		self.trackingHumanoid = humanoidToTrack
		self.cameraTranslationConstraints = Vector3.new(self.cameraTranslationConstraints.x, 0, self.cameraTranslationConstraints.z)
		self.cameraFrozen = true
	end
end

function BaseCamera:RescaleCameraOffset(newScaleFactor)
	self.headHeightR15 = R15_HEAD_OFFSET * newScaleFactor
end

function BaseCamera:OnHumanoidSubjectChildAdded(child)
	if child.Name == "BodyHeightScale" and child:IsA("NumberValue") then
		if self.heightScaleChangedConn then
			self.heightScaleChangedConn:Disconnect()
		end
		self.heightScaleChangedConn = child.Changed:Connect(function(newScaleFactor)
			self:RescaleCameraOffset(newScaleFactor)
		end)
		self:RescaleCameraOffset(child.Value)
	end
end

function BaseCamera:OnHumanoidSubjectChildRemoved(child)
	if child.Name == "BodyHeightScale" then
		self:RescaleCameraOffset(1)
		if self.heightScaleChangedConn then
			self.heightScaleChangedConn:Disconnect()
			self.heightScaleChangedConn = nil
		end
	end
end

function BaseCamera:OnNewCameraSubject()
	if self.subjectStateChangedConn then
		self.subjectStateChangedConn:Disconnect()
		self.subjectStateChangedConn = nil
	end
	if self.humanoidChildAddedConn then
		self.humanoidChildAddedConn:Disconnect()
		self.humanoidChildAddedConn = nil
	end
	if self.humanoidChildRemovedConn then
		self.humanoidChildRemovedConn:Disconnect()
		self.humanoidChildRemovedConn = nil
	end
	if self.heightScaleChangedConn then
		self.heightScaleChangedConn:Disconnect()
		self.heightScaleChangedConn = nil
	end

	local humanoid = workspace.CurrentCamera and workspace.CurrentCamera.CameraSubject
	if self.trackingHumanoid ~= humanoid then
		self:CancelCameraFreeze()
	end
	if humanoid and humanoid:IsA("Humanoid") then
		self.humanoidChildAddedConn = humanoid.ChildAdded:Connect(function(child)
			self:OnHumanoidSubjectChildAdded(child)
		end)
		self.humanoidChildRemovedConn = humanoid.ChildRemoved:Connect(function(child)
			self:OnHumanoidSubjectChildRemoved(child)
		end)
		for _, child in pairs(humanoid:GetChildren()) do
			self:OnHumanoidSubjectChildAdded(child)
		end

		self.subjectStateChangedConn = humanoid.StateChanged:Connect(function(oldState, newState)
			if VRService.VREnabled and newState == Enum.HumanoidStateType.Jumping and not self.inFirstPerson then
				self:StartCameraFreeze(self:GetSubjectPosition(), humanoid)
			elseif newState ~= Enum.HumanoidStateType.Jumping and newState ~= Enum.HumanoidStateType.Freefall then
				self:CancelCameraFreeze(true)
			end
		end)
	end
end

function BaseCamera:GetVRFocus(subjectPosition, timeDelta)
	local lastFocus = self.LastCameraFocus or subjectPosition
	if not self.cameraFrozen then
		self.cameraTranslationConstraints = Vector3.new(self.cameraTranslationConstraints.x, math.min(1, self.cameraTranslationConstraints.y + 0.42 * timeDelta), self.cameraTranslationConstraints.z)
	end

	local newFocus
	if self.cameraFrozen and self.humanoidJumpOrigin and self.humanoidJumpOrigin.y > lastFocus.y then
		newFocus = CFrame.new(Vector3.new(subjectPosition.x, math.min(self.humanoidJumpOrigin.y, lastFocus.y + 5 * timeDelta), subjectPosition.z))
	else
		newFocus = CFrame.new(Vector3.new(subjectPosition.x, lastFocus.y, subjectPosition.z):lerp(subjectPosition, self.cameraTranslationConstraints.y))
	end

	if self.cameraFrozen then
		-- No longer in 3rd person
		if self.inFirstPerson then -- not VRService.VREnabled
			self:CancelCameraFreeze()
		end
		-- This case you jumped off a cliff and want to keep your character in view
		-- 0.5 is to fix floating point error when not jumping off cliffs
		if self.humanoidJumpOrigin and subjectPosition.y < (self.humanoidJumpOrigin.y - 0.5) then
			self:CancelCameraFreeze()
		end
	end

	return newFocus
end

function BaseCamera:GetRotateAmountValue(vrRotationIntensity)
	vrRotationIntensity = vrRotationIntensity or StarterGui:GetCore("VRRotationIntensity")
	if vrRotationIntensity then
		if vrRotationIntensity == "Low" then
			return VR_LOW_INTENSITY_ROTATION
		elseif vrRotationIntensity == "High" then
			return VR_HIGH_INTENSITY_ROTATION
		end
	end
	return ZERO_VECTOR2
end

function BaseCamera:GetRepeatDelayValue(vrRotationIntensity)
	vrRotationIntensity = vrRotationIntensity or StarterGui:GetCore("VRRotationIntensity")
	if vrRotationIntensity then
		if vrRotationIntensity == "Low" then
			return VR_LOW_INTENSITY_REPEAT
		elseif vrRotationIntensity == "High" then
			return VR_HIGH_INTENSITY_REPEAT
		end
	end
	return 0
end

function BaseCamera:Test()
	print("BaseCamera:Test()")
end

function BaseCamera:Update(dt)
	warn("BaseCamera:Update() This is a virtual function that should never be getting called.")
	return game.Workspace.CurrentCamera.CFrame, game.Workspace.CurrentCamera.Focus
end

return BaseCamera
