function Invisicam.Cleanup(C)for C,J in pairs(C.savedHits)do C.LocalTransparencyModifier=J end end function Invisicam.Update(O,C,J,t)if not O.enabled or not O.char then return J,t end O.camera=game.Workspace.CurrentCamera if not O.humanoidRootPart then local C=O.char:FindFirstChildOfClass("\072\117\109\097\110\111\105\100")if C and C.RootPart then O.humanoidRootPart=C.RootPart else O.humanoidRootPart=O.char:FindFirstChild("\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116")if not O.humanoidRootPart then return J,t end end local m m=O.humanoidRootPart.AncestryChanged:Connect(function(C,J)if C==O.humanoidRootPart and not J then O.humanoidRootPart=nil if m and m.Connected then m:Disconnect()m=nil end end end)end if not O.torsoPart then O:CheckTorsoReference()if not O.torsoPart then return J,t end end local m={}O.behaviorFunction(O,m)local h={}local g={O.char}local function s(C)h[C]=true if not O.savedHits[C]then O.savedHits[C]=C.LocalTransparencyModifier end end local d local Y=0 local r={}local D=TARGET_TRANSPARENCY local R=TARGET_TRANSPARENCY if USE_STACKING_TRANSPARENCY then local C=O.headPart and O.headPart.CFrame.p or m[1]local J=O.torsoPart and O.torsoPart.CFrame.p or m[2]d=O.camera:GetPartsObscuringTarget({C,J},g)for C=1,#d,1 do local J=d[C]Y=Y+1 r[J]=true for C,J in pairs(J:GetChildren())do if J:IsA("\068\101\099\097\108")or J:IsA("\084\101\120\116\117\114\101")then Y=Y+1 break end end end if Y>0 then D=math.pow(.5*TARGET_TRANSPARENCY+(.5*TARGET_TRANSPARENCY)/Y,1/Y)R=math.pow(.5*TARGET_TRANSPARENCY_PERIPHERAL+(.5*TARGET_TRANSPARENCY_PERIPHERAL)/Y,1/Y)end end d=O.camera:GetPartsObscuringTarget(m,g)local c={}for C=1,#d,1 do local J=d[C]c[J]=r[J]and D or R if J.Transparency<c[J]then s(J)end for C,t in pairs(J:GetChildren())do if t:IsA("\068\101\099\097\108")or t:IsA("\084\101\120\116\117\114\101")then if t.Transparency<c[J]then c[t]=c[J]s(t)end end end end for C,J in pairs(O.savedHits)do if h[C]then C.LocalTransparencyModifier=C.Transparency<1 and(c[C]-C.Transparency)/(1.0-C.Transparency)or 0 else C.LocalTransparencyModifier=J O.savedHits[C]=nil end end return J,t end return Invisicam
-- Want to turn off Invisicam? Be sure to call this after.
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
