diff options
| -rw-r--r-- | api.lua | 119 | ||||
| -rw-r--r-- | internal.lua | 61 | 
2 files changed, 139 insertions, 41 deletions
@@ -1,17 +1,54 @@ --- Returns a list of areas that include the provided position +--- Returns a list of areas that include the provided position.  function areas:getAreasAtPos(pos) -	local a = {} -	local px, py, pz = pos.x, pos.y, pos.z -	for id, area in pairs(self.areas) do -		local ap1, ap2 = area.pos1, area.pos2 -		if px >= ap1.x and px <= ap2.x and -		   py >= ap1.y and py <= ap2.y and -		   pz >= ap1.z and pz <= ap2.z then -			a[id] = area +	local res = {} +	if self.store then +		local a = self.store:get_areas_for_pos(pos, false, true) +		for store_id, store_area in pairs(a) do +			local id = tonumber(store_area.data) +			res[id] = self.areas[id] +		end +	else +		local px, py, pz = pos.x, pos.y, pos.z +		for id, area in pairs(self.areas) do +			local ap1, ap2 = area.pos1, area.pos2 +			if +					(px >= ap1.x and px <= ap2.x) and +					(py >= ap1.y and py <= ap2.y) and +					(pz >= ap1.z and pz <= ap2.z) then +				res[id] = area +			end  		end  	end -	return a +	return res +end + +--- Returns areas that intersect with the passed area. +function areas:getAreasIntersectingArea(pos1, pos2) +	local res = {} +	if self.store then +		local a = self.store:get_areas_in_area(pos1, pos2, +				true, false, true) +		for store_id, store_area in pairs(a) do +			local id = tonumber(store_area.data) +			res[id] = self.areas[id] +		end +	else +		self:sortPos(pos1, pos2) +		local p1x, p1y, p1z = pos1.x, pos1.y, pos1.z +		local p2x, p2y, p2z = pos2.x, pos2.y, pos2.z +		for id, area in pairs(self.areas) do +			local ap1, ap2 = area.pos1, area.pos2 +			if +					(ap1.x <= p2x and ap2.x >= p1x) and +					(ap1.y <= p2y and ap2.y >= p1y) and +					(ap1.z <= p2z and ap2.z >= p1z) then +				-- Found an intersecting area. +				res[id] = area +			end +		end +	end +	return res  end  -- Checks if the area is unprotected or owned by you @@ -43,41 +80,49 @@ end  -- Note that this fails and returns false when the specified area is fully  -- owned by the player, but with multiple protection zones, none of which  -- cover the entire checked area. --- @param name (optional) player name.  If not specified checks for any intersecting areas. --- @param allow_open Whether open areas should be counted as is they didn't exist. +-- @param name (optional) Player name.  If not specified checks for any intersecting areas. +-- @param allow_open Whether open areas should be counted as if they didn't exist.  -- @return Boolean indicating whether the player can interact in that area. --- @return Un-owned intersecting area id, if found. +-- @return Un-owned intersecting area ID, if found.  function areas:canInteractInArea(pos1, pos2, name, allow_open)  	if name and minetest.check_player_privs(name, self.adminPrivs) then  		return true  	end -	areas:sortPos(pos1, pos2) -	-- First check for a fully enclosing owned area. -	if name then -		for id, area in pairs(self.areas) do -			-- A little optimization: isAreaOwner isn't necessary -			-- here since we're iterating through all areas. -			if area.owner == name and -					self:isSubarea(pos1, pos2, id) then -				return true -			end +	self:sortPos(pos1, pos2) + +	-- Intersecting non-owned area ID, if found. +	local blocking_area = nil + +	local areas = self:getAreasIntersectingArea(pos1, pos2) +	for id, area in pairs(areas) do +		-- First check for a fully enclosing owned area. +		-- A little optimization: isAreaOwner isn't necessary +		-- here since we're iterating over all relevant areas. +		if area.owner == name and +				self:isSubarea(pos1, pos2, id) then +			return true  		end -	end -	-- Then check for intersecting (non-owned) areas. -	for id, area in pairs(self.areas) do -		local p1, p2 = area.pos1, area.pos2 -		if (p1.x <= pos2.x and p2.x >= pos1.x) and -		   (p1.y <= pos2.y and p2.y >= pos1.y) and -		   (p1.z <= pos2.z and p2.z >= pos1.z) then -			-- Found an intersecting area. -			-- Return if the area is closed or open areas aren't -			-- allowed, and the area isn't owned. -			if (not allow_open or not area.open) and -					(not name or not areas:isAreaOwner(id, name)) then -				return false, id -			end + +		-- Then check for intersecting non-owned (blocking) areas. +		-- We don't bother with this check if we've already found a +		-- blocking area, as the check is somewhat expensive. +		-- The area blocks if the area is closed or open areas aren't +		-- acceptable to the caller, and the area isn't owned. +		-- Note: We can't return directly here, because there might be +		-- an exclosing owned area that we haven't gotten to yet. +		if not blocking_area and +				(not allow_open or not area.open) and +				(not name or not self:isAreaOwner(id, name)) then +			blocking_area = id  		end  	end + +	if blocking_area then +		return false, blocking_area +	end + +	-- There are no intersecting areas or they are only partially +	-- intersecting areas and they are all owned by the player.  	return true  end diff --git a/internal.lua b/internal.lua index f1b048d..ea94a27 100644 --- a/internal.lua +++ b/internal.lua @@ -30,6 +30,40 @@ function areas:load()  		self.areas = {}  	end  	file:close() +	self:populateStore() +end + +--- Checks an AreaStore ID. +-- Deletes the AreaStore (falling back to the iterative method) +-- and prints an error message if the ID is invalid. +-- @return Whether the ID was valid. +function areas:checkAreaStoreId(sid) +	if not sid then +		minetest.log("error", "AreaStore failed to find an ID for an " +			.."area!  Falling back to iterative area checking.") +		self.store = nil +		self.store_ids = nil +	end +	return sid and true or false +end + +-- Populates the AreaStore after loading, if needed. +function areas:populateStore() +	if not rawget(_G, "AreaStore") then +		return +	end +	local store = AreaStore() +	local store_ids = {} +	for id, area in pairs(areas.areas) do +		local sid = store:insert_area(area.pos1, +			area.pos2, tostring(id)) +		if not self:checkAreaStoreId(sid) then +			return +		end +		store_ids[id] = sid +	end +	self.store = store +	self.store_ids = store_ids  end  -- Finds the first usable index in a table @@ -41,15 +75,28 @@ local function findFirstUnusedIndex(t)  	return i  end --- Add a area, returning the new area's id. +--- Add a area. +-- @return The new area's ID.  function areas:add(owner, name, pos1, pos2, parent)  	local id = findFirstUnusedIndex(self.areas) -	self.areas[id] = {name=name, pos1=pos1, pos2=pos2, owner=owner, -			parent=parent} +	self.areas[id] = { +		name = name, +		pos1 = pos1, +		pos2 = pos2, +		owner = owner, +		parent = parent +	} +	-- Add to AreaStore +	if self.store then +		local sid = self.store:insert_area(pos1, pos2, tostring(id)) +		if self:checkAreaStoreId(sid) then +			self.store_ids[id] = sid +		end +	end  	return id  end --- Remove a area, and optionally it's children recursively. +--- Remove a area, and optionally it's children recursively.  -- If a area is deleted non-recursively the children will  -- have the removed area's parent as their new parent.  function areas:remove(id, recurse) @@ -73,6 +120,12 @@ function areas:remove(id, recurse)  	-- Remove main entry  	self.areas[id] = nil + +	-- Remove from AreaStore +	if self.store then +		self.store:remove_area(self.store_ids[id]) +		self.store_ids[id] = nil +	end  end  -- Checks if a area between two points is entirely contained by another area  | 
