function ShoulderCamera.applyInput(E,v,R)local e=UserGameSettings:GetCameraYInvertValue()E.yaw=E.yaw+v E.pitch=math.clamp(E.pitch+R*e,E.minPitch,E.maxPitch)end function ShoulderCamera.processGamepadInput(R,v)local E=R.gamepadPan if E then E=gamepadLinearToCurve(E)if E.X==0 and E.Y==0 then R.lastThumbstickTime=nil if R.lastThumbstickPos.X==0 and R.lastThumbstickPos.Y==0 then R.currentGamepadSpeed=0 end end local e=0 local U=tick()if R.lastThumbstickTime then local Z=(U-R.lastThumbstickTime)*10 R.currentGamepadSpeed=R.currentGamepadSpeed+6*(Z^2/.7)if R.currentGamepadSpeed>6 then R.currentGamepadSpeed=6 end if R.lastGamepadVelocity then local v=(E-R.lastThumbstickPos)/(U-R.lastThumbstickTime)local e=(v-R.lastGamepadVelocity).Magnitude if e>12 then R.currentGamepadSpeed=R.currentGamepadSpeed*(20/e)if R.currentGamepadSpeed>6 then R.currentGamepadSpeed=6 end end end e=(GameSettings.GamepadCameraSensitivity*R.currentGamepadSpeed)*v R.lastGamepadVelocity=(E-R.lastThumbstickPos)/(U-R.lastThumbstickTime)end R.lastThumbstickPos=E R.lastThumbstickTime=U local Z=(-E.X*e)*R.gamepadSensitivityModifier.X local a=((e*E.Y)*GameSettings:GetCameraYInvertValue())*R.gamepadSensitivityModifier.Y R:applyInput(Z,a)end end function ShoulderCamera.handleTouchToolFiring(v)if v.touchObj then if v.lastTapEndTime then local R=tick()-v.lastTapEndTime if R<v.touchDelayTime and(v.currentTool and(v.touchPanAccumulator.Magnitude<.5 and(not v.firingTool and not v.applyingTouchPan)))then v.firingTool=true v.currentTool:Activate()end end else if v.currentTool and v.firingTool then v.currentTool:Deactivate()end v.firingTool=false end end function ShoulderCamera.isTouchPositionForCamera(R,v)if LocalPlayer then local R=LocalPlayer.PlayerGui:GetGuiObjectsAtPosition(v.X,v.Y)for v,R in ipairs(R)do if R.Name=="\068\121\110\097\109\105\099\084\104\117\109\098\115\116\105\099\107\070\114\097\109\101"then return false end end return true end return false end function ShoulderCamera.onInputBegan(E,v,R)if E.touchObj then E.touchObj=nil R=false end if v.KeyCode==Enum.KeyCode.Thumbstick2 then E.gamepadPan=Vector2.new(v.Position.X,v.Position.Y)elseif v.KeyCode==Enum.KeyCode.Thumbstick1 then E.movementPan=Vector2.new(v.Position.X,v.Position.Y)elseif v.UserInputType==Enum.UserInputType.Touch then local e=Vector2.new(v.Position.X,v.Position.Y)if not R and(E:isTouchPositionForCamera(e)and not E.touchObj)then E.touchObj=v E.touchStartTime=tick()E.eventConnections.touchChanged=v.Changed:Connect(function(R)if R=="\080\111\115\105\116\105\111\110"then local R=tick()-E.touchStartTime local U=Vector2.new(v.Position.X,v.Position.Y)local Z=(U-e)*E.currentTouchSensitivity local a=-Z.X local F=-Z.Y if E.touchPanAccumulator.Magnitude>.01 and R>E.touchDelayTime then if not E.applyingTouchPan then E.applyingTouchPan=true E.touchPanAccumulator=Vector2.new(0,0)end end E:applyInput(a,F)E.touchPanAccumulator=E.touchPanAccumulator+Vector2.new(a,F)e=U end end)end end end function ShoulderCamera.onInputChanged(E,v,R)if v.UserInputType==Enum.UserInputType.MouseMovement then local R=-v.Delta.X*E.currentMouseRadsPerPixel.X local e=-v.Delta.Y*E.currentMouseRadsPerPixel.Y E:applyInput(R,e)elseif v.KeyCode==Enum.KeyCode.Thumbstick2 then E.gamepadPan=Vector2.new(v.Position.X,v.Position.Y)elseif v.KeyCode==Enum.KeyCode.Thumbstick1 then E.movementPan=Vector2.new(v.Position.X,v.Position.Y)end end function ShoulderCamera.onInputEnded(E,v,R)if v.KeyCode==Enum.KeyCode.Thumbstick2 then E.gamepadPan=Vector2.new(0,0)elseif v.KeyCode==Enum.KeyCode.Thumbstick1 then E.movementPan=Vector2.new(0,0)elseif v.UserInputType==Enum.UserInputType.Touch then if E.touchObj==v then if E.eventConnections and E.eventConnections.touchChanged then E.eventConnections.touchChanged:Disconnect()E.eventConnections.touchChanged=nil end local v=tick()-E.touchStartTime if E.currentTool and E.firingTool then E.currentTool:Deactivate()elseif E.zoomState and(E.hasScope and(v<E.touchDelayTime and not E.applyingTouchPan))then E.currentTool:Activate()E.currentTool:Deactivate()end E.firingTool=false E.touchPanAccumulator=Vector2.new(0,0)if v<E.touchDelayTime and not E.applyingTouchPan then E.lastTapEndTime=tick()else E.lastTapEndTime=nil end E.applyingTouchPan=false E.gamepadPan=Vector2.new(0,0)E.touchObj=nil end end end return ShoulderCamera
-- Input related functions

function ShoulderCamera:applyInput(yaw, pitch)
	local yInvertValue = UserGameSettings:GetCameraYInvertValue()
	self.yaw = self.yaw + yaw
	self.pitch = math.clamp(self.pitch + pitch * yInvertValue, self.minPitch, self.maxPitch)
end

function ShoulderCamera:processGamepadInput(dt)
	local gamepadPan = self.gamepadPan
	if gamepadPan then
		gamepadPan = gamepadLinearToCurve(gamepadPan)
		if gamepadPan.X == 0 and gamepadPan.Y == 0 then
			self.lastThumbstickTime = nil
			if self.lastThumbstickPos.X == 0 and self.lastThumbstickPos.Y == 0 then
				self.currentGamepadSpeed = 0
			end
		end

		local finalConstant = 0
		local currentTime = tick()

		if self.lastThumbstickTime then
			local elapsed = (currentTime - self.lastThumbstickTime) * 10
			self.currentGamepadSpeed = self.currentGamepadSpeed + (6 * ((elapsed ^ 2) / 0.7))

			if self.currentGamepadSpeed > 6 then
				self.currentGamepadSpeed = 6
			end

			if self.lastGamepadVelocity then
				local velocity = (gamepadPan - self.lastThumbstickPos) / (currentTime - self.lastThumbstickTime)
				local velocityDeltaMag = (velocity - self.lastGamepadVelocity).Magnitude

				if velocityDeltaMag > 12 then
					self.currentGamepadSpeed = self.currentGamepadSpeed * (20 / velocityDeltaMag)
					if self.currentGamepadSpeed > 6 then
						self.currentGamepadSpeed = 6
					end
				end
			end

			finalConstant = GameSettings.GamepadCameraSensitivity * self.currentGamepadSpeed * dt
			self.lastGamepadVelocity = (gamepadPan - self.lastThumbstickPos) / (currentTime - self.lastThumbstickTime)
		end
		self.lastThumbstickPos = gamepadPan
		self.lastThumbstickTime = currentTime

		local yawInput = -gamepadPan.X * finalConstant * self.gamepadSensitivityModifier.X
		local pitchInput = finalConstant
			* gamepadPan.Y
			* GameSettings:GetCameraYInvertValue()
			* self.gamepadSensitivityModifier.Y

		self:applyInput(yawInput, pitchInput)
	end
end

function ShoulderCamera:handleTouchToolFiring()
	if self.touchObj then
		if self.lastTapEndTime then -- and not (self.zoomState and self.hasScope) then
			local touchTime = tick() - self.lastTapEndTime
			if
				touchTime < self.touchDelayTime
				and self.currentTool
				and self.touchPanAccumulator.Magnitude < 0.5
				and not self.firingTool
				and not self.applyingTouchPan
			then
				self.firingTool = true
				self.currentTool:Activate()
			end
		end
	else
		if self.currentTool and self.firingTool then
			self.currentTool:Deactivate()
		end
		self.firingTool = false
	end
end

function ShoulderCamera:isTouchPositionForCamera(pos)
	if LocalPlayer then
		local guiObjects = LocalPlayer.PlayerGui:GetGuiObjectsAtPosition(pos.X, pos.Y)
		for _, guiObject in ipairs(guiObjects) do
			if guiObject.Name == "DynamicThumbstickFrame" then
				return false
			end
		end
		return true
	end
	return false
end

function ShoulderCamera:onInputBegan(inputObj, wasProcessed)
	if self.touchObj then
		self.touchObj = nil
		wasProcessed = false
	end

	if inputObj.KeyCode == Enum.KeyCode.Thumbstick2 then
		self.gamepadPan = Vector2.new(inputObj.Position.X, inputObj.Position.Y)
	elseif inputObj.KeyCode == Enum.KeyCode.Thumbstick1 then
		self.movementPan = Vector2.new(inputObj.Position.X, inputObj.Position.Y)
	elseif inputObj.UserInputType == Enum.UserInputType.Touch then
		local touchStartPos = Vector2.new(inputObj.Position.X, inputObj.Position.Y)
		if not wasProcessed and self:isTouchPositionForCamera(touchStartPos) and not self.touchObj then
			self.touchObj = inputObj
			self.touchStartTime = tick()
			self.eventConnections.touchChanged = inputObj.Changed:Connect(function(prop)
				if prop == "Position" then
					local touchTime = tick() - self.touchStartTime

					local newTouchPos = Vector2.new(inputObj.Position.X, inputObj.Position.Y)
					local delta = (newTouchPos - touchStartPos) * self.currentTouchSensitivity
					local yawInput = -delta.X
					local pitchInput = -delta.Y
					if self.touchPanAccumulator.Magnitude > 0.01 and touchTime > self.touchDelayTime then
						if not self.applyingTouchPan then
							self.applyingTouchPan = true
							self.touchPanAccumulator = Vector2.new(0, 0)
						end
					end
					self:applyInput(yawInput, pitchInput)
					self.touchPanAccumulator = self.touchPanAccumulator + Vector2.new(yawInput, pitchInput)
					touchStartPos = newTouchPos
				end
			end)
		end
	end
end

function ShoulderCamera:onInputChanged(inputObj, wasProcessed)
	if inputObj.UserInputType == Enum.UserInputType.MouseMovement then
		local yawInput = -inputObj.Delta.X * self.currentMouseRadsPerPixel.X
		local pitchInput = -inputObj.Delta.Y * self.currentMouseRadsPerPixel.Y

		self:applyInput(yawInput, pitchInput)
	elseif inputObj.KeyCode == Enum.KeyCode.Thumbstick2 then
		self.gamepadPan = Vector2.new(inputObj.Position.X, inputObj.Position.Y)
	elseif inputObj.KeyCode == Enum.KeyCode.Thumbstick1 then
		self.movementPan = Vector2.new(inputObj.Position.X, inputObj.Position.Y)
	end
end

function ShoulderCamera:onInputEnded(inputObj, wasProcessed)
	if inputObj.KeyCode == Enum.KeyCode.Thumbstick2 then
		self.gamepadPan = Vector2.new(0, 0)
	elseif inputObj.KeyCode == Enum.KeyCode.Thumbstick1 then
		self.movementPan = Vector2.new(0, 0)
	elseif inputObj.UserInputType == Enum.UserInputType.Touch then
		if self.touchObj == inputObj then
			if self.eventConnections and self.eventConnections.touchChanged then
				self.eventConnections.touchChanged:Disconnect()
				self.eventConnections.touchChanged = nil
			end

			local touchTime = tick() - self.touchStartTime
			if self.currentTool and self.firingTool then
				self.currentTool:Deactivate()
			elseif
				self.zoomState
				and self.hasScope
				and touchTime < self.touchDelayTime
				and not self.applyingTouchPan
			then
				self.currentTool:Activate() -- this makes sure to shoot the sniper with a single tap when it is zoomed in
				self.currentTool:Deactivate()
			end
			self.firingTool = false

			self.touchPanAccumulator = Vector2.new(0, 0)
			if touchTime < self.touchDelayTime and not self.applyingTouchPan then
				self.lastTapEndTime = tick()
			else
				self.lastTapEndTime = nil
			end
			self.applyingTouchPan = false

			self.gamepadPan = Vector2.new(0, 0)
			self.touchObj = nil
		end
	end
end

return ShoulderCamera
