function Invisicam.Update(a)if not Character then return end if not HumanoidRootPart then local a=Character:FindFirstChildOfClass("\072\117\109\097\110\111\105\100")if a and a.Torso then HumanoidRootPart=a.Torso else HumanoidRootPart=Character:FindFirstChild("\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116")if not HumanoidRootPart then return end end local U U=HumanoidRootPart.AncestryChanged:connect(function(a,q)if a==HumanoidRootPart and not q then HumanoidRootPart=nil if U and U.Connected then U:Disconnect()U=nil end end end)end if not TorsoPart then CheckTorsoReference()if not TorsoPart then return end end local U={}BehaviorFunction(U)local q={}local y={Character}local function i(a)q[a]=true if not SavedHits[a]then SavedHits[a]=a.LocalTransparencyModifier end end local T local v=0 local C={}local s={}local K=TARGET_TRANSPARENCY local l=TARGET_TRANSPARENCY if USE_STACKING_TRANSPARENCY then local a=HeadPart and HeadPart.CFrame.p or U[1]local q=TorsoPart and TorsoPart.CFrame.p or U[2]T=Camera:GetPartsObscuringTarget({a,q},y)for a=1,#T,1 do local U=T[a]v=v+1 C[U]=true for a,U in pairs(U:GetChildren())do if U:IsA("\068\101\099\097\108")or U:IsA("\084\101\120\116\117\114\101")then v=v+1 break end end end if v>0 then K=math.pow(.5*TARGET_TRANSPARENCY+(.5*TARGET_TRANSPARENCY)/v,1/v)l=math.pow(.5*TARGET_TRANSPARENCY_PERIPHERAL+(.5*TARGET_TRANSPARENCY_PERIPHERAL)/v,1/v)end end T=Camera:GetPartsObscuringTarget(U,y)local h={}for a=1,#T,1 do local U=T[a]h[U]=C[U]and K or l if U.Transparency<h[U]then i(U)end for a,q in pairs(U:GetChildren())do if q:IsA("\068\101\099\097\108")or q:IsA("\084\101\120\116\117\114\101")then if q.Transparency<h[U]then h[q]=h[U]i(q)end end end end for a,U in pairs(SavedHits)do if q[a]then a.LocalTransparencyModifier=a.Transparency<1 and(h[a]-a.Transparency)/(1.0-a.Transparency)or 0 else a.LocalTransparencyModifier=U SavedHits[a]=nil end end end function Invisicam.SetMode(U,a)AssertTypes(a,"\110\117\109\098\101\114")for U,q in pairs(MODE)do if q==a then Mode=a BehaviorFunction=Behaviors[Mode]return end end error("\073\110\118\097\108\105\100 \109\111\100\101 \110\117\109\098\101\114")end function Invisicam.SetCustomBehavior(U,a)AssertTypes(a,"\102\117\110\099\116\105\111\110")Behaviors[MODE.CUSTOM]=a if Mode==MODE.CUSTOM then BehaviorFunction=a end end function Invisicam.GetObscuredParts(a)return SavedHits end

function Invisicam:Update()

	-- Bail if there is no Character
	if not Character then return end
	
	-- Make sure we still have a HumanoidRootPart
	if not HumanoidRootPart then
		local humanoid = Character:FindFirstChildOfClass("Humanoid")
		if humanoid and humanoid.Torso then
			HumanoidRootPart = humanoid.Torso
		else
			-- Not set up with Humanoid? Try and see if there's one in the Character at all:
			HumanoidRootPart = Character:FindFirstChild("HumanoidRootPart")
			if not HumanoidRootPart then
				-- Bail out, since we're relying on HumanoidRootPart existing
				return
			end
		end
		local ancestryChangedConn;
		ancestryChangedConn = HumanoidRootPart.AncestryChanged:connect(function(child, parent)
			if child == HumanoidRootPart and not parent then 
				HumanoidRootPart = nil
				if ancestryChangedConn and ancestryChangedConn.Connected then
					ancestryChangedConn:Disconnect()
					ancestryChangedConn = nil
				end
			end
		end)
	end
	
	if not TorsoPart then
		CheckTorsoReference()
		if not TorsoPart then
			-- Bail out, since we're relying on Torso existing, should never happen since we fall back to using HumanoidRootPart as torso
			return
		end
	end

	-- Make a list of world points to raycast to
	local castPoints = {}
	BehaviorFunction(castPoints)
	
	-- Cast to get a list of objects between the camera and the cast points
	local currentHits = {}
	local ignoreList = {Character}
	local function add(hit)
		currentHits[hit] = true
		if not SavedHits[hit] then
			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 = HeadPart and HeadPart.CFrame.p or castPoints[1]
		local torsoPoint = TorsoPart and TorsoPart.CFrame.p or castPoints[2]
		hitParts = 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 = 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(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
			SavedHits[hitPart] = nil
		end
	end
end

function Invisicam:SetMode(newMode)
	AssertTypes(newMode, 'number')
	for modeName, modeNum in pairs(MODE) do
		if modeNum == newMode then
			Mode = newMode
			BehaviorFunction = Behaviors[Mode]
			return
		end
	end
	error("Invalid mode number")
end

function Invisicam:SetCustomBehavior(func)
	AssertTypes(func, 'function')
	Behaviors[MODE.CUSTOM] = func
	if Mode == MODE.CUSTOM then
		BehaviorFunction = func
	end
end

function Invisicam:GetObscuredParts()
	return SavedHits
end
