diff --git a/.github/workflows/luacheck.yml b/.github/workflows/luacheck.yml new file mode 100644 index 0000000..4077f41 --- /dev/null +++ b/.github/workflows/luacheck.yml @@ -0,0 +1,17 @@ +name: luacheck + +on: [push, pull_request] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: apt + run: sudo apt-get install -y luarocks + - name: luacheck install + run: luarocks install --local luacheck + - name: luacheck run + run: $HOME/.luarocks/bin/luacheck ./ diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 0000000..c47f79b --- /dev/null +++ b/.luacheckrc @@ -0,0 +1,24 @@ +unused_args = false + +read_globals = { + "DIR_DELIM", + "core", + "dump", + "vector", "nodeupdate", + "VoxelManip", "VoxelArea", + "PseudoRandom", "ItemStack", + "AreaStore", + "default", + "factions", + table = { fields = { "copy", "getn" } } +} + +globals = { + "minetest", + -- mod namespace + "areas" +} + +files["legacy.lua"] = { + ignore = {"512"} +} diff --git a/README.md b/README.md index bd6a54d..e665e52 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,29 @@ -Areas mod for Minetest 0.4.8+ -============================= +Areas mod for Minetest +====================== + +Dependencies +------------ + +Minetest 5.0.0+ is recommended, but 0.4.16+ should work as well. Configuration ------------- -If you wish to specify configuration options, such as whether players are -allowed to protect their own areas with the `protect` command (disabled by -default), you should check config.lua and set the appropriate settings in your -server's configuration file (probably `minetest.conf`). +Open the tab `Settings -> All Settings -> Mods -> areas` to get a list of all +possible settings. + +For server owners: Check `settingtypes.txt` and modify your `minetest.conf` +according to the wanted setting changes. + Tutorial -------- -To protect an area you must first set the corner positions of the area. -In order to set the corner positions you can run: +1) Specify the corner positions of the area you would like to protect. +Use one of the following commands: + * `/area_pos set` and punch the two corner nodes to set them. * `/area_pos set1/set2` and punch only the first or second corner node to set them one at a time. @@ -23,25 +31,25 @@ In order to set the corner positions you can run: * `/area_pos1/2 X Y Z` to set one of the positions to the specified coordinates. -Once you have set the border positions you can protect the area by running one -of the following commands: +2) Protect the selected area by running one of the following commands: + * `/set_owner ` -- If you have the `areas` privilege. * `/protect ` -- If you have the `areas` privilege or the server administrator has enabled area self-protection. -The area name is used only for informational purposes (so that you know what -an area is for). It is not used for any other purpose. +The area name is used only for informational purposes and has no functional +importance. + For example: `/set_owner SomePlayer Mese city` -Now that you own an area you may want to add sub-owners to it. You can do this -with the `add_owner` command. Anyone with an area can use the `add_owner` -command on their areas. Before using the `add_owner` command you have to -select the corners of the sub-area as you did for `set_owner`. If your markers -are still around your original area and you want to grant access to your -entire area you will not have to re-set them. You can also use `select_area` to -place the markers at the corners of an existing area if you've reset your +3) You now own an area. You may now add sub-owners to it if you want to (see command `/add_owner`). Before using the `/add_owner` command you have to +select the corners of the sub-area as you did in step 1. + +If your markers are still around your original area and you want to grant +access to your entire area you will not have to re-set them. Use `/select_area` to place the markers at the corners of an existing area if you've reset your markers and want to grant access to a full area. -The `add_owner` command expects three arguments: + +The `/add_owner` command expects three arguments: 1. The ID number of the parent area (the area that you want to add a sub-area to). 2. The name of the player that will own the sub-area. @@ -93,6 +101,9 @@ Commands * `/area_pos2 [X,Y,Z|X Y Z]` -- Sets area position two to your position or the one supplied. + + * `/areas_cleanup` -- Removes all ownerless areas. + Useful for cleaning after user deletion, for example using /remove_player. License ------- @@ -101,4 +112,3 @@ Copyright (C) 2013 ShadowNinja Licensed under the GNU LGPL version 2.1 or later. See LICENSE.txt and http://www.gnu.org/licenses/lgpl-2.1.txt - diff --git a/api.lua b/api.lua index 12cfe22..3a6158f 100644 --- a/api.lua +++ b/api.lua @@ -1,14 +1,48 @@ +local hudHandlers = {} ---plants to place in openfarming -local plants = { ["farming:blueberries"]="air", ["farming:carrot"]="air", ["farming:coffee_beans"]="air", ["farming:corn"]="air", ["farming:cucumber"]="air", - ["farming:melon_slice"]="air", ["farming:potato"]="air", ["farming:pumpkin_slice"]="air", ["farming:raspberries"]="air", ["farming:rhubarb"]="air", - ["farming:tomato"]="air", ["farming:seed_cotton"]="air", ["farming:seed_wheat"]="air",["default:papyrus"]="air", ["farming:trellis"]="air", - ["farming:grapes"]="farming:trellis", ["farming:beanpole"]="air", ["farming:beans"]="farming:beanpole", - } +---plants to place in openfarming +local plants = { + ["farming:blueberries"]="air", ["farming:carrot"]="air", ["farming:coffee_beans"]="air", + ["farming:corn"]="air", ["farming:cucumber"]="air", ["farming:melon_slice"]="air", + ["farming:potato"]="air", ["farming:pumpkin_slice"]="air", ["farming:raspberries"]="air", + ["farming:rhubarb"]="air", ["farming:tomato"]="air", ["farming:seed_cotton"]="air", + ["farming:seed_wheat"]="air",["default:papyrus"]="air", ["farming:trellis"]="air", + ["farming:grapes"]="farming:trellis", ["farming:beanpole"]="air", ["farming:beans"]="farming:beanpole", +} + +areas.registered_on_adds = {} +areas.registered_on_removes = {} +areas.registered_on_moves = {} + +function areas:registerOnAdd(func) + table.insert(areas.registered_on_adds, func) +end + +function areas:registerOnRemove(func) + table.insert(areas.registered_on_removes, func) +end + +function areas:registerOnMove(func) + table.insert(areas.registered_on_moves, func) +end + +--- Adds a function as a HUD handler, it will be able to add items to the Areas HUD element. +function areas:registerHudHandler(handler) + table.insert(hudHandlers, handler) +end + +function areas:getExternalHudEntries(pos) + local areas = {} + for _, func in pairs(hudHandlers) do + func(pos, areas) + end + return areas +end --- Returns a list of areas that include the provided position. function areas:getAreasAtPos(pos) 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 @@ -64,7 +98,6 @@ function areas:canInteract(pos, name) return true end local owned = false - if pos == nil then return not owned end -- pour éviter crash avec nénuphar for _, area in pairs(self:getAreasAtPos(pos)) do if area.owner == name or area.open then return true @@ -86,11 +119,13 @@ function areas:canInteract(pos, name) if plants[wstack] ~= nil and plants[wstack] == node then return true end - - owned = true - else - owned = true + elseif areas.factions_available and area.faction_open then + local faction_name = factions.get_player_faction(area.owner) + if faction_name ~= nil and faction_name == factions.get_player_faction(name) then + return true + end end + owned = true end return not owned end @@ -108,7 +143,7 @@ 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 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. @@ -153,74 +188,3 @@ function areas:canInteractInArea(pos1, pos2, name, allow_open) -- intersecting areas and they are all owned by the player. return true end - - - -function areas:canMakeArea(pos1, pos2, name) --MFF crabman(25/02/2016) fix areas in areas - if name and minetest.check_player_privs(name, self.adminPrivs) then - return true - end - areas:sortPos(pos1, pos2) - - local id_areas_intersect = {} - local areas = self:getAreasIntersectingArea(pos1, pos2) - - if not areas then return true end - - for id, area in pairs(areas) do - if area.owner == name and self:isSubarea(pos1, pos2, id) then - return true - end - if not area.open and not self:isAreaOwner(id, name) then - table.insert(id_areas_intersect, id) - end - end - - if #id_areas_intersect > 0 then - return false, id_areas_intersect[1] - end - - return true -end - - ---MFF crabman(5/03/2016 ) return special area pos if a spawn is set. ---1 party (2 party in beds mod) -function areas:getSpawn(pos) - for _, area in pairs(areas:getAreasAtPos(pos)) do - if area.spawn and area.spawn.x then - return area.spawn - end - end - return nil -end - ---MFF DEBUT crabman(17/09/2015 ) respawn player in special area(event) if a spawn is set. ---1 party (2 party in beds mod) -local dead_players = {} -minetest.register_on_dieplayer(function(player) - local player_name = player:get_player_name() - if not player_name then return end - local pos = player:getpos() - if pos then - dead_players[player_name] = pos - end -end) - - -function areas:onRespawn(player) - local player_name = player:get_player_name() - if not player_name or not dead_players[player_name] then return false end - local pos = dead_players[player_name] - dead_players[player_name] = nil - if pos then - for _, area in pairs(areas:getAreasAtPos(pos)) do - if area.spawn then - player:setpos(area.spawn) - return true - end - end - end - return false -end ---FIN diff --git a/api.md b/api.md new file mode 100644 index 0000000..e243a8a --- /dev/null +++ b/api.md @@ -0,0 +1,48 @@ +Areas mod API +=== + +API list +--- + + * `areas:registerHudHandler(handler)` - Registers a handler to add items to the Areas HUD. See [HUD](#hud). + * `areas:registerOnAdd(func(id, area))` + * `areas:registerOnRemove(func(id))` + * `areas:registerOnMove(func(id, area, pos1, pos2))` + + +HUD +--- + +If you are making a protection mod or a similar mod that adds invisible regions +to the world, and you would like then to show up in the areas HUD element, you +can register a callback to show your areas. + +HUD handler specification: + + * `handler(pos, list)` + * `pos` - The position to check. + * `list` - The list of area HUD elements, this should be modified in-place. + +The area list item is a table containing a list of tables with the following fields: + + * `id` - An identifier for the area. This should be a unique string in the format `mod:id`. + * `name` - The name of the area. + * `owner` - The player name of the region owner, if any. + +All of the fields are optional but at least one of them must be set. + +### Example + + local function areas_hud_handler(pos, areas) + local val = find_my_protection(pos) + + if val then + table.insert(areas, { + id = "mod:"..val.id, + name = val.name, + owner = val.owner, + }) + end + end + + areas:registerHudHandler(areas_hud_handler) diff --git a/chatcommands.lua b/chatcommands.lua index 5b56169..ab918f3 100644 --- a/chatcommands.lua +++ b/chatcommands.lua @@ -1,15 +1,16 @@ +local S = minetest.get_translator("areas") minetest.register_chatcommand("protect", { - params = "", - description = "Protect your own area", + params = S(""), + description = S("Protect your own area"), privs = {[areas.config.self_protection_privilege]=true}, func = function(name, param) if param == "" then - return false, "Invalid usage, see /help protect." + return false, S("Invalid usage, see /help @1.", "protect") end local pos1, pos2 = areas:getPos(name) if not (pos1 and pos2) then - return false, "You need to select an area first." + return false, S("You need to select an area first.") end minetest.log("action", "/protect invoked, owner="..name.. @@ -19,38 +20,37 @@ minetest.register_chatcommand("protect", { local canAdd, errMsg = areas:canPlayerAddArea(pos1, pos2, name) if not canAdd then - return false, "You can't protect that area: "..errMsg + return false, S("You can't protect that area: @1", errMsg) end local id = areas:add(name, param, pos1, pos2, nil) areas:save() - return true, "Area protected. ID: "..id + return true, S("Area protected. ID: @1", id) end }) minetest.register_chatcommand("set_owner", { - params = " ", - description = "Protect an area beetween two positions and give" + params = S("").." "..S(""), + description = S("Protect an area between two positions and give" .." a player access to it without setting the parent of the" - .." area to any existing area", + .." area to any existing area"), privs = areas.adminPrivs, func = function(name, param) local ownerName, areaName = param:match('^(%S+)%s(.+)$') if not ownerName then - return false, "Incorrect usage, see /help set_owner." + return false, S("Invalid usage, see /help @1.", "set_owner") end local pos1, pos2 = areas:getPos(name) if not (pos1 and pos2) then - return false, "You need to select an area first." + return false, S("You need to select an area first.") end if not areas:player_exists(ownerName) then - return false, "The player \"" - ..ownerName.."\" does not exist." + return false, S("The player \"@1\" does not exist.", ownerName) end minetest.log("action", name.." runs /set_owner. Owner = "..ownerName.. @@ -62,34 +62,33 @@ minetest.register_chatcommand("set_owner", { areas:save() minetest.chat_send_player(ownerName, - "You have been granted control over area #".. - id..". Type /list_areas to show your areas.") - return true, "Area protected. ID: "..id + S("You have been granted control over area #@1. ".. + "Type /list_areas to show your areas.", id)) + return true, S("Area protected. ID: @1", id) end }) minetest.register_chatcommand("add_owner", { - params = " ", - description = "Give a player access to a sub-area beetween two" + params = S("").." "..S("").." "..S(""), + description = S("Give a player access to a sub-area beetween two" .." positions that have already been protected," - .." Use set_owner if you don't want the parent to be set.", + .." Use set_owner if you don't want the parent to be set."), func = function(name, param) - local pid, ownerName, areaName - = param:match('^(%d+) ([^ ]+) (.+)$') + local pid, ownerName, areaName = param:match('^(%d+) ([^ ]+) (.+)$') if not pid then - minetest.chat_send_player(name, "Incorrect usage, see /help add_owner") + minetest.chat_send_player(name, S("Invalid usage, see /help @1.", "add_owner")) return end local pos1, pos2 = areas:getPos(name) if not (pos1 and pos2) then - return false, "You need to select an area first." + return false, S("You need to select an area first.") end if not areas:player_exists(ownerName) then - return false, "The player \""..ownerName.."\" does not exist." + return false, S("The player \"@1\" does not exist.", ownerName) end minetest.log("action", name.." runs /add_owner. Owner = "..ownerName.. @@ -101,52 +100,52 @@ minetest.register_chatcommand("add_owner", { pid = tonumber(pid) if (not areas:isAreaOwner(pid, name)) or (not areas:isSubarea(pos1, pos2, pid)) then - return false, "You can't protect that area." + return false, S("You can't protect that area.") end local id = areas:add(ownerName, areaName, pos1, pos2, pid) areas:save() minetest.chat_send_player(ownerName, - "You have been granted control over area #".. - id..". Type /list_areas to show your areas.") - return true, "Area protected. ID: "..id + S("You have been granted control over area #@1. ".. + "Type /list_areas to show your areas.", id)) + return true, S("Area protected. ID: @1", id) end }) minetest.register_chatcommand("rename_area", { - params = " ", - description = "Rename a area that you own", + params = S("").." "..S(""), + description = S("Rename an area that you own"), func = function(name, param) local id, newName = param:match("^(%d+)%s(.+)$") if not id then - return false, "Invalid usage, see /help rename_area." + return false, S("Invalid usage, see /help @1.", "rename_area") end id = tonumber(id) if not id then - return false, "That area doesn't exist." + return false, S("That area doesn't exist.") end if not areas:isAreaOwner(id, name) then - return true, "You don't own that area." + return true, S("You don't own that area.") end areas.areas[id].name = newName areas:save() - return true, "Area renamed." + return true, S("Area renamed.") end }) minetest.register_chatcommand("find_areas", { params = "", - description = "Find areas using a Lua regular expression", + description = S("Find areas using a Lua regular expression"), privs = areas.adminPrivs, func = function(name, param) if param == "" then - return false, "A regular expression is required." + return false, S("A regular expression is required.") end -- Check expression for validity @@ -154,7 +153,7 @@ minetest.register_chatcommand("find_areas", { ("Test [1]: Player (0,0,0) (0,0,0)"):find(param) end if not pcall(testRegExp) then - return false, "Invalid regular expression." + return false, S("Invalid regular expression.") end local matches = {} @@ -167,14 +166,14 @@ minetest.register_chatcommand("find_areas", { if #matches > 0 then return true, table.concat(matches, "\n") else - return true, "No matches found." + return true, S("No matches found.") end end }) minetest.register_chatcommand("list_areas", { - description = "List your areas, or all areas if you are an admin.", + description = S("List your areas, or all areas if you are an admin."), func = function(name, param) local admin = minetest.check_player_privs(name, areas.adminPrivs) local areaStrings = {} @@ -184,7 +183,7 @@ minetest.register_chatcommand("list_areas", { end end if #areaStrings == 0 then - return true, "No visible areas." + return true, S("No visible areas.") end return true, table.concat(areaStrings, "\n") end @@ -192,152 +191,176 @@ minetest.register_chatcommand("list_areas", { minetest.register_chatcommand("recursive_remove_areas", { - params = "", - description = "Recursively remove areas using an id", + params = S(""), + description = S("Recursively remove areas using an ID"), func = function(name, param) local id = tonumber(param) if not id then - return false, "Invalid usage, see" - .." /help recursive_remove_areas" + return false, S("Invalid usage, see" + .." /help @1.", "recursive_remove_areas") end if not areas:isAreaOwner(id, name) then - return false, "Area "..id.." does not exist or is" - .." not owned by you." + return false, S("Area @1 does not exist or is" + .." not owned by you.", id) end areas:remove(id, true) areas:save() - return true, "Removed area "..id.." and it's sub areas." + return true, S("Removed area @1 and it's sub areas.", id) end }) minetest.register_chatcommand("remove_area", { - params = "", - description = "Remove an area using an id", + params = S(""), + description = S("Remove an area using an ID"), func = function(name, param) local id = tonumber(param) if not id then - return false, "Invalid usage, see /help remove_area" + return false, S("Invalid usage, see /help @1.", "remove_area") end if not areas:isAreaOwner(id, name) then - return false, "Area "..id.." does not exist or" - .." is not owned by you." + return false, S("Area @1 does not exist or" + .." is not owned by you.", id) end areas:remove(id) areas:save() - return true, "Removed area "..id + return true, S("Removed area @1", id) end }) minetest.register_chatcommand("change_owner", { - params = " ", - description = "Change the owner of an area using it's ID", + params = S("").." "..S(""), + description = S("Change the owner of an area using its ID"), func = function(name, param) local id, newOwner = param:match("^(%d+)%s(%S+)$") if not id then - return false, "Invalid usage, see" - .." /help change_owner." + return false, S("Invalid usage, see" + .." /help @1.", "change_owner") end if not areas:player_exists(newOwner) then - return false, "The player \""..newOwner - .."\" does not exist." + return false, S("The player \"@1\" does not exist.", newOwner) end id = tonumber(id) if not areas:isAreaOwner(id, name) then - return false, "Area "..id.." does not exist" - .." or is not owned by you." + return false, S("Area @1 does not exist" + .." or is not owned by you.", id) end areas.areas[id].owner = newOwner areas:save() minetest.chat_send_player(newOwner, - ("%s has given you control over the area %q (ID %d).") - :format(name, areas.areas[id].name, id)) - return true, "Owner changed." + S("@1 has given you control over the area \"@2\" (ID @3).", + name, areas.areas[id].name, id)) + return true, S("Owner changed.") end }) minetest.register_chatcommand("area_open", { - params = "", - description = "Toggle an area open (anyone can interact) or closed", + params = S(""), + description = S("Toggle an area open (anyone can interact) or closed"), func = function(name, param) local id = tonumber(param) if not id then - return false, "Invalid usage, see /help area_open." + return false, S("Invalid usage, see /help @1.", "area_open") end if not areas:isAreaOwner(id, name) then - return false, "Area "..id.." does not exist" - .." or is not owned by you." + return false, S("Area @1 does not exist" + .." or is not owned by you.", id) end local open = not areas.areas[id].open -- Save false as nil to avoid inflating the DB. areas.areas[id].open = open or nil areas:save() - return true, ("Area %s."):format(open and "opened" or "closed") + return true, open and S("Area opened.") or S("Area closed.") end }) -minetest.register_chatcommand("area_openfarming", { - params = "", - description = "Toggle an area open (anyone can interact farming) or closed", - func = function(name, param) - local id = tonumber(param) - if not id then - return false, "Invalid usage, see /help area_openfarming." - end - - if not areas:isAreaOwner(id, name) then - return false, "Area "..id.." does not exist" +minetest.register_chatcommand( + "area_openfarming", { + params = "", + description = "Toggle an area as open farming (anyone can harvest and plant) or closed", + func = function(name, param) + local id = tonumber(param) + if not id then + return false, "Invalid usage, see /help area_openfarming." + end + + if not areas:isAreaOwner(id, name) then + return false, "Area "..id.." does not exist" .." or is not owned by you." + end + local open = not areas.areas[id].openfarming + -- Save false as nil to avoid inflating the DB. + areas.areas[id].openfarming = open or nil + areas:save() + return true, ("Area %s to farming."):format(open and "opened" or "closed") end - local openfarming = not areas.areas[id].openfarming - -- Save false as nil to avoid inflating the DB. - areas.areas[id].openfarming = openfarming or nil - areas:save() - return true, ("Area %s to farming."):format(openfarming and "opened" or "closed") - end }) +if areas.factions_available then + minetest.register_chatcommand("area_faction_open", { + params = S(""), + description = S("Toggle an area open/closed for members in your faction."), + func = function(name, param) + local id = tonumber(param) + if not id then + return false, S("Invalid usage, see /help @1.", "area_faction_open") + end + + if not areas:isAreaOwner(id, name) then + return false, S("Area @1 does not exist" + .." or is not owned by you.", id) + end + local open = not areas.areas[id].faction_open + -- Save false as nil to avoid inflating the DB. + areas.areas[id].faction_open = open or nil + areas:save() + return true, open and S("Area opened for faction members.") + or S("Area closed for faction members.") + end + }) +end + minetest.register_chatcommand("move_area", { - params = "", - description = "Move (or resize) an area to the current positions.", + params = S(""), + description = S("Move (or resize) an area to the current positions."), privs = areas.adminPrivs, func = function(name, param) local id = tonumber(param) if not id then - return false, "Invalid usage, see /help move_area." + return false, S("Invalid usage, see /help @1.", "move_area") end local area = areas.areas[id] if not area then - return false, "Area does not exist." + return false, S("Area does not exist.") end local pos1, pos2 = areas:getPos(name) if not pos1 then - return false, "You need to select an area first." + return false, S("You need to select an area first.") end areas:move(id, area, pos1, pos2) areas:save() - return true, "Area successfully moved." + return true, S("Area successfully moved.") end, }) minetest.register_chatcommand("area_info", { - description = "Get information about area configuration and usage.", + description = S("Get information about area configuration and usage."), func = function(name, param) local lines = {} local privs = minetest.get_player_privs(name) @@ -359,27 +382,24 @@ minetest.register_chatcommand("area_info", { local max_size = has_high_limit and size_limit_high or size_limit - -- Privilege information - local self_prot_line = ("Self protection is %sabled"):format( - self_prot and "en" or "dis") - if self_prot and prot_priv then - self_prot_line = self_prot_line.. - (" %s have the neccessary privilege (%q).") - :format( - has_prot_priv and "and you" or - "but you don't", - prot_priv) - else - self_prot_line = self_prot_line.."." - end + -- Self protection information + local self_prot_line = self_prot and S("Self protection is enabled.") or + S("Self protection is disabled.") table.insert(lines, self_prot_line) + -- Privilege information + local priv_line = has_prot_priv and + S("You have the necessary privilege (\"@1\").", prot_priv) or + S("You don't have the necessary privilege (\"@1\").", prot_priv) + table.insert(lines, priv_line) if privs.areas then - table.insert(lines, "You are an area".. - " administrator (\"areas\" privilege).") + table.insert(lines, S("You are an area".. + " administrator (\"areas\" privilege).")) elseif has_high_limit then table.insert(lines, - "You have extended area protection".. - " limits (\"areas_high_limit\" privilege).") + S("You have extended area protection".. + " limits (\"areas_high_limit\" privilege).")) + elseif privs.megabuilder then + table.insert(lines, "You are a megabuilder (\"megabuilder\" privilege).") end -- Area count @@ -389,26 +409,23 @@ minetest.register_chatcommand("area_info", { area_num = area_num + 1 end end - local count_line = ("You have %d area%s"):format( - area_num, area_num == 1 and "" or "s") - if privs.areas then - count_line = count_line.. - " and have no area protection limits." - elseif can_prot then - count_line = count_line..(", out of a maximum of %d.") - :format(max_count) - end - table.insert(lines, count_line) + table.insert(lines, S("You have @1 areas.", area_num)) + + -- Area limit + local area_limit_line = (privs.areas or privs.megabuilder) and + S("Limit: no area count limit") or + S("Limit: @1 areas", max_count) + table.insert(lines, area_limit_line) -- Area size limits local function size_info(str, size) - table.insert(lines, ("%s spanning up to %dx%dx%d.") - :format(str, size.x, size.y, size.z)) + table.insert(lines, S("@1 spanning up to @2x@3x@4.", + str, size.x, size.y, size.z)) end - local function priv_limit_info(priv, max_count, max_size) - size_info(("Players with the %q privilege".. - " can protect up to %d areas"):format( - priv, max_count), max_size) + local function priv_limit_info(lpriv, lmax_count, lmax_size) + size_info(S("Players with the \"@1\" privilege".. + " can protect up to @2 areas", lpriv, lmax_count), + lmax_size) end if self_prot then if privs.areas then @@ -416,8 +433,11 @@ minetest.register_chatcommand("area_info", { limit, size_limit) priv_limit_info("areas_high_limit", limit_high, size_limit_high) + table.insert(lines, "Players with the \"megabuilder\" privilege can protect unlimited areas in size and number.") + elseif privs.megabuilder then + table.insert(lines, "You can protect areas unlimited in size and number.") elseif has_prot_priv then - size_info("You can protect areas", max_size) + size_info(S("You can protect areas"), max_size) end end @@ -426,62 +446,26 @@ minetest.register_chatcommand("area_info", { }) ---MFF DEBUT crabman(17/09/2015 ) respawn player at in special area(event) if a spawn is set. -minetest.register_chatcommand("area_addspawn", { - params = "", - privs = areas.adminPrivs, - description = "Define special spawn for area", - func = function(name, param) - local id = param:match("^(%d+)") - if not id then - return false, "Invalid usage, see /help area_addspawn." - end +minetest.register_chatcommand("areas_cleanup", { + description = S("Removes all ownerless areas"), + privs = areas.adminPrivs, + func = function() + local total, count = 0, 0 - id = tonumber(id) - if not id then - return false, "Error, Param id must be int." - end + local aareas = areas.areas + for id, _ in pairs(aareas) do + local owner = aareas[id].owner - local player = minetest.get_player_by_name(name) - if not player then - return false, "Error, there is not player" - end - local pos = player:getpos() - if not pos then - return false, "Error, there is not pos." - end + if not areas:player_exists(owner) then + areas:remove(id) + count = count + 1 + end - if not areas.areas[id] then - return false, "Area ".. id .." does not exist." + total = total + 1 end - areas.areas[id].spawn = pos areas:save() - return true, "spawn of area ".. id .." defined." + + return true, "Total areas: " .. total .. ", Removed " .. + count .. " areas. New count: " .. (total - count) end }) - -minetest.register_chatcommand("area_delspawn", { - params = "", - privs = areas.adminPrivs, - description = "Delete special spawn of area", - func = function(name, param) - local id = param:match("^(%d+)") - if not id then - return false, "Invalid usage, see /help area_delspawn." - end - - id = tonumber(id) - if not id then - return false, "Error, Param id must be int." - end - - if not areas.areas[id] then - return false, "Area ".. id .." does not exist." - end - areas.areas[id].spawn = nil - areas:save() - return true, "spawn of area ".. id .." deleted." - end -}) --- FIN - diff --git a/hud.lua b/hud.lua index 75d3188..79ef907 100644 --- a/hud.lua +++ b/hud.lua @@ -1,85 +1,70 @@ -- This is inspired by the landrush mod by Bremaweb - +local S = minetest.get_translator("areas") areas.hud = {} +areas.hud.refresh = 0 + +minetest.register_globalstep(function(dtime) + areas.hud.refresh = areas.hud.refresh + dtime + if areas.hud.refresh > areas.config["tick"] then + areas.hud.refresh = 0 + else + return + end -local function tick() for _, player in pairs(minetest.get_connected_players()) do local name = player:get_player_name() - local pos = vector.round(player:getpos()) - local area_text = "No area(s)\n\n" - local area_owner_name = "" - local mod_owner = 0 - local mod_open = 0 - local mod_farming = 0 - local area_name = "" - local nb_areas = 0 + local pos = vector.round(player:get_pos()) + pos = vector.apply(pos, function(p) + return math.max(math.min(p, 2147483), -2147483) + end) + local areaStrings = {} + for id, area in pairs(areas:getAreasAtPos(pos)) do - nb_areas = nb_areas+1 - if areas:isAreaOwner(id, name) then - mod_owner = 1 - end - - if area.open then - mod_open = 1 - end - if area.openfarming then - mod_farming = 1 - end - - if not area.parent then - area_owner_name = area.owner - area_name = area.name - end + local faction_info = area.faction_open and areas.factions_available and + factions.get_player_faction(area.owner) + area.faction_open = faction_info + table.insert(areaStrings, ("%s [%u] (%s%s%s)") + :format(area.name, id, area.owner, + area.open and S(":open") or area.openfarming and ":openfarming" or "", + faction_info and ":"..faction_info or "")) end - local icon = "areas_not_area.png" - if nb_areas > 0 then - local plural = "" - if nb_areas > 1 then - plural = "s" - end - area_text = ("%s\nOwner: %s\n%u area" .. plural):format(area_name, area_owner_name, nb_areas) - icon = ("areas_%u_%u_%u.png"):format(mod_owner, mod_open, mod_farming) + for i, area in pairs(areas:getExternalHudEntries(pos)) do + local str = "" + if area.name then str = area.name .. " " end + if area.id then str = str.."["..area.id.."] " end + if area.owner then str = str.."("..area.owner..")" end + table.insert(areaStrings, str) end - if not areas.hud[name] then - areas.hud[name] = {} - areas.hud[name].icon = player:hud_add({ - hud_elem_type = "image", - position = {x=0,y=1}, - scale = {x=1,y=1}, - offset = {x=26,y=-60}, - text = icon, - }) - areas.hud[name].areas_id = player:hud_add({ + local areaString = S("Areas:") + if #areaStrings > 0 then + areaString = areaString.."\n".. + table.concat(areaStrings, "\n") + end + local hud = areas.hud[name] + if not hud then + hud = {} + areas.hud[name] = hud + hud.areasId = player:hud_add({ hud_elem_type = "text", name = "Areas", number = 0xFFFFFF, position = {x=0, y=1}, - offset = {x=48, y=-40}, - text = area_text, - scale = {x=1, y=1}, + offset = {x=8, y=-8}, + text = areaString, + scale = {x=200, y=60}, alignment = {x=1, y=-1}, }) - areas.hud[name].old_area_text = area_text - areas.hud[name].old_icon = icon - else - if areas.hud[name].old_area_text ~= area_text then - player:hud_change(areas.hud[name].areas_id, "text", area_text) - areas.hud[name].old_area_text = area_text - end - if areas.hud[name].old_icon ~= icon then - player:hud_change(areas.hud[name].icon, "text", icon) - areas.hud[name].old_icon = icon - end + hud.oldAreas = areaString + return + elseif hud.oldAreas ~= areaString then + player:hud_change(hud.areasId, "text", areaString) + hud.oldAreas = areaString end end - minetest.after(1.5, tick) -end - -tick() +end) minetest.register_on_leaveplayer(function(player) areas.hud[player:get_player_name()] = nil end) - diff --git a/init.lua b/init.lua index 8f59a3e..d94d453 100644 --- a/init.lua +++ b/init.lua @@ -4,6 +4,8 @@ areas = {} +areas.factions_available = minetest.global_exists("factions") + areas.adminPrivs = {areas=true} areas.startTime = os.clock() @@ -23,7 +25,11 @@ minetest.register_privilege("areas", { description = "Can administer areas." }) minetest.register_privilege("areas_high_limit", { - description = "Can can more, bigger areas." + description = "Can protect more, bigger areas." +}) +-- Mega_builder privilege -- MFF +minetest.register_privilege("megabuilder", { + description = "Can protect an infinite amount of areas." }) if not minetest.registered_privileges[areas.config.self_protection_privilege] then @@ -32,8 +38,7 @@ if not minetest.registered_privileges[areas.config.self_protection_privilege] th }) end -if minetest.setting_getbool("log_mods") then +if minetest.settings:get_bool("log_mods") then local diffTime = os.clock() - areas.startTime minetest.log("action", "areas loaded in "..diffTime.."s.") end - diff --git a/interact.lua b/interact.lua index 2e54800..9cd44e2 100644 --- a/interact.lua +++ b/interact.lua @@ -1,3 +1,4 @@ +local S = minetest.get_translator("areas") local old_is_protected = minetest.is_protected function minetest.is_protected(pos, name) @@ -11,9 +12,8 @@ minetest.register_on_protection_violation(function(pos, name) if not areas:canInteract(pos, name) then local owners = areas:getNodeOwners(pos) minetest.chat_send_player(name, - ("%s is protected by %s."):format( + S("@1 is protected by @2.", minetest.pos_to_string(pos), table.concat(owners, ", "))) end end) - diff --git a/internal.lua b/internal.lua index 2da3228..a1b2ee7 100644 --- a/internal.lua +++ b/internal.lua @@ -1,10 +1,21 @@ --- Mega_builder privilege -minetest.register_privilege("megabuilder","Can protect an infinite amount of areas.") +local S = minetest.get_translator("areas") function areas:player_exists(name) return minetest.get_auth_handler().get_auth(name) ~= nil end +local safe_file_write = minetest.safe_file_write +if safe_file_write == nil then + function safe_file_write(path, content) + local file, err = io.open(path, "w") + if err then + return err + end + file:write(content) + file:close() + end +end + -- Save the areas table to a file function areas:save() local datastr = minetest.serialize(self.areas) @@ -12,12 +23,7 @@ function areas:save() minetest.log("error", "[areas] Failed to serialize area data!") return end - local file, err = io.open(self.config.filename, "w") - if err then - return err - end - file:write(datastr) - file:close() + return safe_file_write(self.config.filename, datastr) end -- Load the areas table from the save file @@ -42,7 +48,7 @@ end 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.") + .."area! Falling back to iterative area checking.") self.store = nil self.store_ids = nil end @@ -88,6 +94,11 @@ function areas:add(owner, name, pos1, pos2, parent) owner = owner, parent = parent } + + for i=1, #areas.registered_on_adds do + areas.registered_on_adds[i](id, self.areas[id]) + end + -- Add to AreaStore if self.store then local sid = self.store:insert_area(pos1, pos2, tostring(id)) @@ -120,6 +131,10 @@ function areas:remove(id, recurse) end end + for i=1, #areas.registered_on_removes do + areas.registered_on_removes[i](id) + end + -- Remove main entry self.areas[id] = nil @@ -135,6 +150,10 @@ function areas:move(id, area, pos1, pos2) area.pos1 = pos1 area.pos2 = pos2 + for i=1, #areas.registered_on_moves do + areas.registered_on_moves[i](id, area, pos1, pos2) + end + if self.store then self.store:remove_area(areas.store_ids[id]) local sid = self.store:insert_area(pos1, pos2, tostring(id)) @@ -193,22 +212,22 @@ function areas:canPlayerAddArea(pos1, pos2, name) -- and if the area is too big. if not self.config.self_protection or not privs[areas.config.self_protection_privilege] then - return false, "Self protection is disabled or you do not have" - .." the necessary privilege." + return false, S("Self protection is disabled or you do not have" + .." the necessary privilege.") end - -- MFF: megabuilders skip checks on size and number of areas. - if not privs["megabuilder"] then - -- Check size + -- MFF: megabuilders skip checks on size and number of areas + 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 + self.config.self_protection_max_size_high or + self.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, "Area is too big." + (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.") end + end -- Check number of areas the user has and make sure it not above the max local count = 0 @@ -218,22 +237,20 @@ function areas:canPlayerAddArea(pos1, pos2, name) end end local max_areas = privs.areas_high_limit and - self.config.self_protection_max_areas_high or - self.config.self_protection_max_areas - if count >= max_areas then - return false, "You have reached the maximum amount of" - .." areas that you are allowed to protect." - end + self.config.self_protection_max_areas_high or + self.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 -- Check intersecting areas - local can, id = self:canMakeArea(pos1, pos2, name) --MFF crabman(25/02/2016) fix areas in areas + local can, id = self:canInteractInArea(pos1, pos2, name) if not can then local area = self.areas[id] - return false, ("The area intersects with %s [%u] (%s).") - :format(area.name, id, area.owner) + return false, S("The area intersects with @1 [@2] (@3).", + area.name, id, area.owner) end - return true end @@ -288,4 +305,3 @@ function areas:isAreaOwner(id, name) end return false end - diff --git a/legacy.lua b/legacy.lua index 83b3d27..2f333fe 100644 --- a/legacy.lua +++ b/legacy.lua @@ -1,25 +1,26 @@ -- This file contains functions to convert from -- the old areas format and other compatability code. +local S = minetest.get_translator("areas") minetest.register_chatcommand("legacy_load_areas", { - params = "", - description = "Loads, converts, and saves the areas from" - .." a legacy save file.", + params = S(""), + description = S("Loads, converts, and saves the areas from" + .." a legacy save file."), privs = {areas=true, server=true}, func = function(name, param) - minetest.chat_send_player(name, "Converting areas...") + minetest.chat_send_player(name, S("Converting areas…")) local version = tonumber(param) if version == 0 then - err = areas:node_ownership_load() + local err = areas:node_ownership_load() if err then - minetest.chat_send_player(name, "Error loading legacy file: "..err) + minetest.chat_send_player(name, S("Error loading legacy file: @1", err)) return end else - minetest.chat_send_player(name, "Invalid version number. (0 allowed)") + minetest.chat_send_player(name, S("Invalid version number. (0 allowed)")) return end - minetest.chat_send_player(name, "Legacy file loaded.") + minetest.chat_send_player(name, S("Legacy file loaded.")) for k, area in pairs(areas.areas) do -- New position format @@ -34,20 +35,21 @@ minetest.register_chatcommand("legacy_load_areas", { areas:sortPos(area.pos1, area.pos2) -- Add name - area.name = "unnamed" + area.name = S("unnamed") -- Remove ID area.id = nil end - minetest.chat_send_player(name, "Table format updated.") + minetest.chat_send_player(name, S("Table format updated.")) areas:save() - minetest.chat_send_player(name, "Converted areas saved. Done.") + minetest.chat_send_player(name, S("Converted areas saved. Done.")) end }) function areas:node_ownership_load() local filename = minetest.get_worldpath().."/owners.tbl" + local tables, err tables, err = loadfile(filename) if err then return err @@ -104,35 +106,3 @@ function areas.hasOwner(pos) end return false end - -IsPlayerNodeOwner = areas.isNodeOwner -GetNodeOwnerName = areas.getNodeOwnerName -HasOwner = areas.hasOwner - --- This is entirely untested and may break in strange and new ways. -if areas.config.legacy_table then - owner_defs = setmetatable({}, { - __index = function(table, key) - local a = rawget(areas.areas, key) - if not a then return a end - local b = {} - for k, v in pairs(a) do b[k] = v end - b.x1, b.y1, b.z1 = b.pos1.x, b.pos1.y, b.pos1.z - b.x2, b.y1, b.z2 = b.pos2.x, b.pos2.y, b.pos2.z - b.pos1, b.pos2 = nil, nil - b.id = key - return b - end, - __newindex = function(table, key, value) - local a = value - a.pos1, a.pos2 = {x=a.x1, y=a.y1, z=a.z1}, - {x=a.x2, y=a.y2, z=a.z2} - a.x1, a.y1, a.z1, a.x2, a.y2, a.z2 = - nil, nil, nil, nil, nil, nil - a.name = a.name or "unnamed" - a.id = nil - return rawset(areas.areas, key, a) - end - }) -end - diff --git a/locale/areas.fr.tr b/locale/areas.fr.tr new file mode 100644 index 0000000..4ca4c8d --- /dev/null +++ b/locale/areas.fr.tr @@ -0,0 +1,125 @@ +# textdomain: areas + + +### chatcommands.lua ### + += += += += += +@1 has given you control over the area "@2" (ID @3).=@1 vous a donné le contrôle de la zone "@2" (ID @3). +@1 spanning up to @2x@3x@4.=@1 s’étendant jusqu’à @2x@3x@4. +A regular expression is required.=Une expression régulière est requise. +Area @1 does not exist or is not owned by you.=La zone @1 n’existe pas ou ne vous appartient pas. +Area closed for faction members.=Zone fermée aux membres de la faction. +Area closed.=Zone fermée. +Area does not exist.=La zone n’existe pas. +Area opened for faction members.=Zone ouverte aux membres de la faction. +Area opened.=Zone ouverte. +Area protected. ID: @1=Zone protégée. ID : @1 +Area renamed.=Zone renommée. +Area successfully moved.=Zone déplacée avec succès. +Change the owner of an area using its ID=Change le propriétaire d’une zone en utilisant son ID. +Find areas using a Lua regular expression=Trouve les zones en utilisant une expression régulière Lua. +Get information about area configuration and usage.=Obtient des informations sur la configuration des zones et l’utilisation des zones. + +Give a player access to a sub-area beetween two positions that have already been protected, Use set_owner if you don't want the parent to be set.=Donne au joueur accès aux sous-zones entre deux positions qui ont déjà été protégées ; utilisez set_owner si vous ne voulez pas que la zone pricipale soit définie. + +Invalid regular expression.=Expression régulière invalide. +Limit: @1 areas=Limite: @1 zones. +Limit: no area count limit=Limite: pas de limite de nombre de zones. +List your areas, or all areas if you are an admin.=Liste vos zones, ou toutes les zones si vous êtes administrateur. +Move (or resize) an area to the current positions.=Déplace (ou redimensionne) une zone aux positions actuelles. +No matches found.=Aucun résultat. +No visible areas.=Pas de zone visible. +Owner changed.=Propriétaire changé. +Players with the "@1" privilege can protect up to @2 areas=Les joueurs avec le privilège "@1" peuvent protéger jusqu’à @2 zones + +Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=Protège une zone entre deux positions et donne à un joueur accès à cette zone sans définir la zone principale de cette zone ni aucune zone existante. + +Protect your own area=Protège votre zone. +Recursively remove areas using an ID=Supprime les zones récursivement en utilisant un ID. +Remove an area using an ID=Supprime une zone en utilisant son ID. +Removed area @1=Zone @1 supprimée. +Removed area @1 and it's sub areas.=Zone @1 et ses sous-zones supprimées. +Rename an area that you own=Renomme une zone qui vous appartient. +Self protection is disabled.=L’autoprotection est désactivée. +Self protection is enabled.=L’autoprotection est activée. +That area doesn't exist.=La zone n’existe pas. +The player "@1" does not exist.=Le joueur "@1" n’existe pas. +Toggle an area open (anyone can interact) or closed=Bascule entre zone ouverte (tout le monde peut intéragir) ou fermée. +Toggle an area open/closed for members in your faction.=Bascule entre zone ouverte/fermée pour les membres de votre faction. +You are an area administrator ("areas" privilege).=Vous êtes un administrateur de zone (privilège "areas"). +You can protect areas=Vous pouvez protéger des zones. +You can't protect that area.=Vous ne pouvez pas protéger cette zone. +You can't protect that area: @1=Vous ne pouvez pas protéger cette zone : @1. +You don't have the necessary privilege ("@1").=Vous n’avez pas le privilège nécessaire ("@1"). +You don't own that area.=Vous ne possédez pas cette zone. +You have @1 areas.=Vous avez @1 zones. + +You have been granted control over area #@1. Type /list_areas to show your areas.=Vous avez reçu l’autorisation de contrôler la zone #@1. + +You have extended area protection limits ("areas_high_limit" privilege).=Votre limite de protection de zones est étendue (privilège "areas_high_limit"). + +You have the necessary privilege ("@1").=Vous avez le privilège nécessaire ("@1"). +You need to select an area first.=Vous devez sélectionner une zone d’abord. + +### chatcommands.lua ### +### pos.lua ### + += +Invalid usage, see /help @1.=Utilisation incorrecte, voir /help @1. + +### hud.lua ### + +:open= : ouverte +Areas:=Zones : + +### interact.lua ### + +@1 is protected by @2.=@1 est protégée par @2. + +### internal.lua ### + +Area is too big.=La zone est trop grande. + +Self protection is disabled or you do not have the necessary privilege.=L’autoprotection est désactivée ou vous n’avez pas le privilège nécessaire. + +The area intersects with @1 [@2] (@3).=La zone a une intersection avec @1 [@2] (@3). + +You have reached the maximum amount of areas that you are allowed to protect.=Vous avez atteint le nombre maximum de zones que vous êtes autorisé à protéger. + + +### legacy.lua ### + += +Converted areas saved. Done.=Zones converties sauvegardées. Fait. +Converting areas…=Conversion des zones… +Error loading legacy file: @1=Erreur lors du chargement du fichier : @1 +Invalid version number. (0 allowed)=Numéro de version invalide. (0 autorisé) +Legacy file loaded.=Fichier obsolète chargé. + +Loads, converts, and saves the areas from a legacy save file.=Charge, fait la conversion et sauvegarde les zones depuis un fichier de sauvegarde obsolète. + +Table format updated.=Format de tableau mis à jour. +unnamed=Non nommé + +### pos.lua ### + += +Area @1 selected.=Zone @1 sélectionnée. +Area position @1 set to @2=Position @1 de la zone définie à @2. +Position @1 set to @2=Position @1 définie à @2. +Position @1: =Position @1 : +Select an area by ID.=Sélectionnez une zone par son ID. +Select position @1 by punching a node.=Sélectionnez une position en frappant un bloc. +Select positions by punching two nodes.=Sélectionnez une position en frappant deux blocs. + +Set area protection region position @1 to your location or the one specified=Définit la position @1 de la région de protection de zone à votre position ou à celle spécifiée. + +Set area protection region, position 1, or position 2 by punching nodes, or display the region=Définit la région de protection de zone, la position 1, ou la position 2 en frappant des blocs, ou en affichant la région. + +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 diff --git a/locale/areas.it.tr b/locale/areas.it.tr new file mode 100644 index 0000000..009697c --- /dev/null +++ b/locale/areas.it.tr @@ -0,0 +1,125 @@ +# textdomain: areas + + +### chatcommands.lua ### + += += += += += +@1 has given you control over the area "@2" (ID @3).=@1 ti ha dato il controllo sull'area "@2" (ID @3). +@1 spanning up to @2x@3x@4.=@1 si estende fino a @2x@3@4. +A regular expression is required.=È necessaria una espressione regolare. +Area @1 does not exist or is not owned by you.=L'area @1 non esiste o non è di tua proprietà. +Area closed for faction members.=Area chiusa per i membri della fazione. +Area closed.=Area chiusa. +Area does not exist.=L'area non esiste. +Area opened for faction members.=Area aperta per i membri della fazione. +Area opened.=Area aperta. +Area protected. ID: @1=Area protetta. ID: @1 +Area renamed.=Area rinominata. +Area successfully moved.=Area spostata con successo. +Change the owner of an area using its ID=Cambia il proprietario di un'area usando il suo ID +Find areas using a Lua regular expression=Trova aree usando una espressione regolare Lua +Get information about area configuration and usage.=Ottieni informazioni sulla configurazione e l'uso delle aree. + +Give a player access to a sub-area beetween two positions that have already been protected, Use set_owner if you don't want the parent to be set.=Dai a un giocatore l'accesso a una sotto-area tra due posizioni che sono già state protette, usa set_owner se non vuoi che sia impostato il parent. + +Invalid regular expression.=Espressione regolare non valida. +Limit: @1 areas=Limite: @1 aree +Limit: no area count limit=Limite: nessun limite al numero delle aree +List your areas, or all areas if you are an admin.=Elenca le tue aree, o tutte le aree se sei un amministratore. +Move (or resize) an area to the current positions.=Sposta (o ridimensiona) un'area alle posizioni attuali. +No matches found.=Nessuna corrispondenza trovata. +No visible areas.=Nessuna area visibile. +Owner changed.=Proprietario cambiato. +Players with the "@1" privilege can protect up to @2 areas=I giocatori col privilegio "@1" possono proteggere fino a @2 aree + +Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=Proteggi un'area tra due posizioni e danne l'accesso a un giocatore senza impostare il parent dell'area a qualsiasi area esistente + +Protect your own area=Proteggi la tua area +Recursively remove areas using an ID=Elimina ricorsivamente delle aree usando un ID +Remove an area using an ID=Elimina un'area usando un ID +Removed area @1=Eliminata l'area @1 +Removed area @1 and it's sub areas.=Eliminata l'area @1 e le sue sotto-aree. +Rename an area that you own=Rinomina un'area che ti appartiene +Self protection is disabled.=L'auto-protezione è disattivata. +Self protection is enabled.=L'auto-protezione è attivata. +That area doesn't exist.=Quell'area non esiste. +The player "@1" does not exist.=Il giocatore "@1" non esiste. +Toggle an area open (anyone can interact) or closed=Apri o chiudi un'area (chiunque può interagirvi) +Toggle an area open/closed for members in your faction.=Apri o chiudi un'area per i membri della tua fazione. +You are an area administrator ("areas" privilege).=Sei un amministratore di aree (privilegio "areas") +You can protect areas=Puoi proteggere aree +You can't protect that area.=Non puoi proteggere quell'area. +You can't protect that area: @1=Non puoi proteggere quell'area: @1 +You don't have the necessary privilege ("@1").=Non hai il privilegio necessario ("@1") +You don't own that area.=Non possiedi quell'area. +You have @1 areas.=Hai @1 aree. + +You have been granted control over area #@1. Type /list_areas to show your areas.=Ti è stato concesso il controllo sull'area #@1. Digita /list_areas per mostrare le tue aree. + +You have extended area protection limits ("areas_high_limit" privilege).=Hai limiti di protezione aree estesi (privilegio "areas_high_limit") + +You have the necessary privilege ("@1").=Hai il privilegio necessario ("@1") +You need to select an area first.=Prima devi selezionare un'area. + +### chatcommands.lua ### +### pos.lua ### + += +Invalid usage, see /help @1.=Utilizzo non valido, si veda /help @1. + +### hud.lua ### + +:open=:aperta +Areas:=Aree: + +### interact.lua ### + +@1 is protected by @2.=@1 è protetta da @2. + +### internal.lua ### + +Area is too big.=L'area è troppo grande. + +Self protection is disabled or you do not have the necessary privilege.=L'auto-protezione è disattivata o non possiedi il privilegio necessario. + +The area intersects with @1 [@2] (@3).=L'area interseca con @1 [@2] (@3). + +You have reached the maximum amount of areas that you are allowed to protect.=Hai raggiunto il numero massimo di aree che ti è consentito proteggere. + + +### legacy.lua ### + += +Converted areas saved. Done.=Aree convertite salvate. Fatto. +Converting areas…=Conversione delle aree... +Error loading legacy file: @1=Errore nel caricamento del file precedente: @1 +Invalid version number. (0 allowed)=Numero di versione non valido. (0 permesso) +Legacy file loaded.=File precedente caricato. + +Loads, converts, and saves the areas from a legacy save file.=Carica, converte e salva le aree da un file di salvataggio precedente. + +Table format updated.=Aggiornato il formato della tabella. +unnamed=innominato + +### pos.lua ### + += +Area @1 selected.=Area @1 selezionata. +Area position @1 set to @2=Posizione @1 dell'area impostata a @2 +Position @1 set to @2=Posizione @1 impostata a @2 +Position @1: =Posizione @1: +Select an area by ID.=Scegli un'area tramite l'ID. +Select position @1 by punching a node.=Seleziona la posizione @1 colpendo un nodo. +Select positions by punching two nodes.=Seleziona le posizioni colpendo due nodi. + +Set area protection region position @1 to your location or the one specified=Imposta la protezione area della posizione @1 della regione alla tua posizione o quella specificata + +Set area protection region, position 1, or position 2 by punching nodes, or display the region=Imposta la protezione area della regione, posizione 1, o posizione 2, colpendo due nodi, o mostra la regione + +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 diff --git a/locale/template.txt b/locale/template.txt new file mode 100644 index 0000000..02b9c77 --- /dev/null +++ b/locale/template.txt @@ -0,0 +1,127 @@ +# textdomain: areas + + +### chatcommands.lua ### + += += += += += +@1 has given you control over the area "@2" (ID @3).= +@1 spanning up to @2x@3x@4.= +A regular expression is required.= +Area @1 does not exist or is not owned by you.= +Area closed for faction members.= +Area closed.= +Area does not exist.= +Area opened for faction members.= +Area opened.= +Area protected. ID: @1= +Area renamed.= +Area successfully moved.= +Change the owner of an area using its ID= +Find areas using a Lua regular expression= +Get information about area configuration and usage.= + +Give a player access to a sub-area beetween two positions that have already been protected, Use set_owner if you don't want the parent to be set.= + +Invalid regular expression.= +Limit: @1 areas= +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= + +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= +Removed area @1 and it's sub 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.= +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).= +You can protect areas= +You can't protect that area.= +You can't protect that area: @1= +You don't have the necessary privilege ("@1").= +You don't own that area.= +You have @1 areas.= + +You have been granted control over area #@1. Type /list_areas to show your areas.= + +You have extended area protection limits ("areas_high_limit" privilege).= + +You have the necessary privilege ("@1").= +You need to select an area first.= + +Removes all ownerless areas.= + +### chatcommands.lua ### +### pos.lua ### + += +Invalid usage, see /help @1.= + +### hud.lua ### + +:open= +Areas:= + +### interact.lua ### + +@1 is protected by @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).= + +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= +Invalid version number. (0 allowed)= +Legacy file loaded.= + +Loads, converts, and saves the areas from a legacy save file.= + +Table format updated.= +unnamed= + +### pos.lua ### + += +Area @1 selected.= +Area position @1 set to @2= +Position @1 set to @2= +Position @1: = +Select an area by ID.= +Select position @1 by punching a node.= +Select positions by punching two nodes.= + +Set area protection region position @1 to your location or the one specified= + +Set area protection region, position 1, or position 2 by punching nodes, or display the region= + +The area @1 does not exist.= +Unable to get position.= +Unknown subcommand: @1= diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..e694301 --- /dev/null +++ b/mod.conf @@ -0,0 +1,2 @@ +name = areas +optional_depends = playerfactions diff --git a/pos.lua b/pos.lua index 8d3e6fe..7a9cbd2 100644 --- a/pos.lua +++ b/pos.lua @@ -1,3 +1,4 @@ +local S = minetest.get_translator("areas") -- I could depend on WorldEdit for this, but you need to have the 'worldedit' -- permission to use those commands and you don't have @@ -11,31 +12,41 @@ areas.set_pos = {} areas.pos1 = {} areas.pos2 = {} +local LIMIT = 30992 -- this is due to MAPBLOCK_SIZE=16! + +local function posLimit(pos) + return { + x = math.max(math.min(pos.x, LIMIT), -LIMIT), + y = math.max(math.min(pos.y, LIMIT), -LIMIT), + z = math.max(math.min(pos.z, LIMIT), -LIMIT) + } +end + minetest.register_chatcommand("select_area", { - params = "", - description = "Select a area by id.", + params = S(""), + description = S("Select an area by ID."), func = function(name, param) local id = tonumber(param) if not id then - return false, "Invalid usage, see /help select_area." + return false, S("Invalid usage, see /help @1.", "select_area") end if not areas.areas[id] then - return false, "The area "..id.." does not exist." + return false, S("The area @1 does not exist.", id) end areas:setPos1(name, areas.areas[id].pos1) areas:setPos2(name, areas.areas[id].pos2) - return true, "Area "..id.." selected." + return true, S("Area @1 selected.", id) end, }) minetest.register_chatcommand("area_pos1", { params = "[X Y Z|X,Y,Z]", - description = "Set area protection region position 1 to your" - .." location or the one specified", + description = S("Set area protection region position @1 to your" + .." location or the one specified", "1"), privs = {}, func = function(name, param) - local pos = nil + local pos local found, _, x, y, z = param:find( "^(-?%d+)[, ](-?%d+)[, ](-?%d+)$") if found then @@ -43,26 +54,26 @@ minetest.register_chatcommand("area_pos1", { elseif param == "" then local player = minetest.get_player_by_name(name) if player then - pos = player:getpos() + pos = player:get_pos() else - return false, "Unable to get position." + return false, S("Unable to get position.") end else - return false, "Invalid usage, see /help area_pos1." + return false, S("Invalid usage, see /help @1.", "area_pos1") end - pos = vector.round(pos) + pos = posLimit(vector.round(pos)) areas:setPos1(name, pos) - return true, "Area position 1 set to " - ..minetest.pos_to_string(pos) + return true, S("Area position @1 set to @2", "1", + minetest.pos_to_string(pos)) end, }) minetest.register_chatcommand("area_pos2", { params = "[X Y Z|X,Y,Z]", - description = "Set area protection region position 2 to your" - .." location or the one specified", + description = S("Set area protection region position @1 to your" + .." location or the one specified", "2"), func = function(name, param) - local pos = nil + local pos local found, _, x, y, z = param:find( "^(-?%d+)[, ](-?%d+)[, ](-?%d+)$") if found then @@ -70,50 +81,50 @@ minetest.register_chatcommand("area_pos2", { elseif param == "" then local player = minetest.get_player_by_name(name) if player then - pos = player:getpos() + pos = player:get_pos() else - return false, "Unable to get position." + return false, S("Unable to get position.") end else - return false, "Invalid usage, see /help area_pos2." + return false, S("Invalid usage, see /help @1.", "area_pos2") end - pos = vector.round(pos) + pos = posLimit(vector.round(pos)) areas:setPos2(name, pos) - return true, "Area position 2 set to " - ..minetest.pos_to_string(pos) + return true, S("Area position @1 set to @2", "2", + minetest.pos_to_string(pos)) end, }) minetest.register_chatcommand("area_pos", { params = "set/set1/set2/get", - description = "Set area protection region, position 1, or position 2" - .." by punching nodes, or display the region", + description = S("Set area protection region, position 1, or position 2" + .." by punching nodes, or display the region"), func = function(name, param) if param == "set" then -- Set both area positions areas.set_pos[name] = "pos1" - return true, "Select positions by punching two nodes." + return true, S("Select positions by punching two nodes.") elseif param == "set1" then -- Set area position 1 areas.set_pos[name] = "pos1only" - return true, "Select position 1 by punching a node." + return true, S("Select position @1 by punching a node.", "1") elseif param == "set2" then -- Set area position 2 areas.set_pos[name] = "pos2" - return true, "Select position 2 by punching a node." + return true, S("Select position @1 by punching a node.", "2") elseif param == "get" then -- Display current area positions - local pos1str, pos2str = "Position 1: ", "Position 2: " + local pos1str, pos2str = S("Position @1: ", "1"), S("Position @1: ", "2") if areas.pos1[name] then pos1str = pos1str..minetest.pos_to_string(areas.pos1[name]) else - pos1str = pos1str.."" + pos1str = pos1str..S("") end if areas.pos2[name] then pos2str = pos2str..minetest.pos_to_string(areas.pos2[name]) else - pos2str = pos2str.."" + pos2str = pos2str..S("") end return true, pos1str.."\n"..pos2str else - return false, "Unknown subcommand: "..param + return false, S("Unknown subcommand: @1", param) end end, }) @@ -130,12 +141,12 @@ function areas:getPos(playerName) end function areas:setPos1(playerName, pos) - areas.pos1[playerName] = pos + areas.pos1[playerName] = posLimit(pos) areas.markPos1(playerName) end function areas:setPos2(playerName, pos) - areas.pos2[playerName] = pos + areas.pos2[playerName] = posLimit(pos) areas.markPos2(playerName) end @@ -149,22 +160,22 @@ minetest.register_on_punchnode(function(pos, node, puncher) areas.markPos1(name) areas.set_pos[name] = "pos2" minetest.chat_send_player(name, - "Position 1 set to " - ..minetest.pos_to_string(pos)) + S("Position @1 set to @2", "1", + minetest.pos_to_string(pos))) elseif areas.set_pos[name] == "pos1only" then areas.pos1[name] = pos areas.markPos1(name) areas.set_pos[name] = nil minetest.chat_send_player(name, - "Position 1 set to " - ..minetest.pos_to_string(pos)) + S("Position @1 set to @2", "1", + minetest.pos_to_string(pos))) elseif areas.set_pos[name] == "pos2" then areas.pos2[name] = pos areas.markPos2(name) areas.set_pos[name] = nil minetest.chat_send_player(name, - "Position 2 set to " - ..minetest.pos_to_string(pos)) + S("Position @1 set to @2", "2", + minetest.pos_to_string(pos))) end end end) @@ -252,4 +263,3 @@ minetest.register_entity("areas:pos2", { areas.marker2[name] = nil end, }) - diff --git a/settings.lua b/settings.lua index 140a655..17d290c 100644 --- a/settings.lua +++ b/settings.lua @@ -2,42 +2,44 @@ local world_path = minetest.get_worldpath() areas.config = {} -local function setting(tp, name, default) - local full_name = "areas."..name +local function setting(name, tp, default) + local full_name = "areas." .. name local value - if tp == "boolean" then - value = minetest.setting_getbool(full_name) + if tp == "bool" then + value = minetest.settings:get_bool(full_name) + default = value == nil and minetest.is_yes(default) elseif tp == "string" then - value = minetest.setting_get(full_name) - elseif tp == "position" then + value = minetest.settings:get(full_name) + elseif tp == "v3f" then value = minetest.setting_get_pos(full_name) - elseif tp == "number" then - value = tonumber(minetest.setting_get(full_name)) + default = value == nil and minetest.string_to_pos(default) + elseif tp == "float" or tp == "int" then + value = tonumber(minetest.settings:get(full_name)) + local v, other = default:match("^(%S+) (.+)") + default = value == nil and tonumber(other and v or default) else - error("Invalid setting type!") + error("Cannot parse setting type " .. tp) end + if value == nil then value = default + assert(default ~= nil, "Cannot parse default for " .. full_name) end + --print("add", name, default, value) areas.config[name] = value end +local file = io.open(areas.modpath .. "/settingtypes.txt", "r") +for line in file:lines() do + local name, tp, value = line:match("^areas%.(%S+) %(.*%) (%S+) (.*)") + if value then + setting(name, tp, value) + end +end +file:close() + -------------- -- Settings -- -------------- -setting("string", "filename", world_path.."/areas.dat") - --- Allow players with a privilege create their own areas --- within the maximum size and number. -setting("boolean", "self_protection", false) -setting("string", "self_protection_privilege", "interact") -setting("position", "self_protection_max_size", {x=64, y=128, z=64}) -setting("number", "self_protection_max_areas", 4) --- For players with the areas_high_limit privilege. -setting("position", "self_protection_max_size_high", {x=512, y=512, z=512}) -setting("number", "self_protection_max_areas_high", 32) - --- legacy_table (owner_defs) compatibility. Untested and has known issues. -setting("boolean", "legacy_table", false) - +setting("filename", "string", world_path.."/areas.dat") diff --git a/settingtypes.txt b/settingtypes.txt new file mode 100644 index 0000000..9abffa4 --- /dev/null +++ b/settingtypes.txt @@ -0,0 +1,37 @@ +# This file is parsed in "settings.lua". Check regex first. + +# Static paths do not work well with settings +#areas.filename (Configuration file path) string (world_path)/areas.dat + +# Allow players with a privilege create their own areas using /protect +# within the specified size and amount limits. +areas.self_protection (Self protection) bool false + +# Self protection: Privilege required to protect an area +areas.self_protection_privilege (Self protection: Required privs) string interact + +# Refresh delay for the name displays in the HUD in seconds +areas.tick (HUD update delay) float 0.5 0 100 + +# Enable the legacy owner_defs metatable mode. Untested and possibly unstable +areas.legacy_table (Legacy owner_defs metatable) bool false + +[Self protection (normal)] + +# Self protection (normal): Maximal size of the protectable area +# Only enter positive whole numbers for the coordinate values or you'll mess up stuff. +areas.self_protection_max_size (Maximal area size) v3f (64, 128, 64) + +# Self protection (normal): Maximal amount of protected areas per player +areas.self_protection_max_areas (Maximal area count) int 4 + +[Self protection (high)] + +# Self protection (normal): Maximal size of the protectable area +# This setting applies for plyaers with the privilege 'areas_high_limit' +areas.self_protection_max_size_high (Maximal area size) v3f (512, 512, 512) + +# Self protection (normal): Maximal amount of protected areas per player +# Only enter positive whole numbers for the coordinate values or you'll mess up stuff. +# This setting applies for plyaers with the privilege 'areas_high_limit' +areas.self_protection_max_areas_high (Maximal area count) float 32