local q={}q.__index=q function q.new()local O=setmetatable({},q)O.lastUpdate=tick()O.transparencyDirty=false O.enabled=false O.lastTransparency=nil O.descendantAddedConn,O.descendantRemovingConn=nil,nil O.toolDescendantAddedConns={}O.toolDescendantRemovingConns={}O.cachedParts={}return O end function q.HasToolAncestor(O,q)if q.Parent==nil then return false end return q.Parent:IsA("\084\111\111\108")or O:HasToolAncestor(q.Parent)end function q.IsValidPartToModify(O,q)if q:IsA("\066\097\115\101\080\097\114\116")or q:IsA("\068\101\099\097\108")then return not O:HasToolAncestor(q)end return false end function q.CachePartsRecursive(O,q)if q then if O:IsValidPartToModify(q)then O.cachedParts[q]=true O.transparencyDirty=true end for q,V in pairs(q:GetChildren())do O:CachePartsRecursive(V)end end end function q.TeardownTransparency(q)for q,O in pairs(q.cachedParts)do q.LocalTransparencyModifier=0 end q.cachedParts={}q.transparencyDirty=true q.lastTransparency=nil if q.descendantAddedConn then q.descendantAddedConn:disconnect()q.descendantAddedConn=nil end if q.descendantRemovingConn then q.descendantRemovingConn:disconnect()q.descendantRemovingConn=nil end for O,V in pairs(q.toolDescendantAddedConns)do V:Disconnect()q.toolDescendantAddedConns[O]=nil end for O,V in pairs(q.toolDescendantRemovingConns)do V:Disconnect()q.toolDescendantRemovingConns[O]=nil end end function q.SetupTransparency(O,q)O:TeardownTransparency()if O.descendantAddedConn then O.descendantAddedConn:disconnect()end O.descendantAddedConn=q.DescendantAdded:Connect(function(V)if O:IsValidPartToModify(V)then O.cachedParts[V]=true O.transparencyDirty=true elseif V:IsA("\084\111\111\108")then if O.toolDescendantAddedConns[V]then O.toolDescendantAddedConns[V]:Disconnect()end O.toolDescendantAddedConns[V]=V.DescendantAdded:Connect(function(q)O.cachedParts[q]=nil if q:IsA("\066\097\115\101\080\097\114\116")or q:IsA("\068\101\099\097\108")then q.LocalTransparencyModifier=0 end end)if O.toolDescendantRemovingConns[V]then O.toolDescendantRemovingConns[V]:disconnect()end O.toolDescendantRemovingConns[V]=V.DescendantRemoving:Connect(function(V)wait()if q and(V and V:IsDescendantOf(q))then if O:IsValidPartToModify(V)then O.cachedParts[V]=true O.transparencyDirty=true end end end)end end)if O.descendantRemovingConn then O.descendantRemovingConn:disconnect()end O.descendantRemovingConn=q.DescendantRemoving:connect(function(q)if O.cachedParts[q]then O.cachedParts[q]=nil q.LocalTransparencyModifier=0 end end)O:CachePartsRecursive(q)end function q.Enable(O,q)if O.enabled~=q then O.enabled=q O:Update()end end function q.SetSubject(O,q)local V=nil if q and q:IsA("\072\117\109\097\110\111\105\100")then V=q.Parent end if q and(q:IsA("\086\101\104\105\099\108\101\083\101\097\116")and q.Occupant)then V=q.Occupant.Parent end if V then O:SetupTransparency(V)else O:TeardownTransparency()end end function q.Update(q)local O=false local V=tick()local d=workspace.CurrentCamera if d then local u=0 if not q.enabled then O=true else local U=(d.Focus.p-d.CoordinateFrame.p).magnitude u=U<2 and 1.0-(U-.5)/1.5 or 0 if u<.5 then u=0 end if q.lastTransparency then local d=u-q.lastTransparency if not O and(u<1 and q.lastTransparency<.95)then local O=MAX_TWEEN_RATE*(V-q.lastUpdate)d=math.clamp(d,-O,O)end u=q.lastTransparency+d else q.transparencyDirty=true end u=math.clamp(Util.Round(u,2),0,1)end if q.transparencyDirty or q.lastTransparency~=u then for q,O in pairs(q.cachedParts)do q.LocalTransparencyModifier=u end q.transparencyDirty=false q.lastTransparency=u end end q.lastUpdate=V end return q
--[[ The Module ]]
--
local TransparencyController = {}
TransparencyController.__index = TransparencyController

function TransparencyController.new()
	local self = setmetatable({}, TransparencyController)

	self.lastUpdate = tick()
	self.transparencyDirty = false
	self.enabled = false
	self.lastTransparency = nil

	self.descendantAddedConn, self.descendantRemovingConn = nil, nil
	self.toolDescendantAddedConns = {}
	self.toolDescendantRemovingConns = {}
	self.cachedParts = {}

	return self
end

function TransparencyController:HasToolAncestor(object)
	if object.Parent == nil then
		return false
	end
	return object.Parent:IsA("Tool") or self:HasToolAncestor(object.Parent)
end

function TransparencyController:IsValidPartToModify(part)
	if part:IsA("BasePart") or part:IsA("Decal") then
		return not self:HasToolAncestor(part)
	end
	return false
end

function TransparencyController:CachePartsRecursive(object)
	if object then
		if self:IsValidPartToModify(object) then
			self.cachedParts[object] = true
			self.transparencyDirty = true
		end
		for _, child in pairs(object:GetChildren()) do
			self:CachePartsRecursive(child)
		end
	end
end

function TransparencyController:TeardownTransparency()
	for child, _ in pairs(self.cachedParts) do
		child.LocalTransparencyModifier = 0
	end
	self.cachedParts = {}
	self.transparencyDirty = true
	self.lastTransparency = nil

	if self.descendantAddedConn then
		self.descendantAddedConn:disconnect()
		self.descendantAddedConn = nil
	end
	if self.descendantRemovingConn then
		self.descendantRemovingConn:disconnect()
		self.descendantRemovingConn = nil
	end
	for object, conn in pairs(self.toolDescendantAddedConns) do
		conn:Disconnect()
		self.toolDescendantAddedConns[object] = nil
	end
	for object, conn in pairs(self.toolDescendantRemovingConns) do
		conn:Disconnect()
		self.toolDescendantRemovingConns[object] = nil
	end
end

function TransparencyController:SetupTransparency(character)
	self:TeardownTransparency()

	if self.descendantAddedConn then
		self.descendantAddedConn:disconnect()
	end
	self.descendantAddedConn = character.DescendantAdded:Connect(function(object)
		-- This is a part we want to invisify
		if self:IsValidPartToModify(object) then
			self.cachedParts[object] = true
			self.transparencyDirty = true
		-- There is now a tool under the character
		elseif object:IsA("Tool") then
			if self.toolDescendantAddedConns[object] then
				self.toolDescendantAddedConns[object]:Disconnect()
			end
			self.toolDescendantAddedConns[object] = object.DescendantAdded:Connect(function(toolChild)
				self.cachedParts[toolChild] = nil
				if toolChild:IsA("BasePart") or toolChild:IsA("Decal") then
					-- Reset the transparency
					toolChild.LocalTransparencyModifier = 0
				end
			end)
			if self.toolDescendantRemovingConns[object] then
				self.toolDescendantRemovingConns[object]:disconnect()
			end
			self.toolDescendantRemovingConns[object] = object.DescendantRemoving:Connect(function(formerToolChild)
				wait() -- wait for new parent
				if character and formerToolChild and formerToolChild:IsDescendantOf(character) then
					if self:IsValidPartToModify(formerToolChild) then
						self.cachedParts[formerToolChild] = true
						self.transparencyDirty = true
					end
				end
			end)
		end
	end)
	if self.descendantRemovingConn then
		self.descendantRemovingConn:disconnect()
	end
	self.descendantRemovingConn = character.DescendantRemoving:connect(function(object)
		if self.cachedParts[object] then
			self.cachedParts[object] = nil
			-- Reset the transparency
			object.LocalTransparencyModifier = 0
		end
	end)
	self:CachePartsRecursive(character)
end

function TransparencyController:Enable(enable)
	if self.enabled ~= enable then
		self.enabled = enable
		self:Update()
	end
end

function TransparencyController:SetSubject(subject)
	local character = nil
	if subject and subject:IsA("Humanoid") then
		character = subject.Parent
	end
	if subject and subject:IsA("VehicleSeat") and subject.Occupant then
		character = subject.Occupant.Parent
	end
	if character then
		self:SetupTransparency(character)
	else
		self:TeardownTransparency()
	end
end

function TransparencyController:Update()
	local instant = false
	local now = tick()
	local currentCamera = workspace.CurrentCamera

	if currentCamera then
		local transparency = 0
		if not self.enabled then
			instant = true
		else
			local distance = (currentCamera.Focus.p - currentCamera.CoordinateFrame.p).magnitude
			transparency = (distance < 2) and (1.0 - (distance - 0.5) / 1.5) or 0 --(7 - distance) / 5
			if transparency < 0.5 then
				transparency = 0
			end

			if self.lastTransparency then
				local deltaTransparency = transparency - self.lastTransparency

				-- Don't tween transparency if it is instant or your character was fully invisible last frame
				if not instant and transparency < 1 and self.lastTransparency < 0.95 then
					local maxDelta = MAX_TWEEN_RATE * (now - self.lastUpdate)
					deltaTransparency = math.clamp(deltaTransparency, -maxDelta, maxDelta)
				end
				transparency = self.lastTransparency + deltaTransparency
			else
				self.transparencyDirty = true
			end

			transparency = math.clamp(Util.Round(transparency, 2), 0, 1)
		end

		if self.transparencyDirty or self.lastTransparency ~= transparency then
			for child, _ in pairs(self.cachedParts) do
				child.LocalTransparencyModifier = transparency
			end
			self.transparencyDirty = false
			self.lastTransparency = transparency
		end
	end
	self.lastUpdate = now
end

return TransparencyController
