local a=20 local D=false local r={}r.__index=r local s={}s.__index=s s.ClassName="\083\105\103\110\097\108"function s.new()local a=setmetatable({},s)a._bindableEvent=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")a._argData=nil a._argCount=nil return a end function s.Fire(a,...)a._argData={...}a._argCount=select("\035",...)a._bindableEvent:Fire()a._argData=nil a._argCount=nil end function s.Connect(D,a)if not(type(a)=="\102\117\110\099\116\105\111\110")then error(("\099\111\110\110\101\099\116\040\037\115\041"):format(typeof(a)),2)end return D._bindableEvent.Event:Connect(function()a(unpack(D._argData,1,D._argCount))end)end function s.Wait(a)a._bindableEvent.Event:Wait()assert(a._argData,"\077\105\115\115\105\110\103 \097\114\103 \100\097\116\097\044 \108\105\107\101\108\121 \100\117\101 \116\111 \058\084\119\101\101\110\083\105\122\101\047\080\111\115\105\116\105\111\110 \099\111\114\114\117\112\116\105\110\103 \116\104\114\101\097\100\114\101\102\115\046")return unpack(a._argData,1,a._argCount)end function s.Disconnect(a)if a._bindableEvent then a._bindableEvent:Destroy()a._bindableEvent=nil end a._argData=nil a._argCount=nil end function r.new(Y,N)local o={}setmetatable(o,r)o.Group=Y o.RegionHeight=N or a o.DisplayBoundParts=D o.GroupParts={}o.Region=o:getRegion()o.PreviousPlayers={}o.PlayerAdded=s.new()o.PlayerRemoving=s.new()return o end function r.getRegion(a)local D={Min={},Max={}}for a,D in pairs(D)do D.Values={}function D.parseCheck(D,r)if a=="\077\105\110"then return D<=r elseif a=="\077\097\120"then return D>=r end end function D.parse(D,a)for a,r in pairs(a)do local s=D.Values[a]or r if D.parseCheck(r,s)then D.Values[a]=r end end end end for r,s in pairs(a.Group:GetDescendants())do if s:isA("\066\097\115\101\080\097\114\116")then table.insert(a.GroupParts,s)local r=s.Size*.5 local Y={s.CFrame*CFrame.new(-r.X,-r.Y,-r.Z),s.CFrame*CFrame.new(-r.X,-r.Y,r.Z);s.CFrame*CFrame.new(-r.X,r.Y,-r.Z);s.CFrame*CFrame.new(-r.X,r.Y,r.Z),s.CFrame*CFrame.new(r.X,-r.Y,-r.Z);s.CFrame*CFrame.new(r.X,-r.Y,r.Z),s.CFrame*CFrame.new(r.X,r.Y,-r.Z),s.CFrame*CFrame.new(r.X,r.Y,r.Z)}for a,r in pairs(Y)do local s,Y,N=r:GetComponents()local o={s;Y,N}D.Min:parse(o)D.Max:parse(o)end end end local r=Vector3.new(unpack(D.Min.Values))local s=Vector3.new(unpack(D.Max.Values))+Vector3.new(0,a.RegionHeight,0)if a.DisplayBoundParts then local D={BoundMin=r,BoundMax=s}for D,r in pairs(D)do local s=Instance.new("\080\097\114\116")s.Anchored=true s.CanCollide=false s.Transparency=.5 s.Size=Vector3.new(4,4,4)s.Color=Color3.fromRGB(255,0,0)s.CFrame=CFrame.new(r)s.Name=D s.Parent=a.Group end end local Y=Region3.new(r,s)return Y end function r.getPlayersInRegion(a)local D=Players:GetPlayers()local r={}for a,D in pairs(D)do local s=D.Character local Y=s and s:FindFirstChild("\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116")if Y then table.insert(r,Y)end end local s=workspace:FindPartsInRegion3WithWhiteList(a.Region,r,#D)local Y={}local N={}if#s>0 then for a,D in pairs(s)do local r=D.Parent if not Y[r]then Y[r]=true local a=Players:GetPlayerFromCharacter(r)if a then N[a]=true end end end end return N end function r.getPlayer(D,a)local r=a.Character local s=r and r:FindFirstChild("\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116")if s then local a=s.Position+Vector3.new(0,4,0)local r=a+Vector3.new(0,-1,0)local Y=Ray.new(a,(r-a).unit*D.RegionHeight)local N=workspace:FindPartOnRayWithWhitelist(Y,D.GroupParts)if N then return true end end end function r.getPlayers(a)local D=a:getPlayersInRegion()local r={}for D,s in pairs(D)do if a:getPlayer(s)then if not a.PreviousPlayers[s]then a.PlayerAdded:Fire(s)end table.insert(r,s)elseif a.PreviousPlayers[s]then a.PlayerRemoving:Fire(s)end end a.PreviousPlayers=r return r end function r.initLoop(D,a)a=tonumber(a)or.5 local r=HttpService:GenerateGUID(false)D.currentLoop=r if not D.loopInitialized then D.loopInitialized=true;(coroutine.wrap(function()while D.currentLoop==r do wait(a)D:getPlayers()end end))()end end function r.endLoop(a)a.currentLoop=nil end return r

local defaultRegionHeight = 20
local displayBoundParts = false

local Zone = {}
Zone.__index = Zone

local Signal = {}
Signal.__index = Signal
Signal.ClassName = "Signal"

function Signal.new()
	local self = setmetatable({}, Signal)

	self._bindableEvent = Instance.new("BindableEvent")
	self._argData = nil
	self._argCount = nil -- Prevent edge case of :Fire("A", nil) --> "A" instead of "A", nil

	return self
end

function Signal:Fire(...)
	self._argData = {...}
	self._argCount = select("#", ...)
	self._bindableEvent:Fire()
	self._argData = nil
	self._argCount = nil
end

function Signal:Connect(handler)
	if not (type(handler) == "function") then
		error(("connect(%s)"):format(typeof(handler)), 2)
	end

	return self._bindableEvent.Event:Connect(function()
		handler(unpack(self._argData, 1, self._argCount))
	end)
end

function Signal:Wait()
	self._bindableEvent.Event:Wait()
	assert(self._argData, "Missing arg data, likely due to :TweenSize/Position corrupting threadrefs.")
	return unpack(self._argData, 1, self._argCount)
end

function Signal:Disconnect()
	if self._bindableEvent then
		self._bindableEvent:Destroy()
		self._bindableEvent = nil
	end

	self._argData = nil
	self._argCount = nil
end

function Zone.new(group, regionHeight)
	local self = {}
	setmetatable(self, Zone)

	self.Group = group
	self.RegionHeight = regionHeight or defaultRegionHeight
	self.DisplayBoundParts = displayBoundParts
	self.GroupParts = {}
	self.Region = self:getRegion()
	self.PreviousPlayers = {}
	self.PlayerAdded = Signal.new()
	self.PlayerRemoving = Signal.new()

	return self
end

function Zone:getRegion()
	local bounds = {["Min"] = {}, ["Max"] = {}}
	for boundType, details in pairs(bounds) do
		details.Values = {}
		function details.parseCheck(v, currentValue)
			if boundType == "Min" then
				return (v <= currentValue)
			elseif boundType == "Max" then
				return (v >= currentValue)
			end
		end
		function details:parse(valuesToParse)
			for i,v in pairs(valuesToParse) do
				local currentValue = self.Values[i] or v
				if self.parseCheck(v, currentValue) then
					self.Values[i] = v
				end
			end
		end
	end
	for _, part in pairs(self.Group:GetDescendants()) do
		if part:isA("BasePart") then
			table.insert(self.GroupParts, part)
			local sizeHalf = part.Size * 0.5
			local corners = {
				part.CFrame * CFrame.new(-sizeHalf.X, -sizeHalf.Y, -sizeHalf.Z),
				part.CFrame * CFrame.new(-sizeHalf.X, -sizeHalf.Y, sizeHalf.Z),
				part.CFrame * CFrame.new(-sizeHalf.X, sizeHalf.Y, -sizeHalf.Z),
				part.CFrame * CFrame.new(-sizeHalf.X, sizeHalf.Y, sizeHalf.Z),
				part.CFrame * CFrame.new(sizeHalf.X, -sizeHalf.Y, -sizeHalf.Z),
				part.CFrame * CFrame.new(sizeHalf.X, -sizeHalf.Y, sizeHalf.Z),
				part.CFrame * CFrame.new(sizeHalf.X, sizeHalf.Y, -sizeHalf.Z),
				part.CFrame * CFrame.new(sizeHalf.X, sizeHalf.Y, sizeHalf.Z),
			}
			for _, cornerCFrame in pairs(corners) do
				local x, y, z = cornerCFrame:GetComponents()
				local values = {x, y, z}
				bounds.Min:parse(values)
				bounds.Max:parse(values)
			end
		end
	end
	local boundMin = Vector3.new(unpack(bounds.Min.Values))
	local boundMax = Vector3.new(unpack(bounds.Max.Values)) + Vector3.new(0, self.RegionHeight, 0)
	if self.DisplayBoundParts then
		local boundParts = {BoundMin = boundMin, BoundMax = boundMax}
		for boundName, boundCFrame in pairs(boundParts) do
			local part = Instance.new("Part")
			part.Anchored = true
			part.CanCollide = false
			part.Transparency = 0.5
			part.Size = Vector3.new(4,4,4)
			part.Color = Color3.fromRGB(255,0,0)
			part.CFrame = CFrame.new(boundCFrame)
			part.Name = boundName
			part.Parent = self.Group
		end
	end
	local region = Region3.new(boundMin, boundMax)
	return region
end

function Zone:getPlayersInRegion()
	local players = Players:GetPlayers()
	local playerCharacters = {}
	for _, player in pairs(players) do
		local char = player.Character
		local hrp = char and char:FindFirstChild("HumanoidRootPart")
		if hrp then
			table.insert(playerCharacters, hrp)
		end
	end
	local partsInRegion = workspace:FindPartsInRegion3WithWhiteList(self.Region, playerCharacters, #players)
	local charsChecked = {}
	local playersInRegion = {}
	if #partsInRegion > 0 then
		for _, part in pairs(partsInRegion) do
			local char = part.Parent
			if not charsChecked[char] then
				charsChecked[char] = true
				local player = Players:GetPlayerFromCharacter(char)
				if player then
					--table.insert(playersInRegion, player)
					playersInRegion[player] = true
				end
			end
		end
	end
	return playersInRegion
end

function Zone:getPlayer(player)
	local char = player.Character
	local hrp = char and char:FindFirstChild("HumanoidRootPart")
	if hrp then
		local origin = hrp.Position + Vector3.new(0, 4, 0)
		local lookDirection = origin + Vector3.new(0, -1, 0)
		local ray = Ray.new(origin, (lookDirection - origin).unit * (self.RegionHeight))
		local groupPart = workspace:FindPartOnRayWithWhitelist(ray, self.GroupParts)
		if groupPart then
			return true
		end
	end
end

function Zone:getPlayers()
	local playersInRegion = self:getPlayersInRegion()
	local playersInZone = {}
	for _, player in pairs(playersInRegion) do
		if self:getPlayer(player) then
			if not self.PreviousPlayers[player] then
				self.PlayerAdded:Fire(player)
			end
			table.insert(playersInZone, player)
		elseif self.PreviousPlayers[player] then
			self.PlayerRemoving:Fire(player)
		end
	end
	self.PreviousPlayers = playersInZone
	return playersInZone
end

function Zone:initLoop(loopDelay)
	loopDelay = tonumber(loopDelay) or 0.5
	local loopId = HttpService:GenerateGUID(false)
	self.currentLoop = loopId
	if not self.loopInitialized then
		self.loopInitialized = true
		coroutine.wrap(function()
			while self.currentLoop == loopId do
				wait(loopDelay)
				self:getPlayers()
			end
		end)()
	end
end

function Zone:endLoop()
	self.currentLoop = nil
end
			
return Zone
