diff --git a/README.md b/README.md index b3d2c52..970dd5b 100644 --- a/README.md +++ b/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/TODO.txt b/TODO.txt index 1f0615d..17ac434 100644 --- a/TODO.txt +++ b/TODO.txt @@ -4,5 +4,9 @@ TODO: - update inventories when items are replaced: - creative - storage (chests, etc.) -- add admin tool for replacing/removing nodes - add LBM when removing an item if it is a node +- make pencil image flip when mode set to "erase" +- 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) diff --git a/changelog.txt b/changelog.txt index efae998..e57265b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -14,6 +14,8 @@ v1.2 - 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/chat.lua b/chat.lua index f900ab7..beb79b9 100644 --- a/chat.lua +++ b/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 = {} @@ -457,3 +459,55 @@ if cleaner.unsafe then end }) 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 = stack:get_name() + 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/init.lua b/init.lua index 31df5ff..b3fb769 100644 --- a/init.lua +++ b/init.lua @@ -1,6 +1,7 @@ ---[[ Cleaner mod - License: MIT -]] + +--- Cleaner +-- +-- @topic tools cleaner = {} @@ -49,3 +50,107 @@ local scripts = { for _, script in ipairs(scripts) do dofile(cleaner.modpath .. "/" .. script .. ".lua") end + + +local S = core.get_translator(cleaner.modname) + + +local sound_handle + +--- Master Pencil +-- +-- @tool cleaner:pencil +-- @img cleaner_pencil.png +-- @privs server +core.register_tool(cleaner.modname .. ":pencil", { + description = S("Master Pencil"), + inventory_image = "cleaner_pencil.png", + liquids_pointable = true, + on_use = function(itemstack, 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 itemstack + 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 = itemstack: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 itemstack + 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}) + return itemstack + 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 + + return itemstack + else + core.chat_send_player(pname, S("Unknown mode: @1", mode)) + end + end + + core.chat_send_player(pname, S("Cannot place unknown node: @1", new_node_name)) + return itemstack + end + end, + on_secondary_use = function(itemstack, 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 itemstack + end + + local imeta = itemstack:get_meta() + local mode = imeta:get_string("mode") + if mode == "erase" or mode == "" then + mode = "write" + elseif mode == "write" then + mode = "swap" + else + mode = "erase" + end + + return aux.tool:set_mode(itemstack, mode, pname) + end, + on_place = function(itemstack, 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 itemstack + end + + if pointed_thing.type == "node" then + local node = core.get_node_or_nil(core.get_pointed_thing_position(pointed_thing)) + if node then + itemstack = aux.tool:set_node(itemstack, node.name, pname) + end + end + + return itemstack + end, +}) diff --git a/misc_functions.lua b/misc_functions.lua index 4812d49..3b8217e 100644 --- a/misc_functions.lua +++ b/misc_functions.lua @@ -1,4 +1,7 @@ +local S = core.get_translator(cleaner.modname) + + --- Cleans duplicate entries from indexed table. -- -- @local @@ -66,9 +69,63 @@ local function update_world_data(t, data) return false end +local tool = { + modes = { + erase = true, + write = true, + swap = true, + }, +} + +tool.set_mode = function(self, stack, mode, pname) + local iname = stack:get_name() + + if not self.modes[mode] 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 + + --[[ FIXME: want to flip item image when mode is set to "erase" + local new_item = table.copy(core.registered_nodes[iname]) + if mode == "erase" then + new_item.inventory_image = "cleaner_pencil.png^[transformFXFY" + else + new_item.inventory_image = "cleaner_pencil.png" + end + + local new_stack = ItemStack(new_item) + ]] + + 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 + + return stack +end + +tool.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 + return { clean_duplicates = clean_duplicates, get_world_data = get_world_data, update_world_data = update_world_data, + tool = tool, } diff --git a/sounds/cleaner_pencil_erase.ogg b/sounds/cleaner_pencil_erase.ogg new file mode 100644 index 0000000..1fe5245 Binary files /dev/null and b/sounds/cleaner_pencil_erase.ogg differ diff --git a/sounds/cleaner_pencil_write.ogg b/sounds/cleaner_pencil_write.ogg new file mode 100644 index 0000000..3afa8aa Binary files /dev/null and b/sounds/cleaner_pencil_write.ogg differ diff --git a/textures/cleaner_pencil.png b/textures/cleaner_pencil.png new file mode 100644 index 0000000..3582687 Binary files /dev/null and b/textures/cleaner_pencil.png differ