function Zone.new(U)local H={}setmetatable(H,Zone)local X="\084\104\101 \122\111\110\101 \099\111\110\116\097\105\110\101\114 \109\117\115\116 \098\101 \097 \109\111\100\101\108\044 \102\111\108\100\101\114\044 \098\097\115\101\112\097\114\116 \111\114 \116\097\098\108\101\033"local r=typeof(U)if not(r=="\116\097\098\108\101"or r=="\073\110\115\116\097\110\099\101")then error(X)end H.accuracy=enum.Accuracy.High H.autoUpdate=true H.respectUpdateQueue=true local m=Janitor.new()H.janitor=m H._updateConnections=m:add(Janitor.new(),"\100\101\115\116\114\111\121")H.container=U H.zoneParts={}H.overlapParams={}H.region=nil H.volume=nil H.boundMin=nil H.boundMax=nil H.recommendedMaxParts=nil H.zoneId=httpService:GenerateGUID()H.activeTriggers={}H.occupants={}H.trackingTouchedTriggers={}H.enterDetection=enum.Detection.Centre H.exitDetection=enum.Detection.Centre H._currentEnterDetection=nil H._currentExitDetection=nil H.totalPartVolume=0 H.allZonePartsAreBlocks=true H.trackedItems={}H.settingsGroupName=nil H.worldModel=workspace H.onItemDetails={}H.itemsToUntrack={}ZoneController.updateDetection(H)H.updated=m:add(Signal.new(),"\100\101\115\116\114\111\121")local i={"\112\108\097\121\101\114";"\112\097\114\116","\108\111\099\097\108\080\108\097\121\101\114";"\105\116\101\109"}local D={"\101\110\116\101\114\101\100";"\101\120\105\116\101\100"}for U,X in pairs(i)do local r=0 local i=0 for U,D in pairs(D)do local Q=m:add(Signal.new(true),"\100\101\115\116\114\111\121")local f=(D:sub(1,1)):upper()..D:sub(2)local y=X..f H[y]=Q Q.connectionsChanged:Connect(function(U)if X=="\108\111\099\097\108\080\108\097\121\101\114"and(not localPlayer and U==1)then error(("\067\097\110 \111\110\108\121 \099\111\110\110\101\099\116 \116\111 \'\108\111\099\097\108\080\108\097\121\101\114\037\115\' \111\110 \116\104\101 \099\108\105\101\110\116\033"):format(f))end i=r r+=U if i==0 and r>0 then ZoneController._registerConnection(H,X,f)elseif i>0 and r==0 then ZoneController._deregisterConnection(H,X)end end)end end Zone.touchedConnectionActions={}for U,X in pairs(i)do local r=("\095\037\115\084\111\117\099\104\101\100\090\111\110\101"):format(X)local m=H[r]if m then H.trackingTouchedTriggers[X]={}Zone.touchedConnectionActions[X]=function(U)m(H,U)end end end H:_update()ZoneController._registerZone(H)m:add(function()ZoneController._deregisterZone(H)end,true)return H end function Zone.fromRegion(U,H)local X=2024 local r=Instance.new("\077\111\100\101\108")local function m(U,H)if H.X>X or H.Y>X or H.Z>X then local X=H*.25 local r=H*.5 m(U*CFrame.new(-X.X,-X.Y,-X.Z),r)m(U*CFrame.new(-X.X,-X.Y,X.Z),r)m(U*CFrame.new(-X.X,X.Y,-X.Z),r)m(U*CFrame.new(-X.X,X.Y,X.Z),r)m(U*CFrame.new(X.X,-X.Y,-X.Z),r)m(U*CFrame.new(X.X,-X.Y,X.Z),r)m(U*CFrame.new(X.X,X.Y,-X.Z),r)m(U*CFrame.new(X.X,X.Y,X.Z),r)else local X=Instance.new("\080\097\114\116")X.CFrame=U X.Size=H X.Anchored=true X.Parent=r end end m(U,H)local i=Zone.new(r)i:relocate()return i end

function Zone.new(container)
	local self = {}
	setmetatable(self, Zone)
	
	-- Validate container
	local INVALID_TYPE_WARNING = "The zone container must be a model, folder, basepart or table!"
	local containerType = typeof(container)
	if not(containerType == "table" or containerType == "Instance") then
		error(INVALID_TYPE_WARNING)
	end

	-- Configurable
	self.accuracy = enum.Accuracy.High
	self.autoUpdate = true
	self.respectUpdateQueue = true
	--self.maxPartsAddition = 20
	--self.ignoreRecommendedMaxParts = false

	-- Variable
	local janitor = Janitor.new()
	self.janitor = janitor
	self._updateConnections = janitor:add(Janitor.new(), "destroy")
	self.container = container
	self.zoneParts = {}
	self.overlapParams = {}
	self.region = nil
	self.volume = nil
	self.boundMin = nil
	self.boundMax = nil
	self.recommendedMaxParts = nil
	self.zoneId = httpService:GenerateGUID()
	self.activeTriggers = {}
	self.occupants = {}
	self.trackingTouchedTriggers = {}
	self.enterDetection = enum.Detection.Centre
	self.exitDetection = enum.Detection.Centre
	self._currentEnterDetection = nil -- This will update automatically internally
	self._currentExitDetection = nil -- This will also update automatically internally
	self.totalPartVolume = 0
	self.allZonePartsAreBlocks = true
	self.trackedItems = {}
	self.settingsGroupName = nil
	self.worldModel = workspace
	self.onItemDetails = {}
	self.itemsToUntrack = {}

	-- This updates _currentEnterDetection and _currentExitDetection right away to prevent nil comparisons
	ZoneController.updateDetection(self)

	-- Signals
	self.updated = janitor:add(Signal.new(), "destroy")
	local triggerTypes = {
		"player",
		"part",
		"localPlayer",
		"item"
	}
	local triggerEvents = {
		"entered",
		"exited",
	}
	for _, triggerType in pairs(triggerTypes) do
		local activeConnections = 0
		local previousActiveConnections = 0
		for i, triggerEvent in pairs(triggerEvents) do
			-- this enables us to determine when a developer connects to an event
			-- so that we can act accoridngly (i.e. begin or end a checker loop)
			local signal = janitor:add(Signal.new(true), "destroy")
			local triggerEventUpper = triggerEvent:sub(1,1):upper()..triggerEvent:sub(2)
			local signalName = triggerType..triggerEventUpper
			self[signalName] = signal
			signal.connectionsChanged:Connect(function(increment)
				if triggerType == "localPlayer" and not localPlayer and increment == 1 then
					error(("Can only connect to 'localPlayer%s' on the client!"):format(triggerEventUpper))
				end
				previousActiveConnections = activeConnections
				activeConnections += increment
				if previousActiveConnections == 0 and activeConnections > 0 then
					-- At least 1 connection active, begin loop
					ZoneController._registerConnection(self, triggerType, triggerEventUpper)
				elseif previousActiveConnections > 0 and activeConnections == 0 then
					-- All connections have disconnected, end loop
					ZoneController._deregisterConnection(self, triggerType)
				end
			end)
		end
	end

	-- Setup touched receiver functions where applicable
	Zone.touchedConnectionActions = {}
	for _, triggerType in pairs(triggerTypes) do
		local methodName = ("_%sTouchedZone"):format(triggerType)
		local correspondingMethod = self[methodName]
		if correspondingMethod then
			self.trackingTouchedTriggers[triggerType] = {}
			Zone.touchedConnectionActions[triggerType] = function(touchedItem)
				correspondingMethod(self, touchedItem)
			end
		end
	end

	-- This constructs the zones boundaries, region, etc
	self:_update()

	-- Register/deregister zone
	ZoneController._registerZone(self)
	janitor:add(function()
		ZoneController._deregisterZone(self)
	end, true)
	
	return self
end

function Zone.fromRegion(cframe, size)
	local MAX_PART_SIZE = 2024
	local container = Instance.new("Model")
	local function createCube(cubeCFrame, cubeSize)
		if cubeSize.X > MAX_PART_SIZE or cubeSize.Y > MAX_PART_SIZE or cubeSize.Z > MAX_PART_SIZE then
			local quarterSize = cubeSize * 0.25
			local halfSize = cubeSize * 0.5
			createCube(cubeCFrame * CFrame.new(-quarterSize.X, -quarterSize.Y, -quarterSize.Z), halfSize)
			createCube(cubeCFrame * CFrame.new(-quarterSize.X, -quarterSize.Y, quarterSize.Z), halfSize)
			createCube(cubeCFrame * CFrame.new(-quarterSize.X, quarterSize.Y, -quarterSize.Z), halfSize)
			createCube(cubeCFrame * CFrame.new(-quarterSize.X, quarterSize.Y, quarterSize.Z), halfSize)
			createCube(cubeCFrame * CFrame.new(quarterSize.X, -quarterSize.Y, -quarterSize.Z), halfSize)
			createCube(cubeCFrame * CFrame.new(quarterSize.X, -quarterSize.Y, quarterSize.Z), halfSize)
			createCube(cubeCFrame * CFrame.new(quarterSize.X, quarterSize.Y, -quarterSize.Z), halfSize)
			createCube(cubeCFrame * CFrame.new(quarterSize.X, quarterSize.Y, quarterSize.Z), halfSize)
		else
			local part = Instance.new("Part")
			part.CFrame = cubeCFrame
			part.Size = cubeSize
			part.Anchored = true
			part.Parent = container
		end
	end
	createCube(cframe, size)
	local zone = Zone.new(container)
	zone:relocate()
	return zone
end


