function ShoulderCamera.applyRootJointFix(k)if k.rootJoint then local J=k.zoomState and Vector3.new(.25,.25,.25)or Vector3.new(.5,.5,.5)local g=k.zoomState and.15 or.2 local b=k.rootJoint.Part0.CFrame-k.rootJoint.Part0.CFrame.Position local I=k.rootJoint.Transform-k.rootJoint.Transform.Position local D=CFrame.Angles(0,k.yaw,0)local f=b:toObjectSpace(D)local R=k.rootRigAttach.CFrame if k:isHumanoidControllable()then R=(k.rootJoint.Transform:inverse()*f)*I:Lerp(CFrame.new(),1-g)+k.rootJoint.Transform.Position*J end k.rootJoint.C0=CFrame.new(k.rootJoint.C0.Position,k.rootJoint.C0.Position+R.LookVector.Unit)end end function ShoulderCamera.sprintFromTouchInput(k)local J=nil local g=nil local b=nil if k.controlModule then J=k.controlModule:GetMoveVector()g=k.controlModule:GetActiveController()end if J and g then b=g.thumbstickFrame~=nil or g.thumbpadFrame~=nil end if b then return J and J.Magnitude>=.9 else return false end end function ShoulderCamera.sprintFromGamepadInput(k)return k.movementPan.Magnitude>.9 end function ShoulderCamera.onCurrentCharacterChanged(J,k)J.currentCharacter=k if J.currentCharacter then J.raycastIgnoreList[1]=J.currentCharacter J.currentHumanoid=k:WaitForChild("\072\117\109\097\110\111\105\100")J.currentRootPart=k:WaitForChild("\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116")J.rootRigAttach=J.currentRootPart:WaitForChild("\082\111\111\116\082\105\103\065\116\116\097\099\104\109\101\110\116")J.rootJoint=(k:WaitForChild("\076\111\119\101\114\084\111\114\115\111")):WaitForChild("\082\111\111\116")J.currentWaist=(k:WaitForChild("\085\112\112\101\114\084\111\114\115\111")):WaitForChild("\087\097\105\115\116")J.currentWrist=(k:WaitForChild("\082\105\103\104\116\072\097\110\100")):WaitForChild("\082\105\103\104\116\087\114\105\115\116")J.wristAttach0=(k:WaitForChild("\082\105\103\104\116\076\111\119\101\114\065\114\109")):WaitForChild("\082\105\103\104\116\087\114\105\115\116\082\105\103\065\116\116\097\099\104\109\101\110\116")J.wristAttach1=(k:WaitForChild("\082\105\103\104\116\072\097\110\100")):WaitForChild("\082\105\103\104\116\087\114\105\115\116\082\105\103\065\116\116\097\099\104\109\101\110\116")J.rightGripAttachment=(k:WaitForChild("\082\105\103\104\116\072\097\110\100")):WaitForChild("\082\105\103\104\116\071\114\105\112\065\116\116\097\099\104\109\101\110\116")J.currentTool=k:FindFirstChildOfClass("\084\111\111\108")J.eventConnections.humanoidDied=J.currentHumanoid.Died:Connect(function()J.zoomedFromInput=false J:updateZoomState()end)J.eventConnections.characterChildAdded=k.ChildAdded:Connect(function(k)if k:IsA("\084\111\111\108")then J.currentTool=k J:updateZoomState()end end)J.eventConnections.characterChildRemoved=k.ChildRemoved:Connect(function(g)if g:IsA("\084\111\111\108")and J.currentTool==g then J.currentTool=k:FindFirstChildOfClass("\084\111\111\108")J:updateZoomState()end end)if Players.LocalPlayer then local k=Players.LocalPlayer:FindFirstChild("\080\108\097\121\101\114\083\099\114\105\112\116\115")if k then local g=k:FindFirstChild("\080\108\097\121\101\114\077\111\100\117\108\101")if g then J.controlModule=require(g:FindFirstChild("\067\111\110\116\114\111\108\077\111\100\117\108\101"))end end end else if J.eventConnections.humanoidDied then J.eventConnections.humanoidDied:Disconnect()J.eventConnections.humanoidDied=nil end if J.eventConnections.characterChildAdded then J.eventConnections.characterChildAdded:Disconnect()J.eventConnections.characterChildAdded=nil end if J.eventConnections.characterChildRemoved then J.eventConnections.characterChildRemoved:Disconnect()J.eventConnections.characterChildRemoved=nil end J.currentTool=nil J.currentHumanoid=nil J.currentRootPart=nil J.controlModule=nil end end function ShoulderCamera.onCurrentCameraChanged(J,k)if J.currentCamera==k then return end J.currentCamera=k if J.currentCamera then J.raycastIgnoreList[2]=J.currentCamera if J.eventConnections.cameraTypeChanged then J.eventConnections.cameraTypeChanged:Disconnect()J.eventConnections.cameraTypeChanged=nil end J.eventConnections.cameraTypeChanged=(J.currentCamera:GetPropertyChangedSignal("\067\097\109\101\114\097\084\121\112\101")):Connect(function()if J.enabled then J.currentCamera.CameraType=Enum.CameraType.Scriptable end end)end end function ShoulderCamera.isHumanoidControllable(k)if not k.currentHumanoid then return false end local J=k.currentHumanoid:GetState()return CONTROLLABLE_HUMANOID_STATES[J]==true end function ShoulderCamera.getCollisionRadius(k)if not k.currentCamera then return 0 end local J=k.currentCamera.ViewportSize local g=J.X/J.Y local b=math.rad(k.fieldOfView)local I=math.tan(b)*math.abs(k.currentCamera.NearPlaneZ)local D=I*g local f=Vector3.new(D,I,k.currentCamera.NearPlaneZ)return f.Magnitude end function ShoulderCamera.penetrateCast(g,k,J)local b=0 local I,D,f,R=nil,k.Origin+k.Direction,Vector3.new(0,1,0),Enum.Material.Air while b<50 do b=b+1 I,D,f,R=workspace:FindPartOnRayWithIgnoreList(k,J,false,true)if I and not I.CanCollide then table.insert(J,I)else break end end return I,D,f,R end function ShoulderCamera.getRelativePitch(k)if k.currentRootPart then local J=CFrame.Angles(k.pitch,0,0)local g=k.currentRootPart.CFrame:toObjectSpace(J)local b=g.lookVector local I=math.asin(b.Y)return math.clamp(I,k.minPitch,k.maxPitch)end return k.pitch end function ShoulderCamera.getCurrentFieldOfView(k)if k.zoomState then return k.zoomedFOV else return k.fieldOfView end end function ShoulderCamera.handlePartTransparencies(k)local J={}local g={}for b,I in pairs(k.currentCharacter:GetChildren())do local D=false if I:IsA("\066\097\115\101\080\097\114\116")then D=J[I.Name]==true I.LocalTransparencyModifier=D and 1 or 0 elseif I:IsA("\065\099\099\111\117\116\114\101\109\101\110\116")then local k=I:GetDescendants()local J={}for k,b in pairs(k)do if b:IsA("\065\116\116\097\099\104\109\101\110\116")and g[b.Name]then D=true elseif b:IsA("\066\097\115\101\080\097\114\116")then table.insert(J,b)end end for k,J in pairs(J)do J.LocalTransparencyModifier=D and 1 or 0 end elseif I:IsA("\084\111\111\108")then D=k.zoomState and(k.hasScope or k.hideToolWhileZoomed)for k,J in pairs(I:GetDescendants())do if J:IsA("\066\097\115\101\080\097\114\116")then J.LocalTransparencyModifier=D and 1 or 0 end end end end end function ShoulderCamera.setSprintEnabled(J,k)J.sprintEnabled=k end function ShoulderCamera.setSlowZoomWalkEnabled(J,k)J.slowZoomWalkEnabled=k end function ShoulderCamera.setHasScope(J,k)if J.hasScope==k then return end J.hasScope=k J:updateZoomState()end function ShoulderCamera.onSprintAction(b,k,J,g)b.sprintingInputActivated=J==Enum.UserInputState.Begin end

function ShoulderCamera:applyRootJointFix()
	if self.rootJoint then
		local translationScale = self.zoomState and Vector3.new(0.25, 0.25, 0.25) or Vector3.new(0.5, 0.5, 0.5)
		local rotationScale = self.zoomState and 0.15 or 0.2
		local rootRotation = self.rootJoint.Part0.CFrame - self.rootJoint.Part0.CFrame.Position
		local rotation = self.rootJoint.Transform - self.rootJoint.Transform.Position
		local yawRotation = CFrame.Angles(0, self.yaw, 0)
		local leadRotation = rootRotation:toObjectSpace(yawRotation)
		local rotationFix = self.rootRigAttach.CFrame
		if self:isHumanoidControllable() then
			rotationFix = self.rootJoint.Transform:inverse() * leadRotation * rotation:Lerp(CFrame.new(), 1 - rotationScale) + (self.rootJoint.Transform.Position * translationScale)
		end

		self.rootJoint.C0 = CFrame.new(self.rootJoint.C0.Position, self.rootJoint.C0.Position + rotationFix.LookVector.Unit)
	end
end

function ShoulderCamera:sprintFromTouchInput()
	local moveVector = nil
	local activeController = nil
	local activeControllerIsTouch = nil
	if self.controlModule then
		moveVector = self.controlModule:GetMoveVector()
		activeController = self.controlModule:GetActiveController()
	end
	if moveVector and activeController then
		activeControllerIsTouch = activeController.thumbstickFrame ~= nil or activeController.thumbpadFrame ~= nil
	end

	if activeControllerIsTouch then
		return (moveVector and moveVector.Magnitude >= 0.9)
	else
		return false
	end
end

function ShoulderCamera:sprintFromGamepadInput()
	return self.movementPan.Magnitude > 0.9
end

function ShoulderCamera:onCurrentCharacterChanged(character)
	self.currentCharacter = character
	if self.currentCharacter then
		self.raycastIgnoreList[1] = self.currentCharacter
		self.currentHumanoid = character:WaitForChild("Humanoid")
		self.currentRootPart = character:WaitForChild("HumanoidRootPart")

		self.rootRigAttach = self.currentRootPart:WaitForChild("RootRigAttachment")
		self.rootJoint = character:WaitForChild("LowerTorso"):WaitForChild("Root")
		self.currentWaist = character:WaitForChild("UpperTorso"):WaitForChild("Waist")
		self.currentWrist = character:WaitForChild("RightHand"):WaitForChild("RightWrist")
		self.wristAttach0 = character:WaitForChild("RightLowerArm"):WaitForChild("RightWristRigAttachment")
		self.wristAttach1 = character:WaitForChild("RightHand"):WaitForChild("RightWristRigAttachment")
		self.rightGripAttachment = character:WaitForChild("RightHand"):WaitForChild("RightGripAttachment")

		self.currentTool = character:FindFirstChildOfClass("Tool")

		self.eventConnections.humanoidDied = self.currentHumanoid.Died:Connect(function()
			self.zoomedFromInput = false
			self:updateZoomState()
		end)
		self.eventConnections.characterChildAdded = character.ChildAdded:Connect(function(child)
			if child:IsA("Tool") then
				self.currentTool = child
				self:updateZoomState()
			end
		end)
		self.eventConnections.characterChildRemoved = character.ChildRemoved:Connect(function(child)
			if child:IsA("Tool") and self.currentTool == child then
				self.currentTool = character:FindFirstChildOfClass("Tool")
				self:updateZoomState()
			end
		end)

		if Players.LocalPlayer then
			local PlayerScripts = Players.LocalPlayer:FindFirstChild("PlayerScripts")
			if PlayerScripts then
				local PlayerModule = PlayerScripts:FindFirstChild("PlayerModule")
				if PlayerModule then
					self.controlModule = require(PlayerModule:FindFirstChild("ControlModule"))
				end
			end
		end
	else
		if self.eventConnections.humanoidDied then
			self.eventConnections.humanoidDied:Disconnect()
			self.eventConnections.humanoidDied = nil
		end
		if self.eventConnections.characterChildAdded then
			self.eventConnections.characterChildAdded:Disconnect()
			self.eventConnections.characterChildAdded = nil
		end
		if self.eventConnections.characterChildRemoved then
			self.eventConnections.characterChildRemoved:Disconnect()
			self.eventConnections.characterChildRemoved = nil
		end

		self.currentTool = nil
		self.currentHumanoid = nil
		self.currentRootPart = nil
		self.controlModule = nil
	end
end

function ShoulderCamera:onCurrentCameraChanged(camera)
	if self.currentCamera == camera then
		return
	end

	self.currentCamera = camera

	if self.currentCamera then
		self.raycastIgnoreList[2] = self.currentCamera

		if self.eventConnections.cameraTypeChanged then
			self.eventConnections.cameraTypeChanged:Disconnect()
			self.eventConnections.cameraTypeChanged = nil
		end
		self.eventConnections.cameraTypeChanged = self.currentCamera:GetPropertyChangedSignal("CameraType"):Connect(function()
			if self.enabled then
				self.currentCamera.CameraType = Enum.CameraType.Scriptable
			end
		end)
	end
end

function ShoulderCamera:isHumanoidControllable()
	if not self.currentHumanoid then
		return false
	end
	local humanoidState = self.currentHumanoid:GetState()
	return CONTROLLABLE_HUMANOID_STATES[humanoidState] == true
end

function ShoulderCamera:getCollisionRadius()
	if not self.currentCamera then
		return 0
	end
	local viewportSize = self.currentCamera.ViewportSize
	local aspectRatio = viewportSize.X / viewportSize.Y
	local fovRads = math.rad(self.fieldOfView)
	local imageHeight = math.tan(fovRads) * math.abs(self.currentCamera.NearPlaneZ)
	local imageWidth = imageHeight * aspectRatio

	local cornerPos = Vector3.new(imageWidth, imageHeight, self.currentCamera.NearPlaneZ)
	return cornerPos.Magnitude
end

function ShoulderCamera:penetrateCast(ray, ignoreList)
	local tries = 0
	local hitPart, hitPoint, hitNormal, hitMaterial = nil, ray.Origin + ray.Direction, Vector3.new(0, 1, 0), Enum.Material.Air
	while tries < 50 do
		tries = tries + 1
		hitPart, hitPoint, hitNormal, hitMaterial = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList, false, true)
		if hitPart and not hitPart.CanCollide then
			table.insert(ignoreList, hitPart)
		else
			break
		end
	end
	return hitPart, hitPoint, hitNormal, hitMaterial
end

function ShoulderCamera:getRelativePitch()
	if self.currentRootPart then
		local pitchRotation = CFrame.Angles(self.pitch, 0, 0)
		local relativeRotation = self.currentRootPart.CFrame:toObjectSpace(pitchRotation)
		local relativeLook = relativeRotation.lookVector

		local angle = math.asin(relativeLook.Y)
		return math.clamp(angle, self.minPitch, self.maxPitch)
	end
	return self.pitch
end

function ShoulderCamera:getCurrentFieldOfView()
	if self.zoomState then
		return self.zoomedFOV
	else
		return self.fieldOfView
	end
end

function ShoulderCamera:handlePartTransparencies()
	local partsLookup = {}
	local accoutrementsLookup = {}

	for _, child in pairs(self.currentCharacter:GetChildren()) do
		local hidden = false
		if child:IsA("BasePart") then
			hidden = partsLookup[child.Name] == true
			child.LocalTransparencyModifier = hidden and 1 or 0
		elseif child:IsA("Accoutrement") then
			local descendants = child:GetDescendants()
			local accoutrementParts = {}
			for _, desc in pairs(descendants) do
				if desc:IsA("Attachment") and accoutrementsLookup[desc.Name] then
					hidden = true
				elseif desc:IsA("BasePart") then
					table.insert(accoutrementParts, desc)
				end
			end
			for _, part in pairs(accoutrementParts) do
				part.LocalTransparencyModifier = hidden and 1 or 0
			end
		elseif child:IsA("Tool") then
			hidden = self.zoomState and (self.hasScope or self.hideToolWhileZoomed)
			for _, part in pairs(child:GetDescendants()) do
				if part:IsA("BasePart") then
					part.LocalTransparencyModifier = hidden and 1 or 0
				end
			end
		end
	end
end

function ShoulderCamera:setSprintEnabled(enabled)
	self.sprintEnabled = enabled
end

function ShoulderCamera:setSlowZoomWalkEnabled(enabled)
	self.slowZoomWalkEnabled = enabled
end

function ShoulderCamera:setHasScope(hasScope)
	if self.hasScope == hasScope then
		return
	end

	self.hasScope = hasScope
	self:updateZoomState()
end

function ShoulderCamera:onSprintAction(actionName, inputState, inputObj)
	self.sprintingInputActivated = inputState == Enum.UserInputState.Begin
end

