local v=next local N=RunService:IsServer()local u="\065\068\068\069\068\095\079\066\074\069\067\084\095\067\072\065\078\071\069\095\084\089\080\069"local j="\082\069\077\079\086\069\068\095\079\066\074\069\067\084\095\067\072\065\078\071\069\095\084\089\080\069"local a=false local l={}l.__index=l function l.new(v,N,u)local j={}j.contents={}j.grid=v j.masterContentsManager=u j.connections={}j.contentsListenerCount=0 j.contentsListeners={}setmetatable(j,l)if type(N)=="\115\116\114\105\110\103"then j.folder=Instance.new("\070\111\108\100\101\114")j.folder.Name=N j.folder.Parent=game.Workspace else j.folder=N for v,N in ipairs(j.folder:GetChildren())do j:_onChildAddedToContentsFolder(N)end j.connections.objectAddedToContentsFolder=j.folder.ChildAdded:Connect(function(v)j:_onChildAddedToContentsFolder(v)end)end if u then u:addContentsListener(function(v)j:_onMasterContentsChanged(v)end)end return j end local function J(v)local N={}for v in v:gmatch("\091\094\044\093\043")do table.insert(N,tonumber(v))end return Vector3.new(N[1],N[2],N[3])end function l._onChildAddedToContentsFolder(N,v)local u=v.Name local j=string.sub(u,1,string.find(u,"\124")-1)local a=N.grid.objectTypes[j]if a then local j,l,q,h=a.fromString(u)N:add(a,(N:_allocateCell(J(h))).id,v,true)else warn("\067\104\105\108\100 \119\105\116\104 \105\109\112\114\111\112\101\114 \110\097\109\101 "..(v.Name.." \097\100\100\101\100 \116\111 \099\111\110\116\101\110\116\115 \102\111\108\100\101\114"))end end function l.addContentsListener(N,v)if not N.contentsListeners[v]then N.contentsListeners[v]=true N.contentsListenerCount=N.contentsListenerCount+1 end end function l.removeContentsListener(N,v)if N.contentsListener[v]then N.contentsListener[v]=nil N.contentsListenerCount=N.contentsListenerCount-1 end end function l._onMasterContentsChanged(N,v)if v.type==u then N:remove(v.objectType,tostring(v.cellId))elseif v.type==j then N:remove(v.objectType,tostring(v.cellId),true,v.doNotDestroy)else error("\082\101\099\101\105\118\101\100 \109\097\115\116\101\114 \099\111\110\116\101\110\116\115 \099\104\097\110\103\101 \119\105\116\104 \105\110\118\097\108\105\100 \116\121\112\101")end end function l.canPlace(u,v,N)return u:_spaceAvailable(v,N)and u:_connected(v,N)end function l._spaceAvailable(u,v,N)local j=true if u.masterContentsManager then j=u.masterContentsManager:_spaceAvailable(v,N)end local a=not bit32.btest(v.occupancy,(u:getCell(N)).occupancy)return j and a end function l.getCell(N,v)local u=N.contents[tostring(v)]if u then return u end return Cell.new(N.grid,v)end function l._connected(u,v,N)local j=u:getCell(N)if u.masterContentsManager and u.masterContentsManager:_connected(v,N)then return true end local a=bit32.band(v.connectivity,j.connectivity)if BitOps.numOfSetBits(a)>=2 then local N={}for u=0,23,1 do local a=tostring(u)if bit32.extract(v.connectivity,u)==1 and j.connectivityMap[a]~=nil then for v in pairs(j.connectivityMap[a])do if N[v]then return true else N[v]=true end end end end end local l=u:_getTouchingScenery(N,v)local J=0 for v in pairs(l)do J=J+1 end if J>0 then return true end return false end
-- Save next to a local variable for better performance
local next = next

local isServer = RunService:IsServer()

local ADDED_OBJECT = "ADDED_OBJECT_CHANGE_TYPE"
local REMOVED_OBJECT = "REMOVED_OBJECT_CHANGE_TYPE"

local DEBUG = false

local ContentsManager = {}
ContentsManager.__index = ContentsManager

function ContentsManager.new(grid, folder, masterContentsManager)
	local newContentsManager = {}
	newContentsManager.contents = {}
	newContentsManager.grid = grid
	newContentsManager.masterContentsManager = masterContentsManager
	newContentsManager.connections = {}
	newContentsManager.contentsListenerCount = 0
	newContentsManager.contentsListeners = {}
	setmetatable(newContentsManager, ContentsManager)

	if type(folder) == "string" then
		newContentsManager.folder = Instance.new("Folder")
		newContentsManager.folder.Name = folder
		newContentsManager.folder.Parent = game.Workspace
	else
		newContentsManager.folder = folder

		for _, child in ipairs(newContentsManager.folder:GetChildren()) do
			newContentsManager:_onChildAddedToContentsFolder(child)
		end
		newContentsManager.connections.objectAddedToContentsFolder = newContentsManager.folder.ChildAdded:Connect(
			function(childAdded)
				newContentsManager:_onChildAddedToContentsFolder(childAdded)
			end
		)
	end

	if masterContentsManager then
		masterContentsManager:addContentsListener(function(change)
			newContentsManager:_onMasterContentsChanged(change)
		end)
	end

	return newContentsManager
end

local function Vector3FromString(str)
	local vector3Values = {}
	for num in str:gmatch("[^,]+") do
		table.insert(vector3Values, tonumber(num))
	end

	return Vector3.new(vector3Values[1], vector3Values[2], vector3Values[3])
end

function ContentsManager:_onChildAddedToContentsFolder(child)
	local childName = child.Name
	local objectTypeName = string.sub(childName, 1, string.find(childName, "|") - 1)
	local objectType = self.grid.objectTypes[objectTypeName]
	if objectType then
		local _, _, _, cellName = objectType.fromString(childName)

		self:add(objectType, self:_allocateCell(Vector3FromString(cellName)).id, child, true)
	else
		warn("Child with improper name " .. child.Name .. " added to contents folder")
	end
end

function ContentsManager:addContentsListener(func)
	if not self.contentsListeners[func] then
		self.contentsListeners[func] = true
		self.contentsListenerCount = self.contentsListenerCount + 1
	end
end

function ContentsManager:removeContentsListener(func)
	if self.contentsListener[func] then
		self.contentsListener[func] = nil
		self.contentsListenerCount = self.contentsListenerCount - 1
	end
end

function ContentsManager:_onMasterContentsChanged(change)
	-- TODO: Remove conflicting temp objects and
	-- temp objects that are no longer connected to the rest of the structure

	if change.type == ADDED_OBJECT then
		self:remove(change.objectType, tostring(change.cellId))
	elseif change.type == REMOVED_OBJECT then
		self:remove(change.objectType, tostring(change.cellId), true, change.doNotDestroy)
	else
		error("Received master contents change with invalid type")
	end
end

function ContentsManager:canPlace(objectType, cellId)
	return self:_spaceAvailable(objectType, cellId) and self:_connected(objectType, cellId)
end

function ContentsManager:_spaceAvailable(objectType, cellId)
	local masterSpaceAvailable = true
	if self.masterContentsManager then
		masterSpaceAvailable = self.masterContentsManager:_spaceAvailable(objectType, cellId)
	end

	local spaceAvailable = not bit32.btest(objectType.occupancy, self:getCell(cellId).occupancy)

	return masterSpaceAvailable and spaceAvailable
end

function ContentsManager:getCell(cellId)
	local cell = self.contents[tostring(cellId)]
	if cell then
		return cell
	end

	return Cell.new(self.grid, cellId)
end

function ContentsManager:_connected(objectType, cellId)
	local cell = self:getCell(cellId)

	if self.masterContentsManager and self.masterContentsManager:_connected(objectType, cellId) then
		return true
	end

	local connections = bit32.band(objectType.connectivity, cell.connectivity)
	if BitOps.numOfSetBits(connections) >= 2 then
		-- Check that at least two connections are to the same adjacent grid object
		local setOfConnectedObjects = {}
		for i = 0, 23 do
			local key = tostring(i)
			if bit32.extract(objectType.connectivity, i) == 1 and cell.connectivityMap[key] ~= nil then
				for object in pairs(cell.connectivityMap[key]) do
					if setOfConnectedObjects[object] then
						return true
					else
						setOfConnectedObjects[object] = true
					end
				end
			end
		end
	end

	local touchingScenery = self:_getTouchingScenery(cellId, objectType)
	local touchingScenerySize = 0
	for _ in pairs(touchingScenery) do
		touchingScenerySize = touchingScenerySize + 1
	end

	if touchingScenerySize > 0 then
		return true
	end

	return false
end
