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

			transparency = Util.Clamp(0, 1, Util.Round(transparency, 2))
		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
