--[[ Wrench mod Adds a wrench that allows the player to pickup nodes that contain an inventory with items or metadata that needs perserving. The wrench has the same tool capability as the normal hand. To pickup a node simply right click on it. If the node contains a formspec, you will need to shift+right click instead. Because it enables arbitrary nesting of chests, and so allows the player to carry an unlimited amount of material at once, this wrench is not available to survival-mode players. --]] local LATEST_SERIALIZATION_VERSION = 1 wrench = {} local modpath = minetest.get_modpath(minetest.get_current_modname()) dofile(modpath.."/support.lua") dofile(modpath.."/technic.lua") -- Boilerplate to support localized strings if intllib mod is installed. local S = rawget(_G, "intllib") and intllib.Getter() or function(s) return s end local function get_meta_type(name, metaname) local def = wrench.registered_nodes[name] return def and def.metas and def.metas[metaname] or nil end local function get_pickup_name(name) return "wrench:picked_up_"..(name:gsub(":", "_")) end local function restore(pos, placer, itemstack) local data = itemstack:get_meta():get_string("data") data = (data ~= "" and data) or itemstack:get_metadata() data = minetest.deserialize(data) if not data then minetest.remove_node(pos) minetest.log("error", placer:get_player_name().." wanted to place ".. itemstack:get_name().." at "..minetest.pos_to_string(pos).. ", but it had no data.") minetest.log("verbose", "itemstack: "..itemstack:to_string()) return true end local node = minetest.get_node(pos) minetest.set_node(pos, {name = data.name, param2 = node.param2}) -- Apply stored metadata to the current node local meta = minetest.get_meta(pos) local inv = meta:get_inventory() for key, value in pairs(data.metas) do local meta_type = get_meta_type(data.name, key) if meta_type == wrench.META_TYPE_INT then meta:set_int(key, value) elseif meta_type == wrench.META_TYPE_FLOAT then meta:set_float(key, value) elseif meta_type == wrench.META_TYPE_STRING then meta:set_string(key, value) end end for listname, list in pairs(data.lists) do inv:set_list(listname, list) end itemstack:take_item() return itemstack end minetest.register_on_mods_loaded(function() -- Delayed registration for foreign mod support for name, info in pairs(wrench.registered_nodes) do local olddef = minetest.registered_nodes[name] if olddef then local newdef = {} for key, value in pairs(olddef) do newdef[key] = value end newdef.stack_max = 1 newdef.description = S("%s with items"):format(newdef.description) newdef.groups = {} newdef.groups.not_in_creative_inventory = 1 newdef.on_construct = nil newdef.on_destruct = nil newdef.after_place_node = restore minetest.register_node(":"..get_pickup_name(name), newdef) end end end) minetest.register_tool("wrench:wrench", { description = S("Wrench"), inventory_image = "technic_wrench.png", tool_capabilities = { full_punch_interval = 0.9, max_drop_level = 0, groupcaps = { crumbly = {times={[2]=3.00, [3]=0.70}, uses=0, maxlevel=1}, snappy = {times={[3]=0.40}, uses=0, maxlevel=1}, oddly_breakable_by_hand = {times={[1]=7.00,[2]=4.00,[3]=1.40}, uses=0, maxlevel=3} }, damage_groups = {fleshy=1}, }, on_place = function(itemstack, placer, pointed_thing) local pos = pointed_thing.under if not placer or not pos then return end local player_name = placer:get_player_name() if minetest.is_protected(pos, player_name) then minetest.record_protection_violation(pos, player_name) return end local node_name = minetest.get_node(pos).name local def = wrench.registered_nodes[node_name] if not def then return end local stack_pickup = ItemStack(get_pickup_name(node_name)) local player_inv = placer:get_inventory() if not player_inv:room_for_item("main", stack_pickup) then return end local meta = minetest.get_meta(pos) if def.owned and not minetest.check_player_privs(placer, "protection_bypass") then local owner = meta:get_string("owner") if owner and owner ~= player_name then minetest.log("action", player_name.. " tried to pick up an owned node belonging to ".. owner.." at ".. minetest.pos_to_string(pos)) return end end -- Do the actual pickup: local metadata = {} metadata.name = node_name metadata.version = LATEST_SERIALIZATION_VERSION -- Serialize inventory lists + items local inv = meta:get_inventory() local lists = {} for _, listname in pairs(def.lists or {}) do local list = inv:get_list(listname) for i, stack in pairs(list) do list[i] = stack:to_string() end lists[listname] = list end metadata.lists = lists -- Serialize node metadata fields local item_meta = stack_pickup:get_meta() metadata.metas = {} for key, meta_type in pairs(def.metas or {}) do if meta_type == wrench.META_TYPE_INT then metadata.metas[key] = meta:get_int(key) elseif meta_type == wrench.META_TYPE_FLOAT then metadata.metas[key] = meta:get_float(key) elseif meta_type == wrench.META_TYPE_STRING then metadata.metas[key] = meta:get_string(key) end end item_meta:set_string("data", minetest.serialize(metadata)) minetest.remove_node(pos) itemstack:add_wear(65535 / 20) player_inv:add_item("main", stack_pickup) return itemstack end, })