PlayerScripts/StarterPlayerScripts_NewStructure/RobloxPlayerScript/CameraScript/Poppercam/Zoom.lua
--------------------------------------------------------------------------------
-- Zoom.lua
-- Controls the distance between the focus and the camera.
--------------------------------------------------------------------------------

local ZOOM_STIFFNESS    = 4.5
local ZOOM_DEFAULT      = 16
local ZOOM_ACCELERATION = 0.0375

local ZOOM_OPAQUE       = 2
local ZOOM_TRANSPARENT  = 0.5

local ConstrainedSpring = require(script:WaitForChild("ConstrainedSpring"))
local Popper = require(script:WaitForChild("Popper"))

local cframe = CFrame.new
local clamp = math.clamp
local min = math.min
local max = math.max

local DIST_MIN, DIST_MAX do
	local Player = game:GetService("Players").LocalPlayer

	local function updateBounds()
		DIST_MIN = Player.CameraMinZoomDistance
		DIST_MAX = Player.CameraMaxZoomDistance
	end

	updateBounds()

	Player:GetPropertyChangedSignal('CameraMinZoomDistance'):Connect(updateBounds)
	Player:GetPropertyChangedSignal('CameraMaxZoomDistance'):Connect(updateBounds)
end

--------------------------------------------------------------------------------

local zoomSpring = ConstrainedSpring.new(ZOOM_STIFFNESS, ZOOM_DEFAULT, DIST_MIN, DIST_MAX)

local function zoomToTransparency(zoom)
	zoom = clamp(zoom, ZOOM_TRANSPARENT, ZOOM_OPAQUE)
	local t = (zoom - ZOOM_TRANSPARENT)/(ZOOM_OPAQUE - ZOOM_TRANSPARENT)
	return 1 - (3 - 2*t)*t*t
end

local function stepTargetZoom(z, dz, zoomMin, zoomMax)
	z = clamp(z + dz*(1 + z*ZOOM_ACCELERATION), zoomMin, zoomMax)
	if z < ZOOM_OPAQUE then
		z = dz <= 0 and zoomMin or ZOOM_OPAQUE
	end
	return z
end

--------------------------------------------------------------------------------

function Zoom(renderDt, zoomDelta, focus, subjectRootPart)
	
	local prmax = max(zoomSpring.x, stepTargetZoom(zoomSpring.goal, zoomDelta, DIST_MIN, DIST_MAX))
	
	local poppedZoom = Popper(focus*cframe(0, 0, DIST_MIN), prmax - DIST_MIN, subjectRootPart) + DIST_MIN
	
	local zoomMin = DIST_MIN
	local zoomMax = min(DIST_MAX, poppedZoom)
	zoomSpring:setBounds(zoomMin, zoomMax)
	
	local isPopped = zoomSpring.goal > zoomMax
	
	if zoomDelta ~= 0 then
		if not isPopped then
			zoomSpring.goal = stepTargetZoom(zoomSpring.goal, zoomDelta, zoomMin, zoomMax)
		elseif zoomDelta < 0 then
			zoomSpring.goal = stepTargetZoom(zoomMax, zoomDelta, zoomMin, zoomMax)
		end
	end
	
	local zoom = zoomSpring:step(renderDt)
	local fp = zoomSpring.goal == DIST_MIN
	local transparency = fp and zoomToTransparency(zoom) or 0
	
	return zoom, transparency, fp
end

return Zoom
