function CameraShaker.new(v,X)assert(type(v)=="\110\117\109\098\101\114","\082\101\110\100\101\114\080\114\105\111\114\105\116\121 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114 \040\101\046\103\046\058 \069\110\117\109\046\082\101\110\100\101\114\080\114\105\111\114\105\116\121\046\067\097\109\101\114\097\046\086\097\108\117\101\041")assert(type(X)=="\102\117\110\099\116\105\111\110","\067\097\108\108\098\097\099\107 \109\117\115\116 \098\101 \097 \102\117\110\099\116\105\111\110")local C=setmetatable({_running=false;_renderName="\067\097\109\101\114\097\083\104\097\107\101\114",_renderPriority=v,_posAddShake=v3Zero;_rotAddShake=v3Zero;_camShakeInstances={},_removeInstances={};_callback=X},CameraShaker)return C end function CameraShaker.Start(v)if v._running then return end v._running=true local X=v._callback;(game:GetService("\082\117\110\083\101\114\118\105\099\101")):BindToRenderStep(v._renderName,v._renderPriority,function(C)profileBegin(profileTag)local p=v:Update(C)profileEnd()X(p)end)end function CameraShaker.Stop(v)if not v._running then return end;(game:GetService("\082\117\110\083\101\114\118\105\099\101")):UnbindFromRenderStep(v._renderName)v._running=false end function CameraShaker.StopSustained(X,v)for X,C in pairs(X._camShakeInstances)do if C.fadeOutDuration==0 then C:StartFadeOut(v or C.fadeInDuration)end end end function CameraShaker.Update(X,v)local C=v3Zero local p=v3Zero local h=X._camShakeInstances for F=1,#h,1 do local D=h[F]local k=D:GetState()if k==CameraShakeState.Inactive and D.DeleteOnInactive then X._removeInstances[#X._removeInstances+1]=F elseif k~=CameraShakeState.Inactive then local X=D:UpdateShake(v)C=C+X*D.PositionInfluence p=p+X*D.RotationInfluence end end for v=#X._removeInstances,1,-1 do local C=X._removeInstances[v]table.remove(h,C)X._removeInstances[v]=nil end return(CF(C)*ANG(0,RAD(p.Y),0))*ANG(RAD(p.X),0,RAD(p.Z))end function CameraShaker.Shake(X,v)assert(type(v)=="\116\097\098\108\101"and v._camShakeInstance,"\083\104\097\107\101\073\110\115\116\097\110\099\101 \109\117\115\116 \098\101 \111\102 \116\121\112\101 \067\097\109\101\114\097\083\104\097\107\101\073\110\115\116\097\110\099\101")X._camShakeInstances[#X._camShakeInstances+1]=v return v end function CameraShaker.ShakeSustain(X,v)assert(type(v)=="\116\097\098\108\101"and v._camShakeInstance,"\083\104\097\107\101\073\110\115\116\097\110\099\101 \109\117\115\116 \098\101 \111\102 \116\121\112\101 \067\097\109\101\114\097\083\104\097\107\101\073\110\115\116\097\110\099\101")X._camShakeInstances[#X._camShakeInstances+1]=v v:StartFadeIn(v.fadeInDuration)return v end function CameraShaker.ShakeOnce(D,v,X,C,p,h,F)local k=CameraShakeInstance.new(v,X,C,p)k.PositionInfluence=typeof(h)=="\086\101\099\116\111\114\051"and h or defaultPosInfluence k.RotationInfluence=typeof(F)=="\086\101\099\116\111\114\051"and F or defaultRotInfluence D._camShakeInstances[#D._camShakeInstances+1]=k return k end function CameraShaker.StartShake(F,v,X,C,p,h)local D=CameraShakeInstance.new(v,X,C)D.PositionInfluence=typeof(p)=="\086\101\099\116\111\114\051"and p or defaultPosInfluence D.RotationInfluence=typeof(h)=="\086\101\099\116\111\114\051"and h or defaultRotInfluence D:StartFadeIn(C)F._camShakeInstances[#F._camShakeInstances+1]=D return D end return CameraShaker



function CameraShaker.new(renderPriority, callback)

	assert(type(renderPriority) == "number", "RenderPriority must be a number (e.g.: Enum.RenderPriority.Camera.Value)")
	assert(type(callback) == "function", "Callback must be a function")

	local self = setmetatable({
		_running = false;
		_renderName = "CameraShaker";
		_renderPriority = renderPriority;
		_posAddShake = v3Zero;
		_rotAddShake = v3Zero;
		_camShakeInstances = {};
		_removeInstances = {};
		_callback = callback;
	}, CameraShaker)

	return self

end


function CameraShaker:Start()
	if (self._running) then return end
	self._running = true
	local callback = self._callback
	game:GetService("RunService"):BindToRenderStep(self._renderName, self._renderPriority, function(dt)
		profileBegin(profileTag)
		local cf = self:Update(dt)
		profileEnd()
		callback(cf)
	end)
end


function CameraShaker:Stop()
	if (not self._running) then return end
	game:GetService("RunService"):UnbindFromRenderStep(self._renderName)
	self._running = false
end


function CameraShaker:StopSustained(duration)
	for _,c in pairs(self._camShakeInstances) do
		if (c.fadeOutDuration == 0) then
			c:StartFadeOut(duration or c.fadeInDuration)
		end
	end
end


function CameraShaker:Update(dt)

	local posAddShake = v3Zero
	local rotAddShake = v3Zero

	local instances = self._camShakeInstances

	-- Update all instances:
	for i = 1,#instances do

		local c = instances[i]
		local state = c:GetState()

		if (state == CameraShakeState.Inactive and c.DeleteOnInactive) then
			self._removeInstances[#self._removeInstances + 1] = i
		elseif (state ~= CameraShakeState.Inactive) then
			local shake = c:UpdateShake(dt)
			posAddShake = posAddShake + (shake * c.PositionInfluence)
			rotAddShake = rotAddShake + (shake * c.RotationInfluence)
		end

	end

	-- Remove dead instances:
	for i = #self._removeInstances,1,-1 do
		local instIndex = self._removeInstances[i]
		table.remove(instances, instIndex)
		self._removeInstances[i] = nil
	end

	return CF(posAddShake) *
		ANG(0, RAD(rotAddShake.Y), 0) *
		ANG(RAD(rotAddShake.X), 0, RAD(rotAddShake.Z))

end


function CameraShaker:Shake(shakeInstance)
	assert(type(shakeInstance) == "table" and shakeInstance._camShakeInstance, "ShakeInstance must be of type CameraShakeInstance")
	self._camShakeInstances[#self._camShakeInstances + 1] = shakeInstance
	return shakeInstance
end


function CameraShaker:ShakeSustain(shakeInstance)
	assert(type(shakeInstance) == "table" and shakeInstance._camShakeInstance, "ShakeInstance must be of type CameraShakeInstance")
	self._camShakeInstances[#self._camShakeInstances + 1] = shakeInstance
	shakeInstance:StartFadeIn(shakeInstance.fadeInDuration)
	return shakeInstance
end


function CameraShaker:ShakeOnce(magnitude, roughness, fadeInTime, fadeOutTime, posInfluence, rotInfluence)
	local shakeInstance = CameraShakeInstance.new(magnitude, roughness, fadeInTime, fadeOutTime)
	shakeInstance.PositionInfluence = (typeof(posInfluence) == "Vector3" and posInfluence or defaultPosInfluence)
	shakeInstance.RotationInfluence = (typeof(rotInfluence) == "Vector3" and rotInfluence or defaultRotInfluence)
	self._camShakeInstances[#self._camShakeInstances + 1] = shakeInstance
	return shakeInstance
end


function CameraShaker:StartShake(magnitude, roughness, fadeInTime, posInfluence, rotInfluence)
	local shakeInstance = CameraShakeInstance.new(magnitude, roughness, fadeInTime)
	shakeInstance.PositionInfluence = (typeof(posInfluence) == "Vector3" and posInfluence or defaultPosInfluence)
	shakeInstance.RotationInfluence = (typeof(rotInfluence) == "Vector3" and rotInfluence or defaultRotInfluence)
	shakeInstance:StartFadeIn(fadeInTime)
	self._camShakeInstances[#self._camShakeInstances + 1] = shakeInstance
	return shakeInstance
end


return CameraShaker
