local Y={}Y.__index=Y local Q=debug.profilebegin local c=debug.profileend local T="\067\097\109\101\114\097\083\104\097\107\101\114\085\112\100\097\116\101"local M=Vector3.new local B=CFrame.new local e=CFrame.Angles local x=math.rad local p=M()local C=require(script.CameraShakeInstance)local E=C.CameraShakeState local t=M(.15,.15,.15)local s=M(1,1,1)Y.CameraShakeInstance=C function Y.new(Q,c)assert(type(Q)=="\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(c)=="\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 T=setmetatable({_running=false,_renderName="\067\097\109\101\114\097\083\104\097\107\101\114";_renderPriority=Q,_posAddShake=p;_rotAddShake=p;_camShakeInstances={},_removeInstances={};_callback=c},Y)return T end function Y.Start(Y)if Y._running then return end Y._running=true local M=Y._callback;(game:GetService("\082\117\110\083\101\114\118\105\099\101")):BindToRenderStep(Y._renderName,Y._renderPriority,function(B)Q(T)local e=Y:Update(B)c()M(e)end)end function Y.Stop(Y)if not Y._running then return end;(game:GetService("\082\117\110\083\101\114\118\105\099\101")):UnbindFromRenderStep(Y._renderName)Y._running=false end function Y.StopSustained(Q,Y)for Q,c in pairs(Q._camShakeInstances)do if c.fadeOutDuration==0 then c:StartFadeOut(Y or c.fadeInDuration)end end end function Y.Update(Q,Y)Y=math.clamp(Y,0,.6)local c=p local T=p local M=Q._camShakeInstances for B=1,#M,1 do local e=M[B]local x=e:GetState()if x==E.Inactive and e.DeleteOnInactive then Q._removeInstances[#Q._removeInstances+1]=B elseif x~=E.Inactive then local Q=e:UpdateShake(Y)c=c+Q*e.PositionInfluence T=T+Q*e.RotationInfluence end end for Y=#Q._removeInstances,1,-1 do local c=Q._removeInstances[Y]table.remove(M,c)Q._removeInstances[Y]=nil end return(B(c)*e(0,x(T.Y),0))*e(x(T.X),0,x(T.Z))end function Y.Shake(Q,Y)assert(type(Y)=="\116\097\098\108\101"and Y._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")Q._camShakeInstances[#Q._camShakeInstances+1]=Y return Y end function Y.ShakeSustain(Q,Y)assert(type(Y)=="\116\097\098\108\101"and Y._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")Q._camShakeInstances[#Q._camShakeInstances+1]=Y Y:StartFadeIn(Y.fadeInDuration)return Y end function Y.ShakeOnce(e,Y,Q,c,T,M,B)local x=C.new(Y,Q,c,T)x.PositionInfluence=typeof(M)=="\086\101\099\116\111\114\051"and M or t x.RotationInfluence=typeof(B)=="\086\101\099\116\111\114\051"and B or s e._camShakeInstances[#e._camShakeInstances+1]=x return x end function Y.StartShake(B,Y,Q,c,T,M)local e=C.new(Y,Q,c)e.PositionInfluence=typeof(T)=="\086\101\099\116\111\114\051"and T or t e.RotationInfluence=typeof(M)=="\086\101\099\116\111\114\051"and M or s e:StartFadeIn(c)B._camShakeInstances[#B._camShakeInstances+1]=e return e end return Y




local CameraShaker = {}
CameraShaker.__index = CameraShaker

local profileBegin = debug.profilebegin
local profileEnd = debug.profileend
local profileTag = "CameraShakerUpdate"

local V3 = Vector3.new
local CF = CFrame.new
local ANG = CFrame.Angles
local RAD = math.rad
local v3Zero = V3()

local CameraShakeInstance = require(script.CameraShakeInstance)
local CameraShakeState = CameraShakeInstance.CameraShakeState

local defaultPosInfluence = V3(0.15, 0.15, 0.15)
local defaultRotInfluence = V3(1, 1, 1)


CameraShaker.CameraShakeInstance = CameraShakeInstance


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)
	dt = math.clamp(dt, 0, .6)
	
	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
