function getLimbs(L,w)local s={}local F={}local m={}local function H(L,g)if L.Name~="\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116"then local w=getLimbType(L.Name)s[w]=s[w]or{}table.insert(s[w],L)if w~=g then m[w]=m[w]or{}if g then m[w][g]=true end table.insert(F,{Part=L;Type=w})g=w end end for s,F in pairs(L:GetChildren())do if F:isA("\065\116\116\097\099\104\109\101\110\116")and w[F.Name]then local s=w[F.Name].Attachment1.Parent if s and s~=L then H(s,g)end end end end H(L)return s,F,m end function createNoCollision(L,w)local s=Instance.new("\078\111\067\111\108\108\105\115\105\111\110\067\111\110\115\116\114\097\105\110\116")s.Name=L.Name..("\060-\062"..w.Name)s.Part0=L s.Part1=w return s end return function(L,w)local s=Instance.new("\070\111\108\100\101\114")s.Name="\078\111\067\111\108\108\105\115\105\111\110\067\111\110\115\116\114\097\105\110\116\115"local F,m,H=getLimbs(w,L)for L=1,#m,1 do for w=L+1,#m,1 do local F,g=m[L].Type,m[w].Type if not(H[F][g]or H[g][F])then(createNoCollision(m[L].Part,m[w].Part)).Parent=s end end end for L,w in pairs(F)do for L,m in pairs(H[L])do for L,F in pairs(F[L])do for L,w in pairs(w)do(createNoCollision(w,F)).Parent=s end end end end return s end

function getLimbs(characterRoot, attachmentMap)
	local limbs = {}
	local limbRootParts = {}
	local limbParents = {}

	local function parsePart(part, lastLimb)
		if part.Name ~= "HumanoidRootPart" then
			local limbType = getLimbType(part.Name)
			limbs[limbType] = limbs[limbType] or {}
			
			table.insert(limbs[limbType], part)
			
			--//local limbData = limbs[limbType]
			if limbType ~= lastLimb then
				limbParents[limbType] = limbParents[limbType] or {}
				if lastLimb then
					limbParents[limbType][lastLimb] = true
				end
				
				table.insert(limbRootParts, {Part=part, Type=limbType})
				lastLimb = limbType
			end
		end
		
		for _,v in pairs(part:GetChildren()) do
			if v:isA("Attachment") and attachmentMap[v.Name] then
				local part1 = attachmentMap[v.Name].Attachment1.Parent
				if part1 and part1 ~= part then
					parsePart(part1, lastLimb)
				end
			end
		end
	end
	parsePart(characterRoot)
	
	return limbs, limbRootParts, limbParents
end

function createNoCollision(part0, part1)
	local noCollision = Instance.new("NoCollisionConstraint")
	noCollision.Name = part0.Name.."<->"..part1.Name
	noCollision.Part0 = part0
	noCollision.Part1 = part1
	
	return noCollision
end

return function(attachmentMap, characterRoot)
	local noCollisionConstraints = Instance.new("Folder")
	noCollisionConstraints.Name = "NoCollisionConstraints"
	
	local limbs, limbRootParts, limbParents = getLimbs(characterRoot, attachmentMap)
	
	--[[
		Disable collisions between all limb roots (e.g. left/right shoulders, head, etc) unless
		one of them is a parent of the other (handled in next step). This is to ensure limbs
		maintain free range of motion and we don't have issues like
			- Large shoulders clipping with the head and all of them being unable to move
			- Legs being stuck together because rotating would cause the collision boxes to intersect
	--]]
	for i=1, #limbRootParts do
		for j=i+1, #limbRootParts do
			local limbType0, limbType1 = limbRootParts[i].Type, limbRootParts[j].Type

			if not (limbParents[limbType0][limbType1] or limbParents[limbType1][limbType0]) then
				createNoCollision(limbRootParts[i].Part, limbRootParts[j].Part).Parent = noCollisionConstraints
			end
		end
	end

	--[[
		Disable collisions between limbs and their parent limbs. This is mostly to address
		bundle torsos having insane hitboxes that touch more than just limb roots
	--]]
	for limbType, parts in pairs(limbs) do
		for parentLimbType,_ in pairs(limbParents[limbType]) do
			for _,part2 in pairs(limbs[parentLimbType]) do
				for _,part in pairs(parts) do
					createNoCollision(part, part2).Parent = noCollisionConstraints
				end
			end
		end
	end
	
	return noCollisionConstraints
end
