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