diff --git a/api.lua b/api.lua index 641b4ea..a5fd793 100644 --- a/api.lua +++ b/api.lua @@ -19,10 +19,23 @@ local plants = { ["morefarming:seed_carrot"]="air", ["morefarming:seed_corn"]="air", } +areas.registered_protection_conditions = {} areas.registered_on_adds = {} areas.registered_on_removes = {} areas.registered_on_moves = {} +areas.callback_origins = {} + +function areas:registerProtectionCondition(func) + table.insert(areas.registered_protection_conditions, func) + local debug_info = debug.getinfo(func, "S") + areas.callback_origins[func] = { + mod = core.get_current_modname() or "??", + source = debug_info.short_src or "??", + line = debug_info.linedefined or "??" + } +end + function areas:registerOnAdd(func) table.insert(areas.registered_on_adds, func) end @@ -101,56 +114,75 @@ function areas:getAreasIntersectingArea(pos1, pos2) return res 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) if minetest.check_player_privs(name, self.adminPrivs) then return true end - local owned = false - for _, area in pairs(self:getAreasAtPos(pos)) do - if area.owner == name or area.open then - return true - end - if area.openfarming then - -- if area is openfarming - local player = minetest.get_player_by_name(name) - local node = minetest.get_node(pos).name - if player and minetest.registered_nodes[node] then - local wstack = player:get_wielded_item():get_name() - if wstack == "" then wstack = "hand" end + local area = self:getSmallestAreaAtPos(pos) + -- No area, player owns it or area is open + if not area + or area.owner == name + or area.open + then + return true + elseif area.openfarming then + -- if area is openfarming + local player = minetest.get_player_by_name(name) + local node = minetest.get_node(pos).name + if player and minetest.registered_nodes[node] then + local wstack = player:get_wielded_item():get_name() + if wstack == "" then wstack = "hand" end - --on_dig - if minetest.get_item_group(node, "plant") == 1 and (wstack == "hand" or minetest.registered_tools[wstack]) then - return true - end + --on_dig + if minetest.get_item_group(node, "plant") == 1 and (wstack == "hand" or minetest.registered_tools[wstack]) then + return true + end - --on_place - if plants[wstack] ~= nil and plants[wstack] == node then - return true - end + --on_place + if plants[wstack] ~= nil and plants[wstack] == node then + return true end end - if areas.factions_available and area.faction_open then - if (factions.version or 0) < 2 then - local faction_name = factions.get_player_faction(name) - if faction_name then - for _, fname in ipairs(area.faction_open or {}) do - if faction_name == fname then - return true - end - end - end - else + end + elseif areas.factions_available and area.faction_open then + if (factions.version or 0) < 2 then + local faction_name = factions.get_player_faction(name) + if faction_name then 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 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 - owned = true end - return not owned + return false end -- Returns a table (list) of all players that own an area diff --git a/api.md b/api.md index e243a8a..1b38b14 100644 --- a/api.md +++ b/api.md @@ -5,11 +5,27 @@ API list --- * `areas:registerHudHandler(handler)` - Registers a handler to add items to the Areas HUD. See [HUD](#hud). + * `areas:registerProtectionCondition(func(pos1, pos2, name))` - +See [Protection Conditions](#Protection-Conditions) * `areas:registerOnAdd(func(id, area))` * `areas:registerOnRemove(func(id))` * `areas:registerOnMove(func(id, area, pos1, pos2))` +Protection Conditions +--- + +With `areas:registerProtectionCondition(func(pos1, pos2, name))` +you can register rules to control whether to allow or prohibit the creation of an area. + +Return values: +* `true` Forcefully allows the area creation. This overwrites the outcome of any + previously executed conditions, including the default ones registered by this mod. +* `false, errMsg` Disable the creation of the area and return an error message. +* `nil` (or no return value) Enable the creation of the area, + unless specified otherwise by the other registered callbacks. + + HUD --- diff --git a/chatcommands.lua b/chatcommands.lua index eabb908..4a595b8 100644 --- a/chatcommands.lua +++ b/chatcommands.lua @@ -173,13 +173,46 @@ minetest.register_chatcommand("find_areas", { minetest.register_chatcommand("list_areas", { - description = S("List your areas, or all areas if you are an admin."), + params = S("[]"), + description = S("List your areas. Admins can list all areas or those of a specific player."), func = function(name, param) local admin = minetest.check_player_privs(name, areas.adminPrivs) + local admin_show_summary = admin + local owner_name = name + + if admin and #param > 0 then + owner_name = param + admin_show_summary = false + end + local areaStrings = {} - for id, area in pairs(areas.areas) do - if admin or areas:isAreaOwner(id, name) then - table.insert(areaStrings, areas:toString(id)) + if admin_show_summary then + -- Summary per-player + local indices = {} + local counts = {} -- { [1] = name, [2] = count }, ... + for _, area in pairs(areas.areas) do + local i = indices[area.owner] + if i then + counts[i][2] = counts[i][2] + 1 + else + table.insert(counts, { area.owner, 1 }) + indices[area.owner] = #counts + end + end + -- Alphabatical name sorting + table.sort(counts, function (kv_a, kv_b) + return kv_a[1] < kv_b[1] + end) + -- Output + for _, kv in ipairs(counts) do + table.insert(areaStrings, S("@1 : @2 area(s)", kv[1], kv[2])) + end + else + -- Detailed list + for id, area in pairs(areas.areas) do + if areas:isAreaOwner(id, owner_name) then + table.insert(areaStrings, areas:toString(id)) + end end end if #areaStrings == 0 then diff --git a/hud.lua b/hud.lua index 1b62dbb..c6bea81 100644 --- a/hud.lua +++ b/hud.lua @@ -66,7 +66,7 @@ minetest.register_globalstep(function(dtime) hud = {} areas.hud[name] = hud hud.areasId = player:hud_add({ - hud_elem_type = "text", + [minetest.features.hud_def_type_field and "type" or "hud_elem_type"] = "text", -- compatible with older versions name = "Areas", number = 0xFFFFFF, position = {x=0, y=1}, diff --git a/internal.lua b/internal.lua index 559576f..79330b5 100644 --- a/internal.lua +++ b/internal.lua @@ -206,62 +206,93 @@ function areas:getChildren(id) return children end --- Checks if the user has sufficient privileges. --- If the player is not a administrator it also checks --- if the area intersects other areas that they do not own. --- Also checks the size of the area and if the user already --- has more than max_areas. +-- checks all possible restrictions registered with +-- areas:registerProtectionCondition +-- builtin callbacks below function areas:canPlayerAddArea(pos1, pos2, name) + local allowed = true + local errMsg + for i=1, #areas.registered_protection_conditions do + local res, msg = areas.registered_protection_conditions[i](pos1, pos2, name) + if res == true then + -- always allow to protect, no matter of other conditions + return true + elseif res == false then + -- there might be another callback that returns true, so we can't break here + allowed = false + -- save the first error that occurred + errMsg = errMsg or msg + elseif res ~= nil then + local origin = areas.callback_origins[areas.registered_protection_conditions[i]] + error("\n[Mod] areas: Invalid api usage from mod '" .. + origin.mod .. "' in callback registerProtectionCondition() at " .. + origin.source .. ":" .. origin.line) + end + end + + return allowed, errMsg +end + +-- Checks if the user has sufficient privileges. +areas:registerProtectionCondition(function(pos1, pos2, name) local privs = minetest.get_player_privs(name) if privs.areas then + -- always allow administrators to create areas return true end - -- Check self protection privilege, if it is enabled, - -- and if the area is too big. - if not self.config.self_protection or + -- Check self protection privilege + if not areas.config.self_protection or not privs[areas.config.self_protection_privilege] then return false, S("Self protection is disabled or you do not have" .." the necessary privilege.") end +end) - -- MFF: megabuilders skip checks on size and number of areas - if not privs.megabuilder then +-- check if the area is too big +-- NALC: megabuilders skip checks on size and number of areas +areas:registerProtectionCondition(function(pos1, pos2, name) + local privs = minetest.get_player_privs(name) + if not privs.megabuilder then local max_size = privs.areas_high_limit and - self.config.self_protection_max_size_high or - self.config.self_protection_max_size + areas.config.self_protection_max_size_high or + areas.config.self_protection_max_size if - (pos2.x - pos1.x) > max_size.x or - (pos2.y - pos1.y) > max_size.y or - (pos2.z - pos1.z) > max_size.z then - return false, S("Area is too big.") + (pos2.x - pos1.x + 1) > max_size.x or + (pos2.y - pos1.y + 1) > max_size.y or + (pos2.z - pos1.z + 1) > max_size.z then + return false, S("Area is too big.") end end +end) - -- Check number of areas the user has and make sure it not above the max - local count = 0 - for _, area in pairs(self.areas) do - if area.owner == name then - count = count + 1 - end +-- Check number of areas the user has and make sure it not above the max +areas:registerProtectionCondition(function(pos1, pos2, name) + local privs = minetest.get_player_privs(name) + local count = 0 + for _, area in pairs(areas.areas) do + if area.owner == name then + count = count + 1 end - local max_areas = privs.areas_high_limit and - self.config.self_protection_max_areas_high or - self.config.self_protection_max_areas + end + local max_areas = privs.areas_high_limit and + areas.config.self_protection_max_areas_high or + areas.config.self_protection_max_areas if count >= max_areas then return false, S("You have reached the maximum amount of" .." areas that you are allowed to protect.") end +end) - -- Check intersecting areas - local can, id = self:canInteractInArea(pos1, pos2, name) +-- checks if the area intersects other areas that the player do not own. +areas:registerProtectionCondition(function(pos1, pos2, name) + local can, id = areas:canInteractInArea(pos1, pos2, name) if not can then - local area = self.areas[id] + local area = areas.areas[id] return false, S("The area intersects with @1 [@2] (@3).", area.name, id, area.owner) end - return true -end +end) -- Given a id returns a string in the format: -- "name [id]: owner (x1, y1, z1) (x2, y2, z2) -> children" diff --git a/locale/areas.fr.tr b/locale/areas.fr.tr index bbbc089..b60726c 100644 --- a/locale/areas.fr.tr +++ b/locale/areas.fr.tr @@ -133,3 +133,5 @@ Set area protection region, position 1, or position 2 by punching nodes, or disp The area @1 does not exist.=La zone @1 n’existe pas. Unable to get position.=Impossible d’obtenir la position. Unknown subcommand: @1=Sous-commande inconnue : @1 + +Relative coordinates is not supported on this server. Please upgrade Minetest to 5.7.0 or newer versions.= diff --git a/locale/areas.it.tr b/locale/areas.it.tr index 006b04d..7fe77da 100644 --- a/locale/areas.it.tr +++ b/locale/areas.it.tr @@ -133,3 +133,5 @@ Set area protection region, position 1, or position 2 by punching nodes, or disp The area @1 does not exist.=L'area @1 non esiste. Unable to get position.=Impossibile ottenere la posizione. Unknown subcommand: @1=Sotto-comando sconosciuto: @1 + +Relative coordinates is not supported on this server. Please upgrade Minetest to 5.7.0 or newer versions.= diff --git a/locale/areas.ru.tr b/locale/areas.ru.tr new file mode 100644 index 0000000..11e8c74 --- /dev/null +++ b/locale/areas.ru.tr @@ -0,0 +1,137 @@ +# textdomain: areas + + + +### chatcommands.lua ### + +=<имя_территории> + [faction_name]= [имя_фракции] +=<новый_владелец> += +=<имя_игрока> +=<новое_имя> +@1 has given you control over the area "@2" (ID @3).=@1 передал вам территорию "@2" (ID @3). +@1 spanning up to @2x@3x@4.=@1 размером до @2x@3x@4. +A regular expression is required.=Не указано регулярное выражение. +Area @1 does not exist or is not owned by you.=Территория @1 не существует или Вы не имеете к ней доступа. +Area closed for faction members.=Территория закрыта для игроков из фракций. +Area closed.=Территория закрыта. +Area does not exist.=Территория не существует. +Area is open for members of: @1=Территория открыта для игроков из фракций: @1 +Area opened.=Территория открыта. +Area protected. ID: @1=Территория защищена. ID: @1 +Area renamed.=Территория переименована. +Area successfully moved.=Территория успешно перенесена. +Change the owner of an area using its ID=Изменить владельца территории по её ID +Faction doesn't exists=Фракция не существует. +Find areas using a Lua regular expression=Поиск территорий с использованием регулярных выражений Lua +Get information about area configuration and usage.=Получить информацию о конфигурации и использовании защиты территорий. + +Give a player access to a sub-area between two positions that have already been protected, Use set_owner if you don't want the parent to be set.=Дать игроку доступ к выделенной территории, создав дополнительный регион. Используйте set_owner, если вы не хотите привязки к родительской территории. + +Invalid regular expression.=Неверное регулярное выражение. +Limit: @1 areas=Ограничение: @1 территорий(я) +Limit: no area count limit=Ограничение: нет ограничений на кол-во территорий +List your areas, or all areas if you are an admin.=Вывести список доступных Вам территорий или всех существующих территорий, если вы администратор. +Move (or resize) an area to the current positions.=Переместить (или изменить размер) территории. +No matches found.=Совпадений не найдено. +No visible areas.=Нет доступных территорий. +Owner changed.=Владелец изменён. +Players with the "@1" privilege can protect up to @2 areas=Игроки, имеющие привилегию "@1" могут защищать до @2 территорий + +Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=Защитить территорию между двумя позициями и дать игроку доступ к ней без привязки к родительскому региону. + +Protect your own area=Защитить вашу территорию +Recursively remove areas using an ID=Рекурсивное удаление территорий (вместе с дочерними) по его ID +Remove an area using an ID=Удаление территории по его ID +Removed area @1=Удалена территория @1 +Removed area @1 and its sub areas.=Удалена территория @1 и её дочерние территории. +Removes all ownerless areas=Удалить все бесхозные территории (без владельца) +Rename an area that you own=Переименовать территорию, которой вы владеете. +Self protection is disabled.=Защита своих территорий отключена. +Self protection is enabled.=Защита своих территорий включена. +That area doesn't exist.=Территория не существует. +The player "@1" does not exist.=Игрок "@1" не существует. +Toggle an area open (anyone can interact) or closed=Открыть/закрыть территорию для других игроков +Toggle an area open/closed for members in your faction.=Открыть/закрыть территорию для игроков вашей фракции. +You are an area administrator ("areas" privilege).=Вы можете управлять всеми территориями (привилегия "areas") +You can protect areas=Вы можете защищать территории +You can't protect that area.=Вы не можете установить защиту на эту территорию. +You can't protect that area: @1=Вы не можете установить защиту на территорию: @1 +You don't have the necessary privilege ("@1").=У Вас нет необходимой привилегии ("@1"). +You don't own that area.=Вы не являетесь владельцем этой территории. +You have @1 areas.=У вас @1 территорий. + +You have been granted control over area #@1. Type /list_areas to show your areas.=Вам выдан доступ к территории #@1. + +You have extended area protection limits ("areas_high_limit" privilege).=У вас увеличенный лимит на кол-во создаваемых вами защищённых территорий + +You have the necessary privilege ("@1").=У вас есть необходимая привилегия ("@1"). +You need to select an area first.=Сначала необходимо выделить территорию. + +### chatcommands.lua ### +### pos.lua ### + += +Invalid usage, see /help @1.=Неверное использование, см. /help @1. + +### hud.lua ### + +:open=:открытая +Areas:=Территории: + +### init.lua ### + +Can administer areas.=Может управлять территориями. +Can protect areas.=Может создавать защищённые территории. +Can protect more, bigger areas.=Может создавать защищённые территории больше территорий и с бОльшим размером. + +### interact.lua ### + +@1 is protected by @2.=@1 - принадлежит @2. + +### internal.lua ### + +Area is too big.=Территория слишком велика. + +Self protection is disabled or you do not have the necessary privilege.=Возможность защиты территорий отключена или же Вы не имеете необходимых привилегий. + +The area intersects with @1 [@2] (@3).=Территория пересекается с @1 [@2] (@3). + +You have reached the maximum amount of areas that you are allowed to protect.=Вы достигли максимально допустимого количества на создание защищённых территорий. + + +### legacy.lua ### + +=<версия> +Converted areas saved. Done.=Готово. Сконвертированные территории сохранены. +Converting areas…=Конвертирование территорий… +Error loading legacy file: @1=Ошибка загрузки файла с устаревшим форматом: @1 +Invalid version number. (0 allowed)=Неверный номер версии. (поддерживается 0) +Legacy file loaded.=Файл с устаревшим форматом загружен. + +Loads, converts, and saves the areas from a legacy save file.=Загружает, конвертирует и сохраняет территории из файла с устаревшим форматом. + +Table format updated.=Формат обновлён. +unnamed=без_названия + +### pos.lua ### + +=<не_установлена> +Area @1 selected.=Территория @1 выбрана. +Area position @1 set to @2=Позиция @1 установлена в @2 +Position @1 set to @2=Позиция @1 установлена в @2 +Position @1: =Позиция @1: +Select an area by ID.=Выбрать территорию по её ID. +Select position @1 by punching a node.=Установите позицию @1, ударив по блоку. +Select positions by punching two nodes.=Установите позиции, ударив по двум блокам. + +Set area protection region position @1 to your location or the one specified=Установить позицию @1 для выбора защищаемой территории + +Set area protection region, position 1, or position 2 by punching nodes, or display the region=Установить позиции 1 и/или 2, ударяя по блокам, или вывести ранее установленные позиции + +The area @1 does not exist.=Территория @1 не существует. +Unable to get position.=Не удалось получить позицию. +Unknown subcommand: @1=Неизвестная под-команда/аргумент. + +Relative coordinates is not supported on this server. Please upgrade Minetest to 5.7.0 or newer versions.= diff --git a/locale/areas.zh_CN.tr b/locale/areas.zh_CN.tr new file mode 100644 index 0000000..2ac2be1 --- /dev/null +++ b/locale/areas.zh_CN.tr @@ -0,0 +1,137 @@ +# textdomain: areas + + + +### chatcommands.lua ### + +=<保护区名称> + [faction_name]=<保护区编号> [派系名称] +=<新拥有者> +=<上级保护区编号> +=<玩家名称> +=<新名称> +@1 has given you control over the area "@2" (ID @3).=@1 已将保护区“@2”(编号 @3)的控制权交与您。 +@1 spanning up to @2x@3x@4.=@1,最大为 @2x@3x@4。 +A regular expression is required.=需要正则表达式。 +Area @1 does not exist or is not owned by you.=保护区 @1 不存在,或并非由您拥有。 +Area closed for faction members.=保护区已对派系成员关闭。 +Area closed.=保护区已关闭。 +Area does not exist.=保护区不存在。 +Area is open for members of: @1=保护区已对以下派系的成员开放:@1 +Area opened.=保护区已开放。 +Area protected. ID: @1=成功进行保护。 保护区编号:@1 +Area renamed.=保护区已重新命名。 +Area successfully moved.=成功移动保护区。 +Change the owner of an area using its ID=通过保护区编号改变其拥有者 +Faction doesn't exists=派系不存在 +Find areas using a Lua regular expression=使用 Lua 正则表达式寻找保护区 +Get information about area configuration and usage.=获得保护区配置与使用的资讯。 + +Give a player access to a sub-area between two positions that have already been protected, Use set_owner if you don't want the parent to be set.=授予其他玩家已有保护区内一部分地方、两个座标之间区域的的存取权。如果不想上级保护区被设定,请使用 /set_owner。 + +Invalid regular expression.=正则表达式无效。 +Limit: @1 areas=限制:@1个保护区 +Limit: no area count limit=限制:无数量限制 +List your areas, or all areas if you are an admin.=列出您拥有的保护区,或对于管理员而言,列出所有保护区。 +Move (or resize) an area to the current positions.=移动一保护区至目前座标,或改变其大小。 +No matches found.=找不到匹配项。 +No visible areas.=没有保护区可供查阅。 +Owner changed.=拥有者已修改。 +Players with the "@1" privilege can protect up to @2 areas=拥有“@1”权限的玩家最多可以保护 @2 个保护区 + +Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=授予其他玩家两个座标之间区域的控制权,而不设定上级保护区 + +Protect your own area=对您自己的地区施加保护 +Recursively remove areas using an ID=通过保护区编号移除保护区以及其所有子保护区 +Remove an area using an ID=通过保护区编号移除一保护区 +Removed area @1=保护区 @1 已移除 +Removed area @1 and its sub areas.=保护区 @1 以及其所有子保护区已移除。 +Removes all ownerless areas=移除所有无主保护区 +Rename an area that you own=重新命名您拥有的保护区 +Self protection is disabled.=自我保护被禁用。 +Self protection is enabled.=自我保护被启用。 +That area doesn't exist.=保护区不存在。 +The player "@1" does not exist.=玩家“@1”不存在。 +Toggle an area open (anyone can interact) or closed=切换保护区开放状态(如开放,所有玩家均可互动) +Toggle an area open/closed for members in your faction.=切换保护区对派系成员的开放状态 +You are an area administrator ("areas" privilege).=您是保护区管理员(拥有“area”权限) +You can protect areas=您可以建立保护区 +You can't protect that area.=您无法保护该区域。 +You can't protect that area: @1=您无法保护该区域:@1 +You don't have the necessary privilege ("@1").=您没有所需权限(“@1”) +You don't own that area.=您并不拥有该保护区。 +You have @1 areas.=您有 @1 个保护区。 + +You have been granted control over area #@1. Type /list_areas to show your areas.=您已被授予保护区 #@1 的控制权。 使用 /list_areas 以查看您的保护区。 + +You have extended area protection limits ("areas_high_limit" privilege).=您拥有延伸保护权限(拥有“areas_high_limit”权限) + +You have the necessary privilege ("@1").=您拥有所需权限(“@1”) +You need to select an area first.=请先选择范围。 + +### chatcommands.lua ### +### pos.lua ### + +=<保护区编号> +Invalid usage, see /help @1.=无效用法,请参见 /help @1。 + +### hud.lua ### + +:open=:开放 +Areas:=保护区: + +### init.lua ### + +Can administer areas.=可以管理保护区 +Can protect areas.=可以进行保护 +Can protect more, bigger areas.=进行保护时,数量以及体积上限提高。 + +### interact.lua ### + +@1 is protected by @2.=@1 被 @2 保护。 + +### internal.lua ### + +Area is too big.=区域过大。 + +Self protection is disabled or you do not have the necessary privilege.=自我保护被禁用,或您没有所需权限。 + +The area intersects with @1 [@2] (@3).=区域与 @1 [@2] (@3) 重叠。 + +You have reached the maximum amount of areas that you are allowed to protect.=您的现有保护区数量已达到所允许的上限。 + + +### legacy.lua ### + +=<版本> +Converted areas saved. Done.=转换后的保护区已经保存。 完成。 +Converting areas…=正在转换保护区…… +Error loading legacy file: @1=载入旧档案时发生错误:@1 +Invalid version number. (0 allowed)=无效版本号。(允许值:0) +Legacy file loaded.=旧档案已载入。 + +Loads, converts, and saves the areas from a legacy save file.=载入、转换并储存旧存档中的保护区。 + +Table format updated.=表格格式已更新。 +unnamed=未命名 + +### pos.lua ### + +=<未设定> +Area @1 selected.=保护区 @1 已选择。 +Area position @1 set to @2=保护区座标 @1 已设定为 @2。 +Position @1 set to @2=保护区座标 @1 已设定为 @2。 +Position @1: =保护区座标 @1: +Select an area by ID.=通过保护区编号选择区域 +Select position @1 by punching a node.=请击打方块以设定保护区座标 @1 的位置。 +Select positions by punching two nodes.=请击打两个方块,以设定保护区座标的位置。 + +Set area protection region position @1 to your location or the one specified=将保护区座标 @1 的位置设定为您目前的位置或提供的座标 + +Set area protection region, position 1, or position 2 by punching nodes, or display the region=击打方块设定保护区座标,或获得其座标资讯 + +The area @1 does not exist.=保护区 @1 不存在。 +Unable to get position.=无法获得座标。 +Unknown subcommand: @1=子指令不明:@1 + +Relative coordinates is not supported on this server. Please upgrade Minetest to 5.7.0 or newer versions.=此服务器不支援相对座标。请更新Minetest至5.7.0或之后的版本。 \ No newline at end of file diff --git a/locale/areas.zh_TW.tr b/locale/areas.zh_TW.tr new file mode 100644 index 0000000..4a2c33f --- /dev/null +++ b/locale/areas.zh_TW.tr @@ -0,0 +1,137 @@ +# textdomain: areas + + + +### chatcommands.lua ### + +=<保護區名稱> + [faction_name]=<保護區編號> [派系名稱] +=<新擁有者> +=<上級保護區編號> +=<玩家名稱> +=<新名稱> +@1 has given you control over the area "@2" (ID @3).=@1 已將保護區「@2」(編號 @3)的控制權交與您。 +@1 spanning up to @2x@3x@4.=@1,最大爲 @2x@3x@4。 +A regular expression is required.=需要正則表達式。 +Area @1 does not exist or is not owned by you.=保護區 @1 不存在,或並非由您擁有。 +Area closed for faction members.=保護區已對派系成員關閉。 +Area closed.=保護區已關閉。 +Area does not exist.=保護區不存在。 +Area is open for members of: @1=保護區已對以下派系的成員開放:@1 +Area opened.=保護區已開放。 +Area protected. ID: @1=成功進行保護。保護區編號:@1 +Area renamed.=保護區已重新命名。 +Area successfully moved.=成功移動保護區。 +Change the owner of an area using its ID=通過保護區編號改變其擁有者 +Faction doesn't exists=派系不存在 +Find areas using a Lua regular expression=使用 Lua 正則表達式尋找保護區 +Get information about area configuration and usage.=獲得保護區配置與使用的資訊。 + +Give a player access to a sub-area between two positions that have already been protected, Use set_owner if you don't want the parent to be set.=授予其他玩家已有保護區內一部分地方、兩個座標之間區域的的存取權。如果不想上級保護區被設定,請使用 /set_owner。 + +Invalid regular expression.=正則表達式無效。 +Limit: @1 areas=限制:@1個保護區 +Limit: no area count limit=限制:無數量限制 +List your areas, or all areas if you are an admin.=列出您擁有的保護區,或對於管理員而言,列出所有保護區。 +Move (or resize) an area to the current positions.=移動一保護區至目前座標,或改變其大小。 +No matches found.=找不到匹配項。 +No visible areas.=沒有保護區可供查閱。 +Owner changed.=擁有者已修改。 +Players with the "@1" privilege can protect up to @2 areas=擁有「@1」權限的玩家最多可以保護 @2 個保護區 + +Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=授予其他玩家兩個座標之間區域的控制權,而不設定上級保護區 + +Protect your own area=對您自己的地區施加保護 +Recursively remove areas using an ID=通過保護區編號移除保護區以及其所有子保護區 +Remove an area using an ID=通過保護區編號移除一保護區 +Removed area @1=保護區 @1 已移除 +Removed area @1 and its sub areas.=保護區 @1 以及其所有子保護區已移除 +Removes all ownerless areas=移除所有無主保護區 +Rename an area that you own=重新命名您擁有的保護區 +Self protection is disabled.=自我保護被禁用。 +Self protection is enabled.=自我保護被啓用。 +That area doesn't exist.=保護區不存在。 +The player "@1" does not exist.=玩家「@1」不存在。 +Toggle an area open (anyone can interact) or closed=切換保護區開放狀態(如開放,所有玩家均可互動) +Toggle an area open/closed for members in your faction.=切換保護區對派系成員的開放狀態 +You are an area administrator ("areas" privilege).=您是保護區管理員(擁有「area」權限) +You can protect areas=您可以建立保護區 +You can't protect that area.=您無法保護該區域。 +You can't protect that area: @1=您無法保護該區域:@1 +You don't have the necessary privilege ("@1").=您沒有所需權限(「@1」) +You don't own that area.=您並不擁有改保護區。 +You have @1 areas.=您有 @1 個保護區。 + +You have been granted control over area #@1. Type /list_areas to show your areas.=您已被授予保護區 #@1 的控制權。使用 /list_areas 以查看您的保護區。 + +You have extended area protection limits ("areas_high_limit" privilege).=您擁有延伸保護權限(擁有「areas_high_limit」權限) + +You have the necessary privilege ("@1").=您擁有所需權限(「@1」) +You need to select an area first.=請先選擇範圍。 + +### chatcommands.lua ### +### pos.lua ### + +=<保護區編號> +Invalid usage, see /help @1.=無效用法,請參見 /help @1。 + +### hud.lua ### + +:open=:開放 +Areas:=保護區: + +### init.lua ### + +Can administer areas.=可以管理保護區 +Can protect areas.=可以進行保護 +Can protect more, bigger areas.=進行保護時,數量以及體積上限提高。 + +### interact.lua ### + +@1 is protected by @2.=@1 被 @2 保護。 + +### internal.lua ### + +Area is too big.=區域過大。 + +Self protection is disabled or you do not have the necessary privilege.=自我保護被禁用,或您沒有所需權限。 + +The area intersects with @1 [@2] (@3).=區域與 @1 [@2] (@3) 重疊。 + +You have reached the maximum amount of areas that you are allowed to protect.=您的現有保護區數量已達到所允許的上限。 + + +### legacy.lua ### + +=<版本> +Converted areas saved. Done.=轉換後的保護區已經保存。完成。 +Converting areas…=正在轉換保護區…… +Error loading legacy file: @1=載入舊檔案時發生錯誤:@1 +Invalid version number. (0 allowed)=無效版本號。(允許值:0) +Legacy file loaded.=舊檔案已載入。 + +Loads, converts, and saves the areas from a legacy save file.=載入、轉換並儲存舊存檔中的保護區。 + +Table format updated.=表格格式已更新。 +unnamed=未命名 + +### pos.lua ### + +=<未設定> +Area @1 selected.=保護區 @1 已選擇。 +Area position @1 set to @2=保護區座標 @1 已設定爲 @2。 +Position @1 set to @2=保護區座標 @1 已設定爲 @2。 +Position @1: =保護區座標 @1: +Select an area by ID.=通過保護區編號選擇區域 +Select position @1 by punching a node.=請擊打方塊,以設定保護區座標 @1 的位置。 +Select positions by punching two nodes.=請擊打兩個方塊,以設定保護區座標的位置。 + +Set area protection region position @1 to your location or the one specified=將保護區座標 @1 的位置設定爲您目前的位置或提供的座標 + +Set area protection region, position 1, or position 2 by punching nodes, or display the region=擊打方塊設定保護區座標,或獲得其座標資訊 + +The area @1 does not exist.=保護區 @1 不存在。 +Unable to get position.=無法獲得座標。 +Unknown subcommand: @1=子指令不明:@1 + +Relative coordinates is not supported on this server. Please upgrade Minetest to 5.7.0 or newer versions.=此伺服器不支援相對座標。請更新Minetest至5.7.0或之後的版本。 diff --git a/locale/template.txt b/locale/template.txt index 2026c5e..3508101 100644 --- a/locale/template.txt +++ b/locale/template.txt @@ -133,3 +133,5 @@ Set area protection region, position 1, or position 2 by punching nodes, or disp The area @1 does not exist.= Unable to get position.= Unknown subcommand: @1= + +Relative coordinates is not supported on this server. Please upgrade Minetest to 5.7.0 or newer versions.= diff --git a/pos.lua b/pos.lua index 7a9cbd2..f512eac 100644 --- a/pos.lua +++ b/pos.lua @@ -22,6 +22,37 @@ local function posLimit(pos) } end +local parse_relative_pos + +if minetest.parse_relative_number then + parse_relative_pos = function(x_str, y_str, z_str, pos) + + local x = pos and minetest.parse_relative_number(x_str, pos.x) + or tonumber(x_str) + local y = pos and minetest.parse_relative_number(y_str, pos.y) + or tonumber(y_str) + local z = pos and minetest.parse_relative_number(z_str, pos.z) + or tonumber(z_str) + if x and y and z then + return vector.new(x, y, z) + end + end +else + parse_relative_pos = function(x_str, y_str, z_str, pos) + local x = tonumber(x_str) + local y = tonumber(y_str) + local z = tonumber(z_str) + if x and y and z then + return vector.new(x, y, z) + elseif string.sub(x_str, 1, 1) == "~" + or string.sub(y_str, 1, 1) == "~" + or string.sub(z_str, 1, 1) == "~" then + return nil, S("Relative coordinates is not supported on this server. " .. + "Please upgrade Minetest to 5.7.0 or newer versions.") + end + end +end + minetest.register_chatcommand("select_area", { params = S(""), description = S("Select an area by ID."), @@ -47,20 +78,25 @@ minetest.register_chatcommand("area_pos1", { privs = {}, func = function(name, param) local pos - local found, _, x, y, z = param:find( - "^(-?%d+)[, ](-?%d+)[, ](-?%d+)$") + local player = minetest.get_player_by_name(name) + if player then + pos = vector.round(player:get_pos()) + end + local found, _, x_str, y_str, z_str = param:find( + "^(~?-?%d*)[, ](~?-?%d*)[, ](~?-?%d*)$") if found then - pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)} - elseif param == "" then - local player = minetest.get_player_by_name(name) - if player then - pos = player:get_pos() - else - return false, S("Unable to get position.") + local get_pos, reason = parse_relative_pos(x_str, y_str, z_str, pos) + if get_pos then + pos = get_pos + elseif not get_pos and reason then + return false, reason end - else + elseif param ~= "" then return false, S("Invalid usage, see /help @1.", "area_pos1") end + if not pos then + return false, S("Unable to get position.") + end pos = posLimit(vector.round(pos)) areas:setPos1(name, pos) return true, S("Area position @1 set to @2", "1", @@ -74,20 +110,25 @@ minetest.register_chatcommand("area_pos2", { .." location or the one specified", "2"), func = function(name, param) local pos - local found, _, x, y, z = param:find( - "^(-?%d+)[, ](-?%d+)[, ](-?%d+)$") + local player = minetest.get_player_by_name(name) + if player then + pos = vector.round(player:get_pos()) + end + local found, _, x_str, y_str, z_str = param:find( + "^(~?-?%d*)[, ](~?-?%d*)[, ](~?-?%d*)$") if found then - pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)} - elseif param == "" then - local player = minetest.get_player_by_name(name) - if player then - pos = player:get_pos() - else - return false, S("Unable to get position.") + local get_pos, reason = parse_relative_pos(x_str, y_str, z_str, pos) + if get_pos then + pos = get_pos + elseif not get_pos and reason then + return false, reason end - else + elseif param ~= "" then return false, S("Invalid usage, see /help @1.", "area_pos2") end + if not pos then + return false, S("Unable to get position.") + end pos = posLimit(vector.round(pos)) areas:setPos2(name, pos) return true, S("Area position @1 set to @2", "2",