local Y=next local Q=RunService:IsServer()local J="\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 p="\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 H=false local V={}V.__index=V function V.new(Y,Q,J)local p={}p.contents={}p.grid=Y p.masterContentsManager=J p.connections={}p.contentsListenerCount=0 p.contentsListeners={}setmetatable(p,V)if type(Q)=="\115\116\114\105\110\103"then p.folder=Instance.new("\070\111\108\100\101\114")p.folder.Name=Q p.folder.Parent=game.Workspace else p.folder=Q for Y,Q in ipairs(p.folder:GetChildren())do p:_onChildAddedToContentsFolder(Q)end p.connections.objectAddedToContentsFolder=p.folder.ChildAdded:Connect(function(Y)p:_onChildAddedToContentsFolder(Y)end)end if J then J:addContentsListener(function(Y)p:_onMasterContentsChanged(Y)end)end return p end local function n(Y)local Q={}for Y in Y:gmatch("\091\094\044\093\043")do table.insert(Q,tonumber(Y))end return Vector3.new(Q[1],Q[2],Q[3])end function V._onChildAddedToContentsFolder(Q,Y)local J=Y.Name local p=string.sub(J,1,string.find(J,"\124")-1)local H=Q.grid.objectTypes[p]if H then local p,V,w,W=H.fromString(J)Q:add(H,(Q:_allocateCell(n(W))).id,Y,true)else warn("\067\104\105\108\100 \119\105\116\104 \105\109\112\114\111\112\101\114 \110\097\109\101 "..(Y.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 V.addContentsListener(Q,Y)if not Q.contentsListeners[Y]then Q.contentsListeners[Y]=true Q.contentsListenerCount=Q.contentsListenerCount+1 end end function V.removeContentsListener(Q,Y)if Q.contentsListener[Y]then Q.contentsListener[Y]=nil Q.contentsListenerCount=Q.contentsListenerCount-1 end end function V._onMasterContentsChanged(Q,Y)if Y.type==J then Q:remove(Y.objectType,tostring(Y.cellId))elseif Y.type==p then Q:remove(Y.objectType,tostring(Y.cellId),true,Y.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 V.canPlace(J,Y,Q)return J:_spaceAvailable(Y,Q)and J:_connected(Y,Q)end function V._spaceAvailable(J,Y,Q)local p=true if J.masterContentsManager then p=J.masterContentsManager:_spaceAvailable(Y,Q)end local H=not bit32.btest(Y.occupancy,(J:getCell(Q)).occupancy)return p and H end function V.getCell(Q,Y)local J=Q.contents[tostring(Y)]if J then return J end return Cell.new(Q.grid,Y)end function V._connected(J,Y,Q)local p=J:getCell(Q)if J.masterContentsManager and J.masterContentsManager:_connected(Y,Q)then return true end local H=bit32.band(Y.connectivity,p.connectivity)if BitOps.numOfSetBits(H)>=2 then local Q={}for J=0,23,1 do local H=tostring(J)if bit32.extract(Y.connectivity,J)==1 and p.connectivityMap[H]~=nil then for Y in pairs(p.connectivityMap[H])do if Q[Y]then return true else Q[Y]=true end end end end end local V=J:_getTouchingScenery(Q,Y)local n=0 for Y in pairs(V)do n=n+1 end if n>0 then return true end return false end

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
