diff --git a/README.md b/README.md index 15d72e69..97b02032 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ The game includes the mods from the default [minetest_game](https://github.com/m * admin/ * [alternode][] ([MIT][lic.alternode]) -- version: [1.3][ver.alternode] *2021-05-18* - * [cleaner][] ([MIT][lic.cleaner]) -- version: [9fa1b2b Git][ver.cleaner] *2021-07-20* + * [cleaner][] ([MIT][lic.cleaner]) -- version: [182726e Git][ver.cleaner] *2021-07-29* * [no_fall_damage][] ([MIT][lic.no_fall_damage]) -- version [1.0.0][ver.no_fall_damage] *2020-12-19* * [spectator_mode][] ([WTFPL][lic.spectator_mode]) -- version: [3648371 Git][ver.spectator_mode] *2020-07-15* * [whitelist][] ([MIT][lic.whitelist]) -- version: [1.1][ver.whitelist] *2021-06-09* @@ -580,7 +580,7 @@ The game includes the mods from the default [minetest_game](https://github.com/m [ver.castle_weapons]: https://github.com/minetest-mods/castle_weapons/tree/d3cf095 [ver.chatlog]: https://github.com/AntumMT/mod-chatlog/tree/v1.1 [ver.christmas]: https://github.com/TheZenKitteh/minetest-christmas/tree/d3bd872 -[ver.cleaner]: https://github.com/AntumMT/mod-cleaner/tree/9fa1b2b +[ver.cleaner]: https://github.com/AntumMT/mod-cleaner/tree/182726e [ver.cmer]: https://github.com/AntumMT/mod-cmer/tree/2843875 [ver.cmer_chicken]: http://github.com/AntumMT/mod-cmer/tree/085706f [ver.cmer_ghost]: https://github.com/AntumMT/mod-cmer/tree/2f70906 diff --git a/mods/admin/cleaner/README.md b/mods/admin/cleaner/README.md index b3d2c52b..970dd5bf 100644 --- a/mods/admin/cleaner/README.md +++ b/mods/admin/cleaner/README.md @@ -6,7 +6,11 @@ A [Minetest][] mod that can be used to remove/replace unknown entities, nodes, & ### Licensing: -[MIT](LICENSE.txt) +- Code: [MIT](LICENSE.txt) +- Textures: CC0 +- Sounds: + - cleaner_pencil_write: [CC0](https://freesound.org/people/NachtmahrTV/sounds/571800/) + - cleaner_pencil_erase: [CC0](https://freesound.org/people/damsur/sounds/443241/) ### Requirements: diff --git a/mods/admin/cleaner/TODO.txt b/mods/admin/cleaner/TODO.txt index 743eb23b..74c15283 100644 --- a/mods/admin/cleaner/TODO.txt +++ b/mods/admin/cleaner/TODO.txt @@ -4,4 +4,9 @@ TODO: - update inventories when items are replaced: - creative - storage (chests, etc.) -- fix top nodes disappearing with /replace_node command +- add LBM when removing an item if it is a node +- update localization files +- add "radius" option for pencil or "xlen", "ylen", & "zlen" options +- add "xrotate" & "zrorate" modes for pencil +- fix pencil "write" mode when pointing to side of node (node gets placed below) +- add config file usage to HTML docs diff --git a/mods/admin/cleaner/changelog.txt b/mods/admin/cleaner/changelog.txt index ea3a1b90..e57265b5 100644 --- a/mods/admin/cleaner/changelog.txt +++ b/mods/admin/cleaner/changelog.txt @@ -3,15 +3,19 @@ v1.2 ---- - added API - added chat commands: - - remove_entity - - remove_node - - replace_item - - replace_node + - remove_entities + - remove_nodes + - replace_items + - replace_nodes - find_unknown_nodes + - replace_ores (unsafe) +- added support for unregistering ores (unsafe) - added setting for enabling "unsafe" methods & commands -- added support for unregistering ores - all types are loaded from /cleaner.json file - added localization support +- added Spanish localization +- added pencil tool for erasing, adding, & swapping nodes +- added "cleaner" chat command for managing pencil tool settings v1.1 ---- diff --git a/mods/admin/cleaner/chat.lua b/mods/admin/cleaner/chat.lua index 0e4a4dc9..ae8fbf2e 100644 --- a/mods/admin/cleaner/chat.lua +++ b/mods/admin/cleaner/chat.lua @@ -7,6 +7,8 @@ local S = core.get_translator(cleaner.modname) +local aux = dofile(cleaner.modpath .. "/misc_functions.lua") + local function pos_list(ppos, radius) local plist = {} @@ -21,54 +23,44 @@ local function pos_list(ppos, radius) return plist end -local param_desc = { - ["radius"] = S("Search radius."), - ["entity"] = S("Entity technical name."), - ["node"] = S("Node technical name."), - ["old_node"] = S("Technical name of node to be replaced."), - ["new_node"] = S("Technical name of node to be used in place."), - ["old_item"] = S("Technical name of item to be replaced."), - ["new_item"] = S("Technical name of item to be used in place."), - ["ore"] = S("Ore technical name."), +local param_def = { + radius = {name=S("radius"), desc=S("Search radius.")}, + entity = {name=S("entity"), desc=S("Entity technical name.")}, + node = {name=S("node"), desc=S("Node technical name.")}, + old_node = {name=S("old_node"), desc=S("Technical name of node to be replaced.")}, + new_node = {name=S("new_node"), desc=S("Technical name of node to be used in place.")}, + old_item = {name=S("old_item"), desc=S("Technical name of item to be replaced.")}, + new_item = {name=S("new_item"), desc=S("Technical name of item to be used in place.")}, + ore = {name=S("ore"), desc=S("Ore technical name.")}, } -local function format_help(cmd, param_string, params) - local retval = S("Usage:") .. "\n /" .. cmd .. " " .. param_string - .. "\n" - - local p_count = 0 - for _, p in ipairs(params) do - if p_count == 0 then - retval = retval .. "\n" .. S("Params:") - end - - retval = retval .. "\n " .. S(p) .. ": " .. param_desc[p] - p_count = p_count + 1 - end - - return retval -end - local cmd_repo = { entity = { - cmd = "remove_entity", - params = "<" .. S("entity") .. "> [" .. S("radius") .. "]", + cmd = "remove_entities", + params = {"entity"}, + oparams = {radius=100}, }, - node = { - cmd_rem = "remove_node", - cmd_rep = "replace_node", - cmd_find = "find_unknown_nodes", - params_rem = "<" .. S("node") .. "> [" .. S("radius") .. "]", - params_rep = "<" .. S("old_node") .. "> <" .. S("new_node") .. "> [" .. S("radius") .. "]", - params_find = "[" .. S("radius") .. "]", + rem_node = { + cmd = "remove_nodes", + params = {"node"}, + oparams = {radius=5}, + }, + rep_node = { + cmd = "replace_nodes", + params = {"old_node", "new_node"}, + oparams = {radius=5}, + }, + find_node = { + cmd = "find_unknown_nodes", + oparams = {radius=100}, }, item = { - cmd = "replace_item", - params = "<" .. S("old_item") .. "> <" .. S("new_item") .. ">", + cmd = "replace_items", + params = {"old_item", "new_item"}, }, ore = { - cmd = "remove_ore", - params = "<" .. S("ore") .. ">", + cmd = "remove_ores", + params = {"ore"}, }, param = { missing = S("Missing parameter."), @@ -77,19 +69,131 @@ local cmd_repo = { }, } +for k, def in pairs(cmd_repo) do + if k ~= "param" then + local cmd_help = { + param_string = "", + usage_string = "/" .. def.cmd, + } + + if def.params or def.oparams then + if def.params then + local params = {} + for _, p in ipairs(def.params) do + -- translate + table.insert(params, S(p)) + end + + cmd_help.param_string = "<" .. table.concat(params, "> <") .. ">" + end + end + + if def.oparams then + for k, v in pairs(def.oparams) do + local op = k + if type(op) == "number" then + op = v + end + + cmd_help.param_string = cmd_help.param_string .. " [" .. S(op) .. "]" + end + end + + if cmd_help.param_string ~= "" then + cmd_help.usage_string = cmd_help.usage_string .. " " .. cmd_help.param_string + end + + cmd_repo[k].help = cmd_help + end +end + +local function get_cmd_def(cmd) + for k, v in pairs(cmd_repo) do + if v.cmd == cmd then return v end + end +end + +local function format_usage(cmd) + local def = get_cmd_def(cmd) + if def then + return S("Usage:") .. "\n " .. def.help.usage_string + end +end + +local function format_params(cmd) + local def = get_cmd_def(cmd) + + local param_count + local all_params = {} + if def.params then + for k, v in ipairs(def.params) do + table.insert(all_params, p) + end + end + if def.oparams then + for k, v in pairs(def.oparams) do + + end + end + + local retval = "" + local p_count = 0 + + if def.params then + for _, p in ipairs(def.params) do + if p_count == 0 then + retval = retval .. S("Params:") + end + + retval = retval .. "\n " .. S(p) .. ": " .. param_def[p].desc + + p_count = p_count + 1 + end + end + + if def.oparams then + for k, v in pairs(def.oparams) do + if p_count == 0 then + retval = retval .. S("Params:") + end + + local p = k + local dvalue = v + if type(p) == "number" then + p = v + dvalue = nil + end + + retval = retval .. "\n " .. S(p) .. ": " .. param_def[p].desc + if dvalue then + retval = retval .. " (" .. S("default: @1", dvalue) .. ")" + end + + p_count = p_count + 1 + end + end + + return retval +end + +local function format_help(cmd) + return format_usage(cmd) .. "\n\n" .. format_params(cmd) +end + --- Removes nearby entities. -- --- @chatcmd remove_entity +-- @chatcmd remove_entities -- @param entity Entity technical name. -- @tparam[opt] int radius core.register_chatcommand(cmd_repo.entity.cmd, { privs = {server=true}, - description = S("Remove an entity from game."), - params = cmd_repo.entity.params, + description = S("Remove an entity from game.") .. "\n\n" + .. format_params(cmd_repo.entity.cmd), + params = cmd_repo.entity.help.param_string, func = function(name, param) local entity - local radius = 100 + local radius = cmd_repo.entity.oparams.radius if param:find(" ") then entity = param:split(" ") radius = tonumber(entity[2]) @@ -106,8 +210,7 @@ core.register_chatcommand(cmd_repo.entity.cmd, { end if err then - return false, err .. "\n\n" - .. format_help(cmd_repo.entity.cmd, cmd_repo.entity.params, {"entity", "radius"}) + return false, err .. "\n\n" .. format_help(cmd_repo.entity.cmd) end local player = core.get_player_by_name(name) @@ -135,16 +238,17 @@ core.register_chatcommand(cmd_repo.entity.cmd, { --- Removes nearby nodes. -- --- @chatcmd remove_node +-- @chatcmd remove_nodes -- @param node Node technical name. -- @tparam[opt] int radius -core.register_chatcommand(cmd_repo.node.cmd_rem, { +core.register_chatcommand(cmd_repo.rem_node.cmd, { privs = {server=true}, - description = S("Remove a node from game."), - params = cmd_repo.node.params_rem, + description = S("Remove a node from game.") .. "\n\n" + .. format_params(cmd_repo.rem_node.cmd), + params = cmd_repo.rem_node.help.param_string, func = function(name, param) local nname - local radius = 100 + local radius = cmd_repo.rem_node.oparams.radius if param:find(" ") then nname = param:split(" ") radius = tonumber(nname[2]) @@ -161,8 +265,7 @@ core.register_chatcommand(cmd_repo.node.cmd_rem, { end if err then - return false, err .. "\n\n" - .. format_help(cmd_repo.node.cmd_rem, cmd_repo.node.params_rem, {"node", "radius"}) + return false, err .. "\n\n" .. format_help(cmd_repo.rem_node.cmd) end local ppos = core.get_player_by_name(name):get_pos() @@ -182,18 +285,17 @@ core.register_chatcommand(cmd_repo.node.cmd_rem, { --- Replaces an item. -- --- @chatcmd replace_item +-- @chatcmd replace_items -- @param old_item Technical name of item to replace. -- @param new_item Technical name of item to be used in place. core.register_chatcommand(cmd_repo.item.cmd, { privs = {server=true}, - description = S("Replace an item in game."), - params = cmd_repo.item.params, + description = S("Replace an item in game.") .. "\n\n" + .. format_params(cmd_repo.item.cmd), + params = cmd_repo.item.help.param_string, func = function(name, param) - local help = format_help(cmd_repo.item.cmd, cmd_repo.item.params, {"old_item", "new_item"}) - if not param:find(" ") then - return false, cmd_repo.param.missing .. "\n\n" .. help + return false, cmd_repo.param.missing .. "\n\n" .. format_help(cmd_repo.item.cmd) end local src = param:split(" ") @@ -211,24 +313,23 @@ core.register_chatcommand(cmd_repo.item.cmd, { --- Replaces nearby nodes. -- --- FIXME: sometimes nodes on top disappear --- --- @chatcmd replace_node +-- @chatcmd replace_nodes -- @param old_node Technical name of node to replace. -- @param new_node Technical name of node to be used in place. -- @tparam[opt] int radius -core.register_chatcommand(cmd_repo.node.cmd_rep, { +core.register_chatcommand(cmd_repo.rep_node.cmd, { privs = {server=true}, - description = S("Replace a node in game."), - params = cmd_repo.node.params_rep, + description = S("Replace a node in game.") .. "\n\n" + .. format_params(cmd_repo.rep_node.cmd), + params = cmd_repo.rep_node.help.param_string, func = function(name, param) - local help = format_help(cmd_repo.node.cmd_rep, cmd_repo.node.params_rep, {"old_node", "new_node", "radius"}) + local help = format_help(cmd_repo.rep_node.cmd) if not param:find(" ") then return false, cmd_repo.param.missing .. "\n\n" .. help end - local radius = 100 + local radius = cmd_repo.rep_node.oparams.radius local params = param:split(" ") local src = params[1] @@ -241,8 +342,7 @@ core.register_chatcommand(cmd_repo.node.cmd_rep, { return false, cmd_repo.param.mal_radius .. "\n\n" .. help end - local new_node = core.registered_nodes[tgt] - if not new_node then + if not core.registered_nodes[tgt] then return false, S('Cannot use unknown node "@1" as replacement.', tgt) end @@ -251,9 +351,11 @@ core.register_chatcommand(cmd_repo.node.cmd_rep, { for _, npos in ipairs(pos_list(ppos, radius)) do local node = core.get_node_or_nil(npos) if node and node.name == src then - core.remove_node(npos) - core.place_node(npos, new_node) - total_replaced = total_replaced + 1 + if core.swap_node(npos, {name=tgt}) then + total_replaced = total_replaced + 1 + else + cleaner.log("error", "could not replace node at " .. core.pos_to_string(npos, 0)) + end end end @@ -265,18 +367,19 @@ core.register_chatcommand(cmd_repo.node.cmd_rep, { -- -- @chatcmd find_unknown_nodes -- @tparam[opt] int radius Search radius. -core.register_chatcommand(cmd_repo.node.cmd_find, { +core.register_chatcommand(cmd_repo.find_node.cmd, { privs = {server=true}, - description = S("Find names of unknown nodes."), - params = cmd_repo.node.params_find, + description = S("Find names of unknown nodes.") .. "\n\n" + .. format_params(cmd_repo.find_node.cmd), + params = cmd_repo.find_node.help.param_string, func = function(name, param) - local help = format_help(cmd_repo.node.cmd_find, cmd_repo.node.params_find, {"radius"}) + local help = format_help(cmd_repo.find_node.cmd) if param:find(" ") then return false, cmd_repo.param.excess .. "\n\n" .. help end - local radius = 100 + local radius = cmd_repo.find_node.oparams.radius if param and param:trim() ~= "" then radius = tonumber(param) end @@ -322,12 +425,13 @@ core.register_chatcommand(cmd_repo.node.cmd_find, { if cleaner.unsafe then --- Registers an ore to be removed. -- - -- @chatcmd remove_ore + -- @chatcmd remove_ores -- @param ore Ore technical name. core.register_chatcommand(cmd_repo.ore.cmd, { privs = {server=true}, - description = S("Remove an ore from game."), - params = cmd_repo.ore.params, + description = S("Remove an ore from game.") .. "\n\n" + .. format_params(cmd_repo.ore.cmd), + params = cmd_repo.ore.help.param_string, func = function(name, param) local err if not param or param:trim() == "" then @@ -337,7 +441,7 @@ if cleaner.unsafe then end if err then - return false, err .. "\n\n" .. format_help(cmd_repo.ore.cmd, cmd_repo.ore.params, {"ore"}) + return false, err .. "\n\n" .. format_help(cmd_repo.ore.cmd) end local success = false @@ -355,3 +459,57 @@ if cleaner.unsafe then end }) end + +--- @section end + + +--- Manages settings for wielded cleaner tool. +-- +-- @chatcmd cleaner +-- @param action Action to execute. Can be "status", "setmode", or "setnode". +-- @param value Mode or node to be set for tool. +core.register_chatcommand("cleaner", { + privs = {server=true}, + description = S("Manage settings for wielded cleaner tool.") .. "\n\n" + .. S("Params:") .. "\n action: Action to execute. Can be one of \"status\", \"setmode\", or \"setnode\"." + .. "\n value: Mode or node to be set for tool.", + params = " ", + func = function(name, param) + local action, value = param + local idx = param:find(" ") + if idx then + param = string.split(param, " ") + action = param[1] + value = param[2] + end + + local player = core.get_player_by_name(name) + local stack = player:get_wielded_item() + local iname = aux.tool:format_name(stack) + local imeta = stack:get_meta() + + if iname ~= "cleaner:pencil" then + return false, S("Unrecognized wielded item: @1", iname) + end + + if action == "status" then + core.chat_send_player(name, iname .. ": " + .. S("mode=@1, node=@2", imeta:get_string("mode"), imeta:get_string("node"))) + return true + end + + if not action or not value then + return false, S("Missing parameter.") + end + + if action == "setmode" then + stack = aux.tool:set_mode(stack, value, name) + elseif action == "setnode" then + stack = aux.tool:set_node(stack, value, name) + else + return false, S("Unrecognized action: @1", action) + end + + return player:set_wielded_item(stack) + end, +}) diff --git a/mods/admin/cleaner/init.lua b/mods/admin/cleaner/init.lua index 31df5fff..71d9ef20 100644 --- a/mods/admin/cleaner/init.lua +++ b/mods/admin/cleaner/init.lua @@ -1,7 +1,3 @@ ---[[ Cleaner mod - License: MIT -]] - cleaner = {} cleaner.modname = core.get_current_modname() @@ -40,6 +36,7 @@ local scripts = { "settings", "api", "chat", + "tools", "entities", "nodes", "items", diff --git a/mods/admin/cleaner/locale/cleaner.es.tr b/mods/admin/cleaner/locale/cleaner.es.tr new file mode 100644 index 00000000..37b5bcea --- /dev/null +++ b/mods/admin/cleaner/locale/cleaner.es.tr @@ -0,0 +1,44 @@ +# textdomain:cleaner + +# Translators: Jordan Irwin (AntumDeluge) + + +# chat commands +entity=entidad +node=nodo +radius=radio +old_item=objeto_antiguo +new_item=objeto_nuevo +old_node=nodo_antiguo +new_node=nodo_nuevo +ore=mineral +Usage:=Uso: +Params:=Parámetros: +default: @1=por defecto: @1 +Search radius.=Radio de búsqueda. +Entity technical name.=Nombre técnico de entidad. +Node technical name.=Nombre técnico de nodo. +Technical name of node to be replaced.=Nombre técnico del nodo reemplazado. +Technical name of node to be used in place.=Nombre técnico del nodo de reemplazo. +Technical name of item to be replaced.=Nombre técnico del objeto reemplazado. +Technical name of item to be used in place.=Nombre técnico del objeto de reemplazo. +Ore technical name.=Nombre técnico de mineral. +Remove an entity from game.=Eliminar una entidad del juego. +Remove a node from game.=Eliminar un nodo del juego. +Replace an item in game.=Sustituir un objecto del juego. +Replace a node in game.=Sustituir un nodo del juego. +Find names of unknown nodes.=Encontrar los nombres de nodos desconocidos. +Remove an ore from game.=Eliminar un mineral del juego. +Missing parameter.=Parámetro extraviado. +Too many parameters.=Demasiados parámetros. +Radius must be a number.=El radio debe ser un número. +Cannot use unknown item "@1" as replacement.=El objeto "@1" es desonocido, no se puede utilizar como sustitución. +Cannot use unknown node "@1" as replacement.=El nodo "@1" es desonocido, no se puede utilizar como sustitución. +Replaced @1 nodes.=Nodos sustituidos: @1 +Removed @1 nodes.=Se eliminaron @1 nodos. +Removed @1 entities.=Se eliminaron @1 entidades. +Found unknown nodes: @1=Se encontraron @1 nodos desconocidos. +No unknown nodes found.=No se encontraron nodos desconocidos. +Ore "@1" not found, not unregistering.=No se encontró el mineral "@1", se mantiene registrado. +Unregistered @1 ores (this will be undone after server restart).=Se anuló @1 minerales del registro. +Success!=¡Éxito! diff --git a/mods/admin/cleaner/locale/template.txt b/mods/admin/cleaner/locale/template.txt index 9153a0c1..4f818623 100644 --- a/mods/admin/cleaner/locale/template.txt +++ b/mods/admin/cleaner/locale/template.txt @@ -1,4 +1,6 @@ -# Translated by +# textdomain:cleaner + +# Translators: # chat commands @@ -12,6 +14,7 @@ new_node= ore= Usage:= Params:= +default: @1= Search radius.= Entity technical name.= Node technical name.= diff --git a/mods/admin/cleaner/misc_functions.lua b/mods/admin/cleaner/misc_functions.lua index 4812d494..df6bfd4a 100644 --- a/mods/admin/cleaner/misc_functions.lua +++ b/mods/admin/cleaner/misc_functions.lua @@ -1,4 +1,7 @@ +local S = core.get_translator(cleaner.modname) + + --- Cleans duplicate entries from indexed table. -- -- @local @@ -66,9 +69,178 @@ local function update_world_data(t, data) return false end +local tool = { + modes = { + ["cleaner:pencil"] = {"erase", "write", "swap"}, + }, + + format_name = function(self, stack) + local iname = stack:get_name() + if iname == "cleaner:pencil_1" then + iname = "cleaner:pencil" + end + + return iname + end, + + set_mode = function(self, stack, mode, pname) + local iname = self:format_name(stack) + + if not self.modes[iname] then + if pname then + core.chat_send_player(pname, iname .. ": " .. S("unknown mode: @1", mode)) + end + cleaner.log("warning", iname .. ": unknown mode: " .. mode) + return stack + end + + local imeta = stack:get_meta() + imeta:set_string("mode", mode) + + if pname then + core.chat_send_player(pname, iname .. ": " + .. S("mode set to: @1", imeta:get_string("mode"))) + end + + local new_stack + if mode == "erase" then + new_stack = ItemStack("cleaner:pencil_1") + else + new_stack = ItemStack("cleaner:pencil") + end + + local new_meta = new_stack:get_meta() + new_meta:from_table(imeta:to_table()) + + return new_stack + end, + + next_mode = function(self, stack, pname) + local iname = self:format_name(stack) + local modes = self.modes[iname] + + if not modes then + return false, stack, "modes for tool \"" .. stack:get_name() .. "\" not available." + end + + local imeta = stack:get_meta() + local current_mode = imeta:get_string("mode") + if not current_mode or current_mode:trim() == "" then + return true, self:set_mode(stack, modes[1], pname) + end + + local idx = 1 + for _, m in ipairs(modes) do + if current_mode == m then + break + end + idx = idx + 1 + end + + return true, self:set_mode(stack, modes[idx+1] or modes[1], pname) + end, + + set_node = function(self, stack, node, pname) + local imeta = stack:get_meta() + imeta:set_string("node", node) + + if pname then + core.chat_send_player(pname, stack:get_name() .. ": " + .. S("node set to: @1", imeta:get_string("node"))) + end + + return stack + end, +} + +tool.on_use = function(stack, user, pointed_thing) + if not user:is_player() then return end + + local pname = user:get_player_name() + if not core.get_player_privs(pname).server then + core.chat_send_player(pname, S("You do not have permission to use this item. Missing privs: server")) + return stack + end + + if sound_handle then + core.sound_stop(sound_handle) + sound_handle = nil + end + + if pointed_thing.type == "node" then + local npos = core.get_pointed_thing_position(pointed_thing) + local imeta = stack:get_meta() + local mode = imeta:get_string("mode") + local new_node_name = imeta:get_string("node") + + if mode == "erase" then + core.remove_node(npos) + sound_handle = core.sound_play("cleaner_pencil_erase", {object=user}) + return stack + elseif core.registered_nodes[new_node_name] then + if mode == "swap" then + core.swap_node(npos, {name=new_node_name}) + sound_handle = core.sound_play("cleaner_pencil_write", {object=user}) + elseif mode == "write" then + local node_above = core.get_node_or_nil(pointed_thing.above) + if not node_above or node_above.name == "air" then + core.place_node(pointed_thing.above, {name=new_node_name}) + sound_handle = core.sound_play("cleaner_pencil_write", {object=user}) + else + core.chat_send_player(pname, S("Can't place node there.")) + end + else + core.chat_send_player(pname, S("Unknown mode: @1", mode)) + end + + return stack + end + + core.chat_send_player(pname, S("Cannot place unknown node: @1", new_node_name)) + return stack + end +end + +tool.on_secondary_use = function(stack, user, pointed_thing) + if not user:is_player() then return end + + local pname = user:get_player_name() + if not core.get_player_privs(pname).server then + core.chat_send_player(pname, S("You do not have permission to use this item. Missing privs: @1", "server")) + return stack + end + + local success, stack, msg = tool.next_mode(tool, stack, pname) + if not success then + core.chat_send_player(pname, msg) + end + + return stack +end + +tool.on_place = function(stack, placer, pointed_thing) + if not placer:is_player() then return end + + local pname = placer:get_player_name() + if not core.get_player_privs(pname).server then + core.chat_send_player(pname, S("You do not have permission to use this item. Missing privs: @1", "server")) + return stack + end + + if pointed_thing.type == "node" then + local node = core.get_node_or_nil(core.get_pointed_thing_position(pointed_thing)) + if node then + stack = tool:set_node(stack, node.name, pname) + end + end + + return stack +end + return { clean_duplicates = clean_duplicates, get_world_data = get_world_data, update_world_data = update_world_data, + tool = tool, } diff --git a/mods/admin/cleaner/nodes.lua b/mods/admin/cleaner/nodes.lua index 4f1239da..f3878ab6 100644 --- a/mods/admin/cleaner/nodes.lua +++ b/mods/admin/cleaner/nodes.lua @@ -75,12 +75,9 @@ core.register_lbm({ nodenames = {"group:to_replace"}, run_at_every_load = true, action = function(pos, node) - core.remove_node(pos) - local new_node_name = cleaner.get_replace_nodes()[node.name] - local new_node = core.registered_nodes[new_node_name] - if new_node then - core.place_node(pos, new_node) + if core.registered_nodes[new_node_name] then + core.swap_node(pos, {name=new_node_name}) else cleaner.log("error", "cannot replace with unregistered node \"" .. tostring(new_node_name) .. "\"") end diff --git a/mods/admin/cleaner/sounds/cleaner_pencil_erase.ogg b/mods/admin/cleaner/sounds/cleaner_pencil_erase.ogg new file mode 100644 index 00000000..1fe52457 Binary files /dev/null and b/mods/admin/cleaner/sounds/cleaner_pencil_erase.ogg differ diff --git a/mods/admin/cleaner/sounds/cleaner_pencil_write.ogg b/mods/admin/cleaner/sounds/cleaner_pencil_write.ogg new file mode 100644 index 00000000..3afa8aab Binary files /dev/null and b/mods/admin/cleaner/sounds/cleaner_pencil_write.ogg differ diff --git a/mods/admin/cleaner/textures/cleaner_pencil.png b/mods/admin/cleaner/textures/cleaner_pencil.png new file mode 100644 index 00000000..35826879 Binary files /dev/null and b/mods/admin/cleaner/textures/cleaner_pencil.png differ diff --git a/mods/admin/cleaner/tools.lua b/mods/admin/cleaner/tools.lua new file mode 100644 index 00000000..c6c3f80f --- /dev/null +++ b/mods/admin/cleaner/tools.lua @@ -0,0 +1,46 @@ + +--- Cleaner Tools +-- +-- @topic tools + + +local S = core.get_translator(cleaner.modname) + + +local aux = dofile(cleaner.modpath .. "/misc_functions.lua") + +local sound_handle + +--- Master Pencil +-- +-- @tool cleaner:pencil +-- @img cleaner_pencil.png +-- @privs server +-- @usage +-- place (right-click): +-- - when not pointing at a node, changes modes +-- - when pointing at a node, sets node to be used +-- +-- use (left-click): +-- - executes action for current mode: +-- - erase: erases pointed node +-- - write: adds node +-- - swap: replaces pointed node +core.register_tool(cleaner.modname .. ":pencil", { + description = S("Master Pencil"), + inventory_image = "cleaner_pencil.png", + liquids_pointable = true, + on_use = aux.tool.on_use, + on_secondary_use = aux.tool.on_secondary_use, + on_place = aux.tool.on_place, +}) + +core.register_tool(cleaner.modname .. ":pencil_1", { + description = S("Master Pencil"), + inventory_image = "cleaner_pencil.png^[transformFXFY", + liquids_pointable = true, + groups = {not_in_creative_inventory=1}, + on_use = aux.tool.on_use, + on_secondary_use = aux.tool.on_secondary_use, + on_place = aux.tool.on_place, +})