local i=2.8 local function k(i,k,b)if i<=k then return math.min(k,math.max(i,b))end return b end local function b(i,k)k=k or 0 local b=10^k return math.floor(i*b+.5)/b end local function O()local O={}local a=tick()local U=false local m=false local I=nil local C,d=nil,nil local H={}local V={}local N={}local function j(i)if i.Parent==nil then return false end return i.Parent:IsA("\084\111\111\108")or j(i.Parent)end local function Y(i)if i:IsA("\066\097\115\101\080\097\114\116")or i:IsA("\068\101\099\097\108")then return not j(i)end return false end local function G(i)if i then if Y(i)then N[i]=true U=true end for i,k in pairs(i:GetChildren())do G(k)end end end local function v()for i,k in pairs(N)do i.LocalTransparencyModifier=0 end N={}U=true I=nil if C then C:disconnect()C=nil end if d then d:disconnect()d=nil end for i,k in pairs(H)do k:disconnect()H[i]=nil end for i,k in pairs(V)do k:disconnect()V[i]=nil end end local function Q(i)v()if C then C:disconnect()end C=i.DescendantAdded:connect(function(k)local b=k.Name=="\080\101\116"and k or i:FindFirstChild("\080\101\116")if b and k:IsDescendantOf(b)then return end if Y(k)then N[k]=true U=true elseif k:IsA("\084\111\111\108")then if H[k]then H[k]:disconnect()end H[k]=k.DescendantAdded:connect(function(i)N[i]=nil if i:IsA("\066\097\115\101\080\097\114\116")or i:IsA("\068\101\099\097\108")then i.LocalTransparencyModifier=0 end end)if V[k]then V[k]:disconnect()end V[k]=k.DescendantRemoving:connect(function(k)wait()if i and(k and k:IsDescendantOf(i))then if Y(k)then N[k]=true U=true end end end)end end)if d then d:disconnect()end d=i.DescendantRemoving:connect(function(i)if N[i]then N[i]=nil i.LocalTransparencyModifier=0 end end)G(i)end function O.SetEnabled(k,i)if m~=i then m=i k:Update()end end function O.SetSubject(k,i)local b=i and(i:IsA("\072\117\109\097\110\111\105\100")and i.Parent)if b then Q(b)else v()end end function O.Update(O)local C=false local d=tick()local H=workspace.CurrentCamera if H then local O=0 if not m then C=true else local m=(H.Focus.p-H.CoordinateFrame.p).magnitude O=(7-m)/5 if O<.5 then O=0 end if I then local b=O-I if not C and(O<1 and I<.95)then local O=i*(d-a)b=k(-O,O,b)end O=I+b else U=true end O=k(0,1,b(O,2))end if U or I~=O then for i,k in pairs(N)do i.LocalTransparencyModifier=O end U=false I=O end end a=d end return O end return O


local MAX_TWEEN_RATE = 2.8 -- per second

local function clamp(low, high, num)
	if low <= high then
		return math.min(high, math.max(low, num))
	end
	return num
end

local function Round(num, places)
	places = places or 0
	local decimalPivot = 10^places
	return math.floor(num * decimalPivot + 0.5) / decimalPivot
end

local function CreateTransparencyController()
	local module = {}


	local LastUpdate = tick()
	local TransparencyDirty = false
	local Enabled = false
	local LastTransparency = nil

	local DescendantAddedConn, DescendantRemovingConn = nil, nil
	local ToolDescendantAddedConns = {}
	local ToolDescendantRemovingConns = {}
	local CachedParts = {}

	local function HasToolAncestor(object)
		if object.Parent == nil then return false end
		return object.Parent:IsA('Tool') or HasToolAncestor(object.Parent)
	end

	local function IsValidPartToModify(part)
		if part:IsA('BasePart') or part:IsA('Decal') then
			return not HasToolAncestor(part)
		end
		return false
	end

	local function CachePartsRecursive(object)
		if object then
			if IsValidPartToModify(object) then
				CachedParts[object] = true
				TransparencyDirty = true
			end
			for _, child in pairs(object:GetChildren()) do
				CachePartsRecursive(child)
			end
		end
	end

	local function TeardownTransparency()
		for child, _ in pairs(CachedParts) do
			child.LocalTransparencyModifier = 0
		end
		CachedParts = {}
		TransparencyDirty = true
		LastTransparency = nil

		if DescendantAddedConn then
			DescendantAddedConn:disconnect()
			DescendantAddedConn = nil
		end
		if DescendantRemovingConn then
			DescendantRemovingConn:disconnect()
			DescendantRemovingConn = nil
		end
		for object, conn in pairs(ToolDescendantAddedConns) do
			conn:disconnect()
			ToolDescendantAddedConns[object] = nil
		end
		for object, conn in pairs(ToolDescendantRemovingConns) do
			conn:disconnect()
			ToolDescendantRemovingConns[object] = nil
		end
	end

	local function SetupTransparency(character)
		TeardownTransparency()

		if DescendantAddedConn then DescendantAddedConn:disconnect() end
		DescendantAddedConn = character.DescendantAdded:connect(function(object)
			local Pet = (object.Name == "Pet" and object) or character:FindFirstChild("Pet");
			if Pet and object:IsDescendantOf(Pet) then
				return;
			end
			-- This is a part we want to invisify
			if IsValidPartToModify(object) then
				CachedParts[object] = true
				TransparencyDirty = true
			-- There is now a tool under the character
			elseif object:IsA('Tool') then
				if ToolDescendantAddedConns[object] then ToolDescendantAddedConns[object]:disconnect() end
				ToolDescendantAddedConns[object] = object.DescendantAdded:connect(function(toolChild)
					CachedParts[toolChild] = nil
					if toolChild:IsA('BasePart') or toolChild:IsA('Decal') then
						-- Reset the transparency
						toolChild.LocalTransparencyModifier = 0
					end
				end)
				if ToolDescendantRemovingConns[object] then ToolDescendantRemovingConns[object]:disconnect() end
				ToolDescendantRemovingConns[object] = object.DescendantRemoving:connect(function(formerToolChild)
					wait() -- wait for new parent
					if character and formerToolChild and formerToolChild:IsDescendantOf(character) then
						if IsValidPartToModify(formerToolChild) then
							CachedParts[formerToolChild] = true
							TransparencyDirty = true
						end
					end
				end)
			end
		end)
		if DescendantRemovingConn then DescendantRemovingConn:disconnect() end
		DescendantRemovingConn = character.DescendantRemoving:connect(function(object)
			if CachedParts[object] then
				CachedParts[object] = nil
				-- Reset the transparency
				object.LocalTransparencyModifier = 0
			end
		end)
		CachePartsRecursive(character)
	end


	function module:SetEnabled(newState)
		if Enabled ~= newState then
			Enabled = newState
			self:Update()
		end
	end

	function module:SetSubject(subject)
		local character = subject and subject:IsA('Humanoid') and subject.Parent
		if character then
			SetupTransparency(character)
		else
			TeardownTransparency()
		end
	end

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

		if currentCamera then
			local transparency = 0
			if not Enabled then
				instant = true
			else
				local distance = (currentCamera.Focus.p - currentCamera.CoordinateFrame.p).magnitude
				transparency = (7 - distance) / 5
				if transparency < 0.5 then
					transparency = 0
				end

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

				transparency = clamp(0, 1, Round(transparency, 2))
			end

			if TransparencyDirty or LastTransparency ~= transparency then
				for child, _ in pairs(CachedParts) do
					child.LocalTransparencyModifier = transparency
				end
				TransparencyDirty = false
				LastTransparency = transparency
			end
		end
		LastUpdate = now
	end

	return module
end

return CreateTransparencyController
