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

return TransparencyController
