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

return TransparencyController
