2019-12-10 21:29:42 +01:00

991 lines
28 KiB
Lua

--[[
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_tool("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_tool("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_tool("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_tool("tools:sword", {
description = S("Sword"),
inventory_image = "sword.png",
})
frame.register("tools:sword")
minetest.register_tool("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 trunk
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: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
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, "server") 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")