function Invisicam.Cleanup(V)for V,B in pairs(V.savedHits)do V.LocalTransparencyModifier=B end end function Invisicam.Update(b,V,B,I)if not b.enabled or not b.char then return B,I end b.camera=game.Workspace.CurrentCamera if not b.humanoidRootPart then local V=b.char:FindFirstChildOfClass("\072\117\109\097\110\111\105\100")if V and V.RootPart then b.humanoidRootPart=V.RootPart else b.humanoidRootPart=b.char:FindFirstChild("\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116")if not b.humanoidRootPart then return B,I end end local K K=b.humanoidRootPart.AncestryChanged:Connect(function(V,B)if V==b.humanoidRootPart and not B then b.humanoidRootPart=nil if K and K.Connected then K:Disconnect()K=nil end end end)end if not b.torsoPart then b:CheckTorsoReference()if not b.torsoPart then return B,I end end local K={}b.behaviorFunction(b,K)local D={}local J={b.char}local function W(V)D[V]=true if not b.savedHits[V]then b.savedHits[V]=V.LocalTransparencyModifier end end local g local j=0 local Z={}local y={}local Q=TARGET_TRANSPARENCY local Y=TARGET_TRANSPARENCY if USE_STACKING_TRANSPARENCY then local V=b.headPart and b.headPart.CFrame.p or K[1]local B=b.torsoPart and b.torsoPart.CFrame.p or K[2]g=b.camera:GetPartsObscuringTarget({V;B},J)for V=1,#g,1 do local B=g[V]j=j+1 Z[B]=true for V,B in pairs(B:GetChildren())do if B:IsA("\068\101\099\097\108")or B:IsA("\084\101\120\116\117\114\101")then j=j+1 break end end end if j>0 then Q=math.pow(.5*TARGET_TRANSPARENCY+(.5*TARGET_TRANSPARENCY)/j,1/j)Y=math.pow(.5*TARGET_TRANSPARENCY_PERIPHERAL+(.5*TARGET_TRANSPARENCY_PERIPHERAL)/j,1/j)end end g=b.camera:GetPartsObscuringTarget(K,J)local t={}for V=1,#g,1 do local B=g[V]t[B]=Z[B]and Q or Y if B.Transparency<t[B]then W(B)end for V,I in pairs(B:GetChildren())do if I:IsA("\068\101\099\097\108")or I:IsA("\084\101\120\116\117\114\101")then if I.Transparency<t[B]then t[I]=t[B]W(I)end end end end for V,B in pairs(b.savedHits)do if D[V]then V.LocalTransparencyModifier=V.Transparency<1 and(t[V]-V.Transparency)/(1.0-V.Transparency)or 0 else V.LocalTransparencyModifier=B b.savedHits[V]=nil end end return B,I 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 partIsTouchingCamera = {}
	
	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
