--[[ ITB (insidethebox) minetest game - Copyright (C) 2017-2018 sofar & nore This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ]]-- local S = minetest.get_translator("tools") -- -- hand tools -- minetest.register_item(":", { type = "none", wield_image = "wieldhand.png", inventory_image = "wieldhand.png", wield_scale = {x = 1, y = 1, z = 2}, range = 4, tool_capabilities = { full_punch_interval = 0.5, max_drop_level = 0, -- can't pick up any node by hand groupcaps = { hand = {times={[1] = 0.2}, uses = 0, maxlevel = 1}, }, damage_groups = {}, }, groups = {not_in_creative_inventory = 1} }) minetest.register_item("tools:edit", { type = "none", wield_image = "wieldhand_edit.png", inventory_image = "wieldhand_edit.png", wield_scale = {x = 1, y = 1, z = 2}, range = 4, -- to make sure we can reach as far as the player tool_capabilities = { full_punch_interval = 0.1, max_drop_level = 0, groupcaps = { hand = {times={[1] = 0.2}, uses = 0, maxlevel = 1}, axe = {times={[1] = 0.2}, uses = 0, maxlevel = 1}, shovel = {times={[1] = 0.2}, uses = 0, maxlevel = 1}, pickaxe = {times={[1] = 0.2}, uses = 0, maxlevel = 1}, node = {times={[1] = 0.2}, uses = 0, maxlevel = 1}, unbreakable = {times={[1] = 0.2}, uses = 0, maxlevel = 1}, }, damage_groups = {}, }, groups = {not_in_creative_inventory = 1} }) -- node tools minetest.register_craftitem("tools:shovel", { description = S("Shovel"), inventory_image = "shovel.png", tool_capabilities = { groupcaps = { shovel = { times = {[1] = 1.5, [2] = 1.5, [3] = 1.5}, uses = 0, }, }, }, }) frame.register("tools:shovel") minetest.register_craftitem("tools:axe", { description = S("Axe"), inventory_image = "axe.png", tool_capabilities = { groupcaps = { axe = { times = {[1] = 1.5, [2] = 1.5, [3] = 1.5}, uses = 0, }, }, }, }) frame.register("tools:axe") minetest.register_craftitem("tools:pickaxe", { description = S("Pickaxe"), inventory_image = "pickaxe.png", tool_capabilities = { groupcaps = { pickaxe = { times = {[1] = 3, [2] = 3, [3] = 3}, uses = 0, }, }, }, }) frame.register("tools:pickaxe") minetest.register_craftitem("tools:sword", { description = S("Sword"), inventory_image = "sword.png", }) frame.register("tools:sword") minetest.register_craftitem("tools:flint_and_steel", { description = S("Flint and steel"), inventory_image = "flint_and_steel.png", }) frame.register("tools:flint_and_steel") local function node_dig(pos, digger) -- from builtin/item.lua core.node_dig(): local node = minetest.get_node(pos) local def = minetest.registered_nodes[node.name] local sounds = minetest.registered_nodes[node.name].sounds if sounds and sounds.dig then minetest.sound_play(sounds.dig, {pos = pos}) end local oldmetadata = nil if def and def.after_dig_node then oldmetadata = core.get_meta(pos):to_table() end minetest.remove_node(pos, node) if def and def.after_dig_node then local pos_copy = {x=pos.x, y=pos.y, z=pos.z} local node_copy = {name = node.name, param1 = node.param1, param2 = node.param2} def.after_dig_node(pos_copy, node_copy, oldmetadata, digger) end for _, callback in ipairs(core.registered_on_dignodes) do local origin = core.callback_origins[callback] if origin then core.set_last_run_mod(origin.mod) end -- Copy pos and node because callback can modify them local pos_copy = {x=pos.x, y=pos.y, z=pos.z} local node_copy = {name = node.name, param1 = node.param1, param2 = node.param2} callback(pos_copy, node_copy, digger) end end minetest.register_tool("tools:admin", { description = S("Admin remove tool"), inventory_image = "admin_tool.png", liquids_pointable = true, range = 10, on_use = function(itemstack, digger, pointed_thing) if not pointed_thing or not pointed_thing.under then return end -- Prevent non-admins from using an admin tool if they ever get one if not digger or not minetest.check_player_privs(digger, "server") then return end node_dig(pointed_thing.under, digger) return itemstack end, }) minetest.register_tool("tools:player", { description = S("Remove tool").."\n".. S("Left-click to remove a node").."\n".. S("Right-click to remove a lot of nodes").."\n".. S("Shift-left-click a placed node to restrict removal"), inventory_image = "remove_tool.png", liquids_pointable = true, range = 6, on_use = function(itemstack, digger, pointed_thing) if not digger then return end local name = digger:get_player_name() if not boxes.players_editing_boxes[name] then return end if not pointed_thing or not pointed_thing.under then if not digger:get_player_control().sneak then return itemstack end -- empty the contained node local meta = itemstack:get_meta() meta:set_string("nodes", "return {}") minetest.chat_send_player(name, S("Remove tool unrestricted")) meta:set_string("description", S("Remove tool").."\n".. S("Left-click to remove a node").."\n".. S("Right-click to remove a lot of nodes").."\n".. S("Shift-left-click a placed node to restrict removal")) return itemstack end if digger:get_player_control().sneak then -- fill the itemstack local meta = itemstack:get_meta() local nodes = minetest.deserialize(meta:get_string("nodes") or {}) or {} local node = minetest.get_node(pointed_thing.under) nodes[#nodes + 1] = node.name meta:set_string("nodes", minetest.serialize(nodes)) minetest.chat_send_player(name, S("Remove tool restricted to: @1", table.concat(nodes, ", "))) meta:set_string("description", S("Remove tool").."\n"..S("Will remove: @1", table.concat(nodes, ", "))) return itemstack end local box = boxes.players_editing_boxes[name] local pos = pointed_thing.under if pos.x <= box.minp.x or pos.x >= box.maxp.x or pos.y <= box.minp.y or pos.y >= box.maxp.y or pos.z <= box.minp.z or pos.z >= box.maxp.z then return end local meta = itemstack:get_meta() local n = meta:get_string("nodes") if n and n ~= "" and n ~= "return {}" then local nodes = minetest.deserialize(n) local t = minetest.get_node(pointed_thing.under) for _, v in ipairs(nodes) do if t.name == v then node_dig(pointed_thing.under, digger) end end return itemstack end node_dig(pointed_thing.under, digger) return itemstack end, on_place = function(itemstack, placer, pointed_thing) if not pointed_thing or not pointed_thing.under then return end if not placer then return end local name = placer:get_player_name() if not boxes.players_editing_boxes[name] then return end local box = boxes.players_editing_boxes[name] local pos = pointed_thing.under local meta = itemstack:get_meta() local n = meta:get_string("nodes") local nodes = {} if n and n ~= "" and n ~= "return {}" then nodes = minetest.deserialize(n) end for x = pos.x - 1, pos.x + 1 do for y = pos.y + 1, pos.y - 1, -1 do for z = pos.z - 1, pos.z + 1 do if x > box.minp.x and x < box.maxp.x and y > box.minp.y and y < box.maxp.y and z > box.minp.z and z < box.maxp.z then if #nodes == 0 then node_dig({x = x, y = y, z = z}, placer) else for _, v in ipairs(nodes) do local p = {x = x, y = y, z = z} local t = minetest.get_node(p) if t.name == v then node_dig(p, placer) end end end end end end end return itemstack end, }) frame.register("tools:player") local bulk_modes = { [0] = "blob", [1] = "sheet", [2] = "large blob", [3] = "large sheet", [4] = "wall", } minetest.register_tool("tools:bulk", { description = S("Bulk placement tool").."\n".. S("Left-click air to toggle mode").."\n".. S("Shift-left-click to toggle mode").."\n".. S("Left-click a node to pick it up").."\n".. S("Right-click to place nodes"), inventory_image = "node_place_tool.png", liquids_pointable = true, range = 8, on_use = function(itemstack, placer, pointed_thing) if not pointed_thing or not pointed_thing.under or placer:get_player_control().sneak then -- toggle placement mode local meta = itemstack:get_meta() local mode = meta:get_int("mode") or 0 mode = mode + 1 if mode > #bulk_modes then mode = 0 end minetest.chat_send_player(placer:get_player_name(), S("Bulk placement tool will place \"@1\".", bulk_modes[mode])) meta:set_int("mode", mode) return itemstack end local node = minetest.get_node(pointed_thing.under) local def = minetest.registered_nodes[node.name] if not def and not def.groups then return itemstack end if def.groups.not_in_creative_inventory or def.groups.trigger or def.groups.mech or def.groups.door then if not minetest.check_player_privs(placer:get_player_name(), "server") then return itemstack end end itemstack:set_metadata(minetest.serialize(node)) minetest.chat_send_player(placer:get_player_name(), "Bulk tool will place " .. node.name) local meta = itemstack:get_meta() meta:set_string("description", S("Bulk placement tool").."\n".. S("Node: @1", node.name).."\n".. S("Mode: @1", bulk_modes[meta:get_int("mode")])) return itemstack end, on_place = function(itemstack, placer, pointed_thing) if not pointed_thing or not pointed_thing.above or not placer then return end local node = minetest.deserialize(itemstack:get_metadata()) if not node then minetest.chat_send_player(placer:get_player_name(), S("Pick up a node first by left-clicking it with this tool")) return end local name = placer:get_player_name() local box = boxes.players_editing_boxes[name] if not box and not minetest.check_player_privs(name, "server") then return end if not box then box = { minp = { x = -32768, y = -32768, z = -32768 }, maxp = { x = 32768, y = 32768, z = 32768 }, } end -- make sure we don't choke the player. local head = vector.add(vector.round(placer:get_pos()), {x = 0, y = 1, z = 0}) local pos = pointed_thing.under local pnode = minetest.get_node(pos) if not minetest.registered_nodes[pnode.name].buildable_to then pos = pointed_thing.above end local meta = itemstack:get_meta() local mode = meta:get_int("mode") or 0 -- toggle size local size = { 3, 20, 28 } if mode == 2 or mode == 3 then size = {7, 50, 68 } end local function can_place_at(p, b, h) if p.x <= b.minp.x or p.x >= b.maxp.x or p.y <= b.minp.y or p.y >= b.maxp.y or p.z <= b.minp.z or p.z >= b.maxp.z then return false end if vector.equals(p, h) then return false end local n = minetest.get_node(p) if n.name == "air" then return true end local def = minetest.registered_nodes[n.name] if def and def.liquidtype and def.liquidtype == "flowing" then return true end return false end if mode == 0 or mode == 2 then for x = pos.x - size[1], pos.x + size[1] do for y = pos.y - size[1], pos.y + size[1] do for z = pos.z - size[1], pos.z + size[1] do local ppos = {x = x, y = y, z = z} if can_place_at(ppos, box, head) and vector.distance(pos, ppos) <= (math.random(size[2], size[3]) / 10) then minetest.set_node(ppos, node) end end end end elseif mode == 1 or mode == 3 then for x = pos.x - size[1], pos.x + size[1] do for z = pos.z - size[1], pos.z + size[1] do local ppos = {x = x, y = pos.y, z = z} if can_place_at(ppos, box, head) and vector.distance(pos, ppos) <= (math.random(size[2], size[3]) / 10) then minetest.set_node(ppos, node) end end end elseif mode == 4 then local dir = minetest.yaw_to_dir(placer:get_look_yaw() - (math.pi / 2)) for i = 0, 4 do for y = 0, 2 do local ppos = vector.round(vector.add(pos, vector.multiply(dir, i))) ppos.y = ppos.y + y if can_place_at(ppos, box, head) then minetest.set_node(ppos, node) end end end else return itemstack end minetest.log("action", name .. " uses tools:bulk placing a " .. bulk_modes[mode] .. " of " .. node.name .. " at " .. minetest.pos_to_string(pos)) return itemstack end, }) frame.register("tools:bulk") minetest.register_tool("tools:paint", { description = S("Paint tool").."\n".. S("Paint nodes over with a different node").."\n".. S("Left-click to fill the tool with a node").."\n".. S("Right-click to paint"), inventory_image = "node_paint_tool.png", liquids_pointable = true, range = 6, on_use = function(itemstack, placer, pointed_thing) if not pointed_thing or not pointed_thing.under then return itemstack end local node = minetest.get_node(pointed_thing.under) local def = minetest.registered_nodes[node.name] if not def and not def.groups then return itemstack end if def.groups.not_in_creative_inventory or def.groups.door then if not minetest.check_player_privs(placer:get_player_name(), "server") then return itemstack end end local meta = itemstack:get_meta() meta:set_string("node", minetest.serialize(node)) meta:set_string("meta", minetest.serialize(minetest.get_meta(pointed_thing.under):to_table())) meta:set_string("description", "Paint tool\nNode: " .. node.name) minetest.chat_send_player(placer:get_player_name(), "Paint tool will paint " .. node.name) return itemstack end, on_place = function(itemstack, placer, pointed_thing) if not pointed_thing or not pointed_thing.above or not placer then return end local meta = itemstack:get_meta() local node = minetest.deserialize(meta:get_string("node")) local nmeta = minetest.deserialize(meta:get_string("meta")) if not node then minetest.chat_send_player(placer:get_player_name(), S("Pick up a node first by left-clicking it with this tool")) return end local name = placer:get_player_name() local box = boxes.players_editing_boxes[name] if not box and not minetest.check_player_privs(name, "server") then return end if not box then box = { minp = { x = -32768, y = -32768, z = -32768 }, maxp = { x = 32768, y = 32768, z = 32768 }, } end local pos = pointed_thing.under if pos.x > box.minp.x and pos.x < box.maxp.x and pos.y > box.minp.y and pos.y < box.maxp.y and pos.z > box.minp.z and pos.z < box.maxp.z then minetest.set_node(pos, node) end if nmeta then local pmeta = minetest.get_meta(pos) pmeta:from_table(nmeta) end return itemstack end, }) frame.register("tools:paint") -- Register tables for all plant variants local plants_rotate = {} do -- Growth stages local t = { 8 } for i=0,3 do table.insert(t, "carrots_stage_"..i) end table.insert(plants_rotate, t) local t = { 10 } for i=0,3 do table.insert(t, "potatoes_stage_"..i) end table.insert(plants_rotate, t) t = { 11 } for i=0,7 do table.insert(t, "wheat_stage_"..i) end table.insert(plants_rotate, t) t = { 56 } for i=1,5 do table.insert(t, "grass_"..i) end table.insert(plants_rotate, t) -- Plant style variants t = { 8 } for i=1,6 do table.insert(t, "sapling_"..i) end table.insert(plants_rotate, t) table.insert(plants_rotate, { 9, "rose", "dandelion", "white_tulip", "allium", "orchid", "daisy", "houstonia", "paeonia", }) table.insert(plants_rotate, { 10, "mushroom_red", "mushroom_brown" }) end local plant_mappings = {} for p=1, #plants_rotate do local rotate = plants_rotate[p] local param2 = rotate[1] for r=2, #rotate do local nextr if r < #rotate then nextr = r+1 else nextr = 2 end plant_mappings["nodes:"..rotate[r]] = { "nodes:"..rotate[nextr], param2 } plant_mappings["nodes:flowerpot_"..rotate[r]] = { "nodes:flowerpot_"..rotate[nextr] } end end minetest.register_tool("tools:grow", { description = S("Growth tool"), inventory_image = "grow_tool.png", range = 6, on_place = function(itemstack, placer, pointed_thing) if not pointed_thing or not pointed_thing.under or not placer then return end local name = placer:get_player_name() local box = boxes.players_editing_boxes[name] if not box then if not minetest.check_player_privs(placer, "server") then return end box = { minp = { x = -32768, y = -32768, z = -32768 }, maxp = { x = 32768, y = 32768, z = 32768 }, } end local pos = pointed_thing.under local nname = minetest.get_node(pos).name if plant_mappings[nname] then -- Cycle through plant growth stages and plant variants minetest.set_node(pos, {name=plant_mappings[nname][1], param2=plant_mappings[nname][2]}) elseif nname == "nodes:melon_seeds" then -- Grow melon minetest.set_node(pos, {name="nodes:melon", param2=math.random(0,3)}) elseif nname == "nodes:pumpkin_seeds" then -- Grow pumpkin minetest.set_node(pos, {name="nodes:pumpkin", param2=math.random(0,3)}) elseif nname == "nodes:wheat_seeds" then -- Grow wheat minetest.set_node(pos, {name="nodes:wheat_stage_0", param2=11}) elseif nname == "nodes:flowerpot_empty" then -- Grow random flower in empty flowerpot local param2 = minetest.get_node(pos).param2 local flowers = { "rose", "dandelion", "white_tulip", "allium", "orchid", "daisy", "houstonia", "paeonia", } local flower = flowers[math.random(1, #flowers)] minetest.set_node(pos, {name="nodes:flowerpot_"..flower, param2=param2}) elseif nname == "nodes:fire" or minetest.get_item_group(nname, "leaves") == 1 then -- Grow leaves and spread fire local posses = { { x=0, y=0, z=1 }, { x=0, y=0, z=-1 }, { x=0, y=1, z=0 }, { x=0, y=-1, z=0 }, { x=1, y=0, z=0 }, { x=-1, y=0, z=0 }, } for p=1, #posses do local ppos = vector.add(pos, posses[p]) if minetest.get_node(ppos).name == "air" then minetest.set_node(ppos, {name=nname}) end end elseif nname == "nodes:reeds" or minetest.get_item_group(nname, "tree") == 1 then -- Grow reeds and tree trunks upwards local above for i=1,15 do above = {x=pos.x,y=pos.y+i,z=pos.z} local node = minetest.get_node(above) if node.name == "air" then minetest.set_node(above, {name=nname}) break elseif node.name ~= nname then break end end elseif nname == "nodes:vine" or nname == "nodes:ladder" or nname == "nodes:rope" then -- Grow vines. Also "grow" other climbable nodes for convenience local param2 = minetest.get_node(pos).param2 if param2 >= 2 or nname == "nodes:rope" then -- Grow downwards if sideways or rope local below for i=1,15 do below = {x=pos.x,y=pos.y-i,z=pos.z} local node = minetest.get_node(below) if node.name == "air" then minetest.set_node(below, {name=nname, param2=param2}) break elseif node.name ~= nname then break end end else -- At floor or ceiling: grow around local posses = { { x=1, y=0, z=0 }, { x=-1, y=0, z=0 }, { x=0, y=0, z=1 }, { x=0, y=0, z=-1 }, } for p=1, #posses do local ppos = vector.add(pos, posses[p]) if minetest.get_node(ppos).name == "air" then minetest.set_node(ppos, {name=nname, param2=param2}) break end end end elseif nname == "nodes:dirt" then -- Grow grass cover on dirt for x = math.max(box.minp.x + 1, pos.x - 2), math.min(box.maxp.x - 1, pos.x + 2) do for y = math.max(box.minp.y + 1, pos.y), math.min(box.maxp.y - 1, pos.y) do for z = math.max(box.minp.z + 1, pos.z - 2), math.min(box.maxp.z - 1, pos.z + 2) do local ppos = {x = x, y = y, z = z} if vector.distance(pos, ppos) <= 2.3 then local node = minetest.get_node(ppos) local above_node = minetest.get_node({x = x, y = y + 1, z = z}) if node.name == "nodes:dirt" and minetest.registered_nodes[above_node.name] and minetest.registered_nodes[above_node.name].walkable == false then node.name = "nodes:dirt_with_grass" minetest.set_node(ppos, node) end end end end end elseif nname == "nodes:grass" or nname == "nodes:dirt_with_grass" or nname == "nodes:mycelium" or nname == "nodes:dirt_with_snow" or nname == "nodes:sand" or nname == "nodes:gravel" or nname == "nodes:dirt_with_podzol" or nname == "nodes:soil" or nname == "nodes:soil_wet" then -- Grow grass, flowers and other random plants for x = math.max(box.minp.x + 1, pos.x - 2), math.min(box.maxp.x - 1, pos.x + 2) do for y = math.max(box.minp.y + 1, pos.y), math.min(box.maxp.y - 1, pos.y) do for z = math.max(box.minp.z + 1, pos.z - 2), math.min(box.maxp.z - 1, pos.z + 2) do local ppos = {x = x, y = y, z = z} if vector.distance(pos, ppos) <= 2.3 then local node = minetest.get_node(ppos) local pos_above = {x = x, y = y + 1, z = z} local above_node = minetest.get_node(pos_above) if above_node.name == "air" and math.random() < 0.2 and node.name == nname then local plants if (node.name == "nodes:grass" or node.name == "nodes:dirt_with_grass") then plants = { {"grass_1", 56}, {"grass_2", 56}, {"grass_3", 56}, {"grass_4", 56}, {"grass_5", 56}, {"grass_1", 56}, {"grass_2", 56}, {"grass_3", 56}, {"grass_4", 56}, {"grass_5", 56}, {"grass_1", 56}, {"grass_2", 56}, {"grass_3", 56}, {"grass_4", 56}, {"grass_5", 56}, {"grass_1", 56}, {"grass_2", 56}, {"grass_3", 56}, {"grass_4", 56}, {"grass_5", 56}, {"rose", 9}, {"dandelion", 9}, {"white_tulip", 9}, {"allium", 9}, {"orchid", 9}, {"daisy", 9}, {"houstonia", 9}, {"paeonia", 9}, } elseif (node.name == "nodes:mycelium") then plants = { {"mushroom_red",10}, {"mushroom_brown",10}, } elseif (node.name == "nodes:dirt_with_snow") then plants = { { "dead_bush", 56 }, } elseif (node.name == "nodes:sand") then plants = { { "dead_bush", 56 }, { "dead_bush", 56 }, { "dead_bush", 56 }, { "dead_bush", 56 }, { "dead_bush", 56 }, { "grass_1", 56 }, { "grass_2", 56 }, } elseif (node.name == "nodes:gravel") then plants = { { "grass_1", 56 }, { "grass_1", 56 }, { "grass_2", 56 }, } elseif (node.name == "nodes:dirt_with_podzol") then plants = { {"mushroom_red", 10}, {"mushroom_brown", 10}, {"fern", 9}, {"fern", 9}, {"fern", 9}, {"fern", 9}, {"fern", 9}, {"dead_bush", 9}, {"dead_bush", 9}, {"dead_bush", 9}, {"dead_bush", 9}, {"grass_1", 56}, {"grass_2", 56}, {"rose", 9}, {"dandelion", 9}, } elseif (node.name == "nodes:soil" or node.name == "nodes:soil_wet") then plants = { {"carrots_stage_0", 8}, {"carrots_stage_0", 8}, {"carrots_stage_1", 8}, {"carrots_stage_1", 8}, {"carrots_stage_2", 8}, {"carrots_stage_2", 8}, {"carrots_stage_3", 8}, {"carrots_stage_3", 8}, {"potatoes_stage_0", 10}, {"potatoes_stage_0", 10}, {"potatoes_stage_1", 10}, {"potatoes_stage_1", 10}, {"potatoes_stage_2", 10}, {"potatoes_stage_2", 10}, {"potatoes_stage_3", 10}, {"potatoes_stage_3", 10}, {"wheat_stage_0", 11}, {"wheat_stage_1", 11}, {"wheat_stage_2", 11}, {"wheat_stage_3", 11}, {"wheat_stage_4", 11}, {"wheat_stage_5", 11}, {"wheat_stage_6", 11}, {"wheat_stage_7", 11}, } end if plants then local p = plants[math.random(1, #plants)] local nnode = {name = "nodes:" .. p[1], param2 = p[2]} minetest.set_node(pos_above, nnode) end end end end end end elseif nname == "nodes:waterlily" then -- Spread waterlilies on water for x = math.max(box.minp.x + 1, pos.x - 3), math.min(box.maxp.x - 1, pos.x + 3) do for y = math.max(box.minp.y + 1, pos.y), math.min(box.maxp.y - 1, pos.y) do for z = math.max(box.minp.z + 1, pos.z - 3), math.min(box.maxp.z - 1, pos.z + 3) do local ppos = {x = x, y = y, z = z} if vector.distance(pos, ppos) <= 3.0 then local node = minetest.get_node(ppos) local below_node = minetest.get_node({x = x, y = y - 1, z = z}) if node.name == "air" and math.random() < 0.1 and minetest.get_item_group(below_node.name, "water") >= 1 and minetest.registered_nodes[below_node.name].liquidtype == "source" then node.name = "nodes:waterlily" node.param2 = math.random(0,3) minetest.set_node(ppos, node) end end end end end end return itemstack end, }) frame.register("tools:grow") local function dehash_vector(s) return { x = 256 * string.byte(s, 1) + string.byte(s, 2) - 32768, y = 256 * string.byte(s, 3) + string.byte(s, 4) - 32768, z = 256 * string.byte(s, 5) + string.byte(s, 6) - 32768, } end local function particlestream(p, o, name) local d = vector.length(o) local c = 0 while c < d - 0.5 do minetest.add_particle({ texture = "glowblock.png", pos = vector.add(p, vector.multiply(o, c / d)), velocity = vector.divide(o, d * 3), expirationtime = 3, size = 2, glow = 14, playername = name, }) c = c + 0.5 end end minetest.register_tool("tools:reveal", { description = S("Reveal tool").."\n".. S("Reveal breakable nodes and placeholder nodes").."\n".. S("Punch a node to see its connections"), inventory_image = "reveal_tool.png", on_use = function(itemstack, digger, pointed_thing) if not digger then return end local name = digger:get_player_name() if not boxes.players_editing_boxes[name] and not minetest.check_player_privs(digger, "review") then return end local box = boxes.players_editing_boxes[name] if not box then if not minetest.check_player_privs(digger, "server") then return end box = { minp = { x = -32768, y = -32768, z = -32768 }, maxp = { x = 32768, y = 32768, z = 32768 }, } end local off = { { x = 0, y = 0, z = -9/16}, { x = 0, y = 0, z = 9/16}, { x = 0, y = -9/16, z = 0}, { x = 0, y = 9/16, z = 0}, { x = -9/16, y = 0, z = 0}, { x = 9/16, y = 0, z = 0}, } -- if clicking a node, reveal mech connections if pointed_thing.under then local p = pointed_thing.under local meta = minetest.get_meta(p) local offsets = minetest.deserialize(meta:get_string("offsets")) or {} for v, _ in pairs(offsets) do local o = dehash_vector(v) particlestream(p, o, name) end local roffsets = minetest.deserialize(meta:get_string("roffsets")) or {} for v, _ in pairs(roffsets) do local o = dehash_vector(v) particlestream(vector.add(p, o), {x=-o.x, y=-o.y, z=-o.z}, name) end local dn = digger:get_player_name() -- reveal callbacks, mech linkage info local n = minetest.get_node(p) if n and n.name and minetest.registered_nodes[n.name] then local def = minetest.registered_nodes[n.name] minetest.chat_send_player(dn, minetest.colorize("#ff8888", "Detailed info for \"" .. def.description:gsub("\n.*", "") .. "\" at " .. minetest.pos_to_string(p) .. ":")) for so, _ in pairs(offsets) do local offs = dehash_vector(so) local npos = vector.add(p, offs) local nname = minetest.get_node(npos).name minetest.chat_send_player(dn, minetest.colorize("#44ff88", "> triggers " .. nname .. " at " .. minetest.pos_to_string(npos) .. " (offset is " .. minetest.pos_to_string(offs) .. ")")) end for so, _ in pairs(roffsets) do local offs = dehash_vector(so) local npos = vector.add(p, offs) local nname = minetest.get_node(npos).name minetest.chat_send_player(dn, minetest.colorize("#8888ff", "> triggered by " .. nname .. " at " .. minetest.pos_to_string(npos) .. " (offset is " .. minetest.pos_to_string(offs) .. ")")) end local cb = def.on_reveal if cb then cb(dn, p) end end end local limit = 32 local ppos = vector.floor(digger:get_pos()) local nodeslist = {} local needle = {"group:axe", "group:shovel", "group:pickaxe", "group:hand", "nodes:placeholder", "group:torch"} for d = 3, 6 do if limit > 0 then local poslist, _ = minetest.find_nodes_in_area(vector.subtract(ppos, d), vector.add(ppos, d), needle) for _, v in pairs(poslist) do if limit > 0 then if v.x < box.maxp.x and v.y < box.maxp.y and v.z < box.maxp.z and v.x > box.minp.x and v.y > box.minp.y and v.z > box.minp.z then nodeslist[minetest.pos_to_string(v)] = 1 limit = limit - 1 end end end end end for k, _ in pairs(nodeslist) do local pos = minetest.string_to_pos(k) local node = minetest.get_node(pos) local groups = minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].groups local texture if groups.axe then texture = "axe.png" elseif groups.shovel then texture = "shovel.png" elseif groups.pickaxe then texture = "pickaxe.png" elseif groups.hand then texture = "wieldhand.png" end --FIXME leaves somehow don't work? if texture and vector.distance(pos, digger:get_pos()) < 8 and node.name ~= "nodes:snow_ledge" then for _, v in ipairs(off) do minetest.add_particle({ pos = vector.add(pos, v), expirationtime = 8, size = 3, texture = texture, playername = name, glow = 13, }) end end if node.name == "nodes:placeholder" or node.name == "torches:torch" or node.name == "torches:torch_wall" then local meta = minetest.get_meta(pos) local placeable = meta:get_string("placeable") if placeable ~= "" then local nodelist = minetest.parse_json(placeable) --FIXME just do the first placeholder, only local n, _ = next(nodelist) minetest.add_particle({ pos = pos, expirationtime = 8, size = 3, texture = nodes.get_tiles(n), glow = 14, playername = name, }) end end end return itemstack end, }) frame.register("tools:reveal")