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