Use smallest area at given position (#79)

When checking for permission to interact, currently the smallest id takes precedence in a situation where areas enclose other areas. This makes it complicated to set up private areas within faction areas and vice versa. Same applies for open areas within a bigger private area.

When multiple areas have the same volume, players seem to agree that the most recently protected area should also take precedence.
Since we can't guarantee that the largest id is the most recent, we still use that info as we have no other way to determine age.
This commit is contained in:
Luke aka SwissalpS 2024-09-11 22:00:47 +02:00 committed by GitHub
parent 9a5cdb2822
commit c2e3d073be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

58
api.lua
View File

@ -95,36 +95,56 @@ function areas:getAreasIntersectingArea(pos1, pos2)
return res return res
end end
-- Checks if the area is unprotected or owned by you -- Returns smallest area at position and its id or nil.
-- If multiple areas have the same volume, larger id takes precedence.
function areas:getSmallestAreaAtPos(pos)
local smallest_area, smallest_id, volume
local smallest_volume = math.huge
for id, area in pairs(self:getAreasAtPos(pos)) do
volume = (area.pos2.x - area.pos1.x + 1)
* (area.pos2.y - area.pos1.y + 1)
* (area.pos2.z - area.pos1.z + 1)
if smallest_volume >= volume then
smallest_area = area
smallest_id = id
smallest_volume = volume
end
end
return smallest_area, smallest_id
end
-- Checks if the area is unprotected, open, owned by player
-- or player is part of faction of smallest area at position.
function areas:canInteract(pos, name) function areas:canInteract(pos, name)
if minetest.check_player_privs(name, self.adminPrivs) then if minetest.check_player_privs(name, self.adminPrivs) then
return true return true
end end
local owned = false local area = self:getSmallestAreaAtPos(pos)
for _, area in pairs(self:getAreasAtPos(pos)) do -- No area, player owns it or area is open
if area.owner == name or area.open then if not area
return true or area.owner == name
elseif areas.factions_available and area.faction_open then or area.open
if (factions.version or 0) < 2 then then
local faction_name = factions.get_player_faction(name) return true
if faction_name then elseif areas.factions_available and area.faction_open then
for _, fname in ipairs(area.faction_open or {}) do if (factions.version or 0) < 2 then
if faction_name == fname then local faction_name = factions.get_player_faction(name)
return true if faction_name then
end
end
end
else
for _, fname in ipairs(area.faction_open or {}) do for _, fname in ipairs(area.faction_open or {}) do
if factions.player_is_in_faction(fname, name) then if faction_name == fname then
return true return true
end end
end end
end end
else
for _, fname in ipairs(area.faction_open or {}) do
if factions.player_is_in_faction(fname, name) then
return true
end
end
end end
owned = true
end end
return not owned return false
end end
-- Returns a table (list) of all players that own an area -- Returns a table (list) of all players that own an area