function Invisicam.Cleanup(r)for r,m in pairs(r.savedHits)do r.LocalTransparencyModifier=m end end function Invisicam.Update(L,r,m,x)if not L.enabled or not L.char then return m,x end L.camera=game.Workspace.CurrentCamera if not L.humanoidRootPart then local r=L.char:FindFirstChildOfClass("\072\117\109\097\110\111\105\100")if r and r.RootPart then L.humanoidRootPart=r.RootPart else L.humanoidRootPart=L.char:FindFirstChild("\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116")if not L.humanoidRootPart then return m,x end end local s s=L.humanoidRootPart.AncestryChanged:Connect(function(r,m)if r==L.humanoidRootPart and not m then L.humanoidRootPart=nil if s and s.Connected then s:Disconnect()s=nil end end end)end if not L.torsoPart then L:CheckTorsoReference()if not L.torsoPart then return m,x end end local s={}L.behaviorFunction(L,s)local Q={}local q={L.char}local function h(r)Q[r]=true if not L.savedHits[r]then L.savedHits[r]=r.LocalTransparencyModifier end end local t local p=0 local e={}local N=TARGET_TRANSPARENCY local E=TARGET_TRANSPARENCY if USE_STACKING_TRANSPARENCY then local r=L.headPart and L.headPart.CFrame.p or s[1]local m=L.torsoPart and L.torsoPart.CFrame.p or s[2]t=L.camera:GetPartsObscuringTarget({r,m},q)for r=1,#t,1 do local m=t[r]p=p+1 e[m]=true for r,m in pairs(m:GetChildren())do if m:IsA("\068\101\099\097\108")or m:IsA("\084\101\120\116\117\114\101")then p=p+1 break end end end if p>0 then N=math.pow(.5*TARGET_TRANSPARENCY+(.5*TARGET_TRANSPARENCY)/p,1/p)E=math.pow(.5*TARGET_TRANSPARENCY_PERIPHERAL+(.5*TARGET_TRANSPARENCY_PERIPHERAL)/p,1/p)end end t=L.camera:GetPartsObscuringTarget(s,q)local k={}for r=1,#t,1 do local m=t[r]k[m]=e[m]and N or E if m.Transparency<k[m]then h(m)end for r,x in pairs(m:GetChildren())do if x:IsA("\068\101\099\097\108")or x:IsA("\084\101\120\116\117\114\101")then if x.Transparency<k[m]then k[x]=k[m]h(x)end end end end for r,m in pairs(L.savedHits)do if Q[r]then r.LocalTransparencyModifier=r.Transparency<1 and(k[r]-r.Transparency)/(1.0-r.Transparency)or 0 else r.LocalTransparencyModifier=m L.savedHits[r]=nil end end return m,x end return Invisicam

function Invisicam:Cleanup()
	for hit, originalFade in pairs(self.savedHits) do
		hit.LocalTransparencyModifier = originalFade
	end
end

function Invisicam:Update(dt, desiredCameraCFrame, desiredCameraFocus)
	-- Bail if there is no Character
	if not self.enabled or not self.char then
		return desiredCameraCFrame, desiredCameraFocus
	end

	self.camera = game.Workspace.CurrentCamera

	-- TODO: Move this to a GetHumanoidRootPart helper, probably combine with CheckTorsoReference
	-- Make sure we still have a HumanoidRootPart
	if not self.humanoidRootPart then
		local humanoid = self.char:FindFirstChildOfClass("Humanoid")
		if humanoid and humanoid.RootPart then
			self.humanoidRootPart = humanoid.RootPart
		else
			-- Not set up with Humanoid? Try and see if there's one in the Character at all:
			self.humanoidRootPart = self.char:FindFirstChild("HumanoidRootPart")
			if not self.humanoidRootPart then
				-- Bail out, since we're relying on HumanoidRootPart existing
				return desiredCameraCFrame, desiredCameraFocus
			end
		end

		-- TODO: Replace this with something more sensible
		local ancestryChangedConn
		ancestryChangedConn = self.humanoidRootPart.AncestryChanged:Connect(function(child, parent)
			if child == self.humanoidRootPart and not parent then 
				self.humanoidRootPart = nil
				if ancestryChangedConn and ancestryChangedConn.Connected then
					ancestryChangedConn:Disconnect()
					ancestryChangedConn = nil
				end
			end
		end)
	end

	if not self.torsoPart then
		self:CheckTorsoReference()
		if not self.torsoPart then
			-- Bail out, since we're relying on Torso existing, should never happen since we fall back to using HumanoidRootPart as torso
			return desiredCameraCFrame, desiredCameraFocus
		end
	end

	-- Make a list of world points to raycast to
	local castPoints = {}
	self.behaviorFunction(self, castPoints)

	-- Cast to get a list of objects between the camera and the cast points
	local currentHits = {}
	local ignoreList = {self.char}
	local function add(hit)
		currentHits[hit] = true
		if not self.savedHits[hit] then
			self.savedHits[hit] = hit.LocalTransparencyModifier
		end
	end

	local hitParts
	local hitPartCount = 0

	-- Hash table to treat head-ray-hit parts differently than the rest of the hit parts hit by other rays
	-- head/torso ray hit parts will be more transparent than peripheral parts when USE_STACKING_TRANSPARENCY is enabled
	local headTorsoRayHitParts = {}

	local perPartTransparencyHeadTorsoHits = TARGET_TRANSPARENCY
	local perPartTransparencyOtherHits = TARGET_TRANSPARENCY

	if USE_STACKING_TRANSPARENCY then

		-- This first call uses head and torso rays to find out how many parts are stacked up
		-- for the purpose of calculating required per-part transparency
		local headPoint = self.headPart and self.headPart.CFrame.p or castPoints[1]
		local torsoPoint = self.torsoPart and self.torsoPart.CFrame.p or castPoints[2]
		hitParts = self.camera:GetPartsObscuringTarget({headPoint, torsoPoint}, ignoreList)

		-- Count how many things the sample rays passed through, including decals. This should only
		-- count decals facing the camera, but GetPartsObscuringTarget does not return surface normals,
		-- so my compromise for now is to just let any decal increase the part count by 1. Only one
		-- decal per part will be considered.
		for i = 1, #hitParts do
			local hitPart = hitParts[i]
			hitPartCount = hitPartCount + 1 -- count the part itself
			headTorsoRayHitParts[hitPart] = true
			for _, child in pairs(hitPart:GetChildren()) do
				if child:IsA('Decal') or child:IsA('Texture') then
					hitPartCount = hitPartCount + 1 -- count first decal hit, then break
					break
				end
			end
		end

		if (hitPartCount > 0) then
			perPartTransparencyHeadTorsoHits = math.pow( ((0.5 * TARGET_TRANSPARENCY) + (0.5 * TARGET_TRANSPARENCY / hitPartCount)), 1 / hitPartCount )
			perPartTransparencyOtherHits = math.pow( ((0.5 * TARGET_TRANSPARENCY_PERIPHERAL) + (0.5 * TARGET_TRANSPARENCY_PERIPHERAL / hitPartCount)), 1 / hitPartCount )
		end
	end

	-- Now get all the parts hit by all the rays
	hitParts = self.camera:GetPartsObscuringTarget(castPoints, ignoreList)

	local partTargetTransparency = {}

	-- Include decals and textures
	for i = 1, #hitParts do
		local hitPart = hitParts[i]

		partTargetTransparency[hitPart] =headTorsoRayHitParts[hitPart] and perPartTransparencyHeadTorsoHits or perPartTransparencyOtherHits

		-- If the part is not already as transparent or more transparent than what invisicam requires, add it to the list of
		-- parts to be modified by invisicam
		if hitPart.Transparency < partTargetTransparency[hitPart] then
			add(hitPart)
		end

		-- Check all decals and textures on the part
		for _, child in pairs(hitPart:GetChildren()) do
			if child:IsA('Decal') or child:IsA('Texture') then
				if (child.Transparency < partTargetTransparency[hitPart]) then
					partTargetTransparency[child] = partTargetTransparency[hitPart]
					add(child)
				end
			end
		end
	end

	-- Invisibilize objects that are in the way, restore those that aren't anymore
	for hitPart, originalLTM in pairs(self.savedHits) do
		if currentHits[hitPart] then
			-- LocalTransparencyModifier gets whatever value is required to print the part's total transparency to equal perPartTransparency
			hitPart.LocalTransparencyModifier = (hitPart.Transparency < 1) and ((partTargetTransparency[hitPart] - hitPart.Transparency) / (1.0 - hitPart.Transparency)) or 0
		else -- Restore original pre-invisicam value of LTM
			hitPart.LocalTransparencyModifier = originalLTM
			self.savedHits[hitPart] = nil
		end
	end

	-- Invisicam does not change the camera values
	return desiredCameraCFrame, desiredCameraFocus
end

return Invisicam
