SmallJoker
2023-01-29 9b7c44b4530bf82790af495ac574a998656cade9
commit | author | age
d5c554 1 --[[
731a82 2 Wrench mod
S 3
4 Adds a wrench that allows the player to pickup nodes that contain an inventory
5 with items or metadata that needs perserving.
6 The wrench has the same tool capability as the normal hand.
7 To pickup a node simply right click on it. If the node contains a formspec,
8 you will need to shift+right click instead.
0cf413 9 Because it enables arbitrary nesting of chests, and so allows the player
Z 10 to carry an unlimited amount of material at once, this wrench is not
11 available to survival-mode players.
d5c554 12 --]]
21c96a 13
c5a2f0 14 local LATEST_SERIALIZATION_VERSION = 1
S 15
731a82 16 wrench = {}
S 17
18 local modpath = minetest.get_modpath(minetest.get_current_modname())
19 dofile(modpath.."/support.lua")
20 dofile(modpath.."/technic.lua")
21
39c41a 22 -- Boilerplate to support localized strings if intllib mod is installed.
41a10a 23 local S = rawget(_G, "intllib") and intllib.Getter() or function(s) return s end
39c41a 24
731a82 25 local function get_meta_type(name, metaname)
S 26     local def = wrench.registered_nodes[name]
161462 27     return def and def.metas and def.metas[metaname] or nil
21c96a 28 end
M 29
731a82 30 local function get_pickup_name(name)
S 31     return "wrench:picked_up_"..(name:gsub(":", "_"))
32 end
33
34 local function restore(pos, placer, itemstack)
9167d4 35     local data = itemstack:get_meta():get_string("data")
D 36     data = (data ~= "" and data) or    itemstack:get_metadata()
37     data = minetest.deserialize(data)
0f6bdb 38
161462 39     if not data then
D 40         minetest.remove_node(pos)
41         minetest.log("error", placer:get_player_name().." wanted to place "..
0f6bdb 42                 itemstack:get_name().." at "..minetest.pos_to_string(pos)..
161462 43                 ", but it had no data.")
D 44         minetest.log("verbose", "itemstack: "..itemstack:to_string())
45         return true
46     end
0f6bdb 47
S 48     local node = minetest.get_node(pos)
78bfa4 49     minetest.set_node(pos, {name = data.name, param2 = node.param2})
0f6bdb 50
S 51     -- Apply stored metadata to the current node
52     local meta = minetest.get_meta(pos)
53     local inv = meta:get_inventory()
54     for key, value in pairs(data.metas) do
55         local meta_type = get_meta_type(data.name, key)
731a82 56         if meta_type == wrench.META_TYPE_INT then
0f6bdb 57             meta:set_int(key, value)
731a82 58         elseif meta_type == wrench.META_TYPE_FLOAT then
0f6bdb 59             meta:set_float(key, value)
731a82 60         elseif meta_type == wrench.META_TYPE_STRING then
0f6bdb 61             meta:set_string(key, value)
731a82 62         end
S 63     end
0f6bdb 64
S 65     for listname, list in pairs(data.lists) do
4874e2 66         inv:set_list(listname, list)
R 67     end
731a82 68     itemstack:take_item()
S 69     return itemstack
70 end
71
9b7c44 72 minetest.register_on_mods_loaded(function()
S 73     -- Delayed registration for foreign mod support
74     for name, info in pairs(wrench.registered_nodes) do
75         local olddef = minetest.registered_nodes[name]
76         if olddef then
77             local newdef = {}
78             for key, value in pairs(olddef) do
79                 newdef[key] = value
80             end
81             newdef.stack_max = 1
82             newdef.description = S("%s with items"):format(newdef.description)
83             newdef.groups = {}
84             newdef.groups.not_in_creative_inventory = 1
85             newdef.on_construct = nil
86             newdef.on_destruct = nil
87             newdef.after_place_node = restore
88             minetest.register_node(":"..get_pickup_name(name), newdef)
21c96a 89         end
M 90     end
9b7c44 91 end)
21c96a 92
M 93 minetest.register_tool("wrench:wrench", {
39c41a 94     description = S("Wrench"),
21c96a 95     inventory_image = "technic_wrench.png",
M 96     tool_capabilities = {
97         full_punch_interval = 0.9,
98         max_drop_level = 0,
99         groupcaps = {
100             crumbly = {times={[2]=3.00, [3]=0.70}, uses=0, maxlevel=1},
101             snappy = {times={[3]=0.40}, uses=0, maxlevel=1},
731a82 102             oddly_breakable_by_hand = {times={[1]=7.00,[2]=4.00,[3]=1.40},
S 103                         uses=0, maxlevel=3}
21c96a 104         },
M 105         damage_groups = {fleshy=1},
106     },
107     on_place = function(itemstack, placer, pointed_thing)
108         local pos = pointed_thing.under
731a82 109         if not placer or not pos then
S 110             return
111         end
9167d4 112         local player_name = placer:get_player_name()
D 113         if minetest.is_protected(pos, player_name) then
114             minetest.record_protection_violation(pos, player_name)
731a82 115             return
S 116         end
0f6bdb 117         local node_name = minetest.get_node(pos).name
S 118         local def = wrench.registered_nodes[node_name]
731a82 119         if not def then
S 120             return
121         end
122
0f6bdb 123         local stack_pickup = ItemStack(get_pickup_name(node_name))
731a82 124         local player_inv = placer:get_inventory()
0f6bdb 125         if not player_inv:room_for_item("main", stack_pickup) then
731a82 126             return
S 127         end
3b7695 128         local meta = minetest.get_meta(pos)
395089 129         if def.owned and not minetest.check_player_privs(placer, "protection_bypass") then
d5c554 130             local owner = meta:get_string("owner")
9167d4 131             if owner and owner ~= player_name then
D 132                 minetest.log("action", player_name..
133                     " tried to pick up an owned node belonging to "..
d5c554 134                     owner.." at "..
3b7695 135                     minetest.pos_to_string(pos))
731a82 136                 return
21c96a 137             end
M 138         end
731a82 139
0f6bdb 140         -- Do the actual pickup:
731a82 141         local metadata = {}
0f6bdb 142         metadata.name = node_name
c5a2f0 143         metadata.version = LATEST_SERIALIZATION_VERSION
S 144
0f6bdb 145         -- Serialize inventory lists + items
3b7695 146         local inv = meta:get_inventory()
731a82 147         local lists = {}
S 148         for _, listname in pairs(def.lists or {}) do
149             local list = inv:get_list(listname)
150             for i, stack in pairs(list) do
151                 list[i] = stack:to_string()
152             end
153             lists[listname] = list
21c96a 154         end
731a82 155         metadata.lists = lists
9167d4 156
0f6bdb 157         -- Serialize node metadata fields
S 158         local item_meta = stack_pickup:get_meta()
9167d4 159         metadata.metas = {}
0f6bdb 160         for key, meta_type in pairs(def.metas or {}) do
731a82 161             if meta_type == wrench.META_TYPE_INT then
0f6bdb 162                 metadata.metas[key] = meta:get_int(key)
731a82 163             elseif meta_type == wrench.META_TYPE_FLOAT then
0f6bdb 164                 metadata.metas[key] = meta:get_float(key)
731a82 165             elseif meta_type == wrench.META_TYPE_STRING then
0f6bdb 166                 metadata.metas[key] = meta:get_string(key)
3b7695 167             end
M 168         end
9167d4 169
D 170         item_meta:set_string("data", minetest.serialize(metadata))
731a82 171         minetest.remove_node(pos)
S 172         itemstack:add_wear(65535 / 20)
0f6bdb 173         player_inv:add_item("main", stack_pickup)
21c96a 174         return itemstack
M 175     end,
3b32bf 176 })