Update on_place callbacks when placing hopper
[MineClone/MineClone2.git] / mods / ITEMS / mcl_hoppers / init.lua
blob235ddbae2ff04f70c8ff9b4553a929eae98ed48f
1 local chest = minetest.get_content_id("mcl_chests:chest")
4 --[[ BEGIN OF NODE DEFINITIONS ]]
6 local mcl_hoppers_formspec =
7 "size[9,7]"..
8 "background[-0.19,-0.25;9.41,10.48;mcl_hoppers_inventory.png]"..
9 mcl_vars.inventory_header..
10 "list[current_name;main;2,0.5;5,1;]"..
11 "list[current_player;main;0,2.5;9,3;9]"..
12 "list[current_player;main;0,5.74;9,1;]"..
13 "listring[current_name;main]"..
14 "listring[current_player;main]"
16 local redstone_rules =
17 {{x= 1, y= 0, z= 0},
18 {x=-1, y= 0, z= 0},
19 {x= 0, y= 1, z= 0},
20 {x= 0, y= -1, z= 0},
21 {x= 0, y= 0, z= 1},
22 {x= 0, y= 0, z=-1}}
24 -- Downwards hopper (base definition)
26 local def_hopper = {
27 inventory_image = "mcl_hoppers_item.png",
28 wield_image = "mcl_hoppers_item.png",
29 groups = {pickaxey=1, container=2,deco_block=1,},
30 drawtype = "nodebox",
31 paramtype = "light",
32 sunlight_propagates = true,
33 tiles = {"mcl_hoppers_hopper_inside.png^mcl_hoppers_hopper_top.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_inside.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_outside.png"},
34 node_box = {
35 type = "fixed",
36 fixed = {
37 --funnel walls
38 {-0.5, 0.0, 0.4, 0.5, 0.5, 0.5},
39 {0.4, 0.0, -0.5, 0.5, 0.5, 0.5},
40 {-0.5, 0.0, -0.5, -0.4, 0.5, 0.5},
41 {-0.5, 0.0, -0.5, 0.5, 0.5, -0.4},
42 --funnel base
43 {-0.5, 0.0, -0.5, 0.5, 0.1, 0.5},
44 --spout
45 {-0.3, -0.3, -0.3, 0.3, 0.0, 0.3},
46 {-0.1, -0.3, -0.1, 0.1, -0.5, 0.1},
49 selection_box = {
50 type = "fixed",
51 fixed = {
52 --funnel
53 {-0.5, 0.0, -0.5, 0.5, 0.5, 0.5},
54 --spout
55 {-0.3, -0.3, -0.3, 0.3, 0.0, 0.3},
56 {-0.1, -0.3, -0.1, 0.1, -0.5, 0.1},
59 is_ground_content = false,
61 on_construct = function(pos)
62 local meta = minetest.get_meta(pos)
63 meta:set_string("formspec", mcl_hoppers_formspec)
64 local inv = meta:get_inventory()
65 inv:set_size("main", 5)
66 end,
68 after_dig_node = function(pos, oldnode, oldmetadata, digger)
69 local meta = minetest.get_meta(pos)
70 local meta2 = meta
71 meta:from_table(oldmetadata)
72 local inv = meta:get_inventory()
73 for i=1,inv:get_size("main") do
74 local stack = inv:get_stack("main", i)
75 if not stack:is_empty() then
76 local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
77 minetest.add_item(p, stack)
78 end
79 end
80 meta:from_table(meta2:to_table())
81 end,
82 on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
83 minetest.log("action", player:get_player_name()..
84 " moves stuff in mcl_hoppers at "..minetest.pos_to_string(pos))
85 end,
86 on_metadata_inventory_put = function(pos, listname, index, stack, player)
87 minetest.log("action", player:get_player_name()..
88 " moves stuff to mcl_hoppers at "..minetest.pos_to_string(pos))
89 end,
90 on_metadata_inventory_take = function(pos, listname, index, stack, player)
91 minetest.log("action", player:get_player_name()..
92 " takes stuff from mcl_hoppers at "..minetest.pos_to_string(pos))
93 end,
94 sounds = mcl_sounds.node_sound_metal_defaults(),
96 _mcl_blast_resistance = 24,
97 _mcl_hardness = 3,
100 -- Redstone variants (on/off) of downwards hopper.
101 -- Note a hopper is enabled when it is *not* supplied with redstone power and disabled when it is supplied with redstone power.
103 -- Enabled downwards hopper
104 local def_hopper_enabled = table.copy(def_hopper)
105 def_hopper_enabled.description = "Hopper"
106 def_hopper_enabled._doc_items_longdesc = [[Hoppers are containers with 5 inventory slots. They collect dropped items from above, take items from a container above and attempts to put its items it into an adjacent container. Hoppers can go either downwards or sideways. Hoppers interact with chests, droppers, dispensers, shulker boxes, furnaces and hoppers.
108 Hoppers interact with containers the following way:
109 • Furnaces: Hoppers from above will put items into the source slot. Hoppers from below take items from the output slot. They also take items from the fuel slot when they can't be used as a fuel. Sideway hoppers put items into the fuel slot
110 • Ender chests: Hoppers don't interact with ender chests
111 • Other containers: Hoppers interact with them normally
113 Hoppers can be disabled by supplying them with redstone power. Disabled hoppers don't move items.]]
114 def_hopper_enabled._doc_items_usagehelp = "To place a hopper vertically, place it on the floor or a ceiling. To place it sideways, place it at the side of a block. Remember you can place at usable blocks (such as chests) with sneak + right-click. The hopper will keep its orientation when the blocks around it are changed. To access the hopper's inventory, rightclick it."
115 def_hopper_enabled.on_place = function(itemstack, placer, pointed_thing)
116 local upos = pointed_thing.under
117 local apos = pointed_thing.above
119 local uposnode = minetest.get_node(upos)
120 local uposnodedef = minetest.registered_nodes[uposnode.name]
121 if not uposnodedef then return itemstack end
122 -- Use pointed node's on_rightclick function first, if present
123 if placer and not placer:get_player_control().sneak then
124 if uposnodedef and uposnodedef.on_rightclick then
125 return uposnodedef.on_rightclick(pointed_thing.under, uposnode, placer, itemstack) or itemstack
129 local x = upos.x - apos.x
130 local z = upos.z - apos.z
132 local fake_itemstack = ItemStack(itemstack)
133 local newnode, param2
134 if x == -1 then
135 fake_itemstack:set_name("mcl_hoppers:hopper_side")
136 param2 = 0
137 elseif x == 1 then
138 fake_itemstack:set_name("mcl_hoppers:hopper_side")
139 param2 = 2
140 elseif z == -1 then
141 fake_itemstack:set_name("mcl_hoppers:hopper_side")
142 param2 = 3
143 elseif z == 1 then
144 fake_itemstack:set_name("mcl_hoppers:hopper_side")
145 param2 = 1
147 local itemstack, success = minetest.item_place_node(fake_itemstack, placer, pointed_thing, param2)
148 itemstack:set_name("mcl_hoppers:hopper")
149 return itemstack
151 def_hopper_enabled.mesecons = {
152 effector = {
153 action_on = function(pos, node)
154 minetest.swap_node(pos, {name="mcl_hoppers:hopper_disabled", param2=node.param2})
155 end,
156 rules = redstone_rules,
160 minetest.register_node("mcl_hoppers:hopper", def_hopper_enabled)
162 -- Disabled downwards hopper
163 local def_hopper_disabled = table.copy(def_hopper)
164 def_hopper_disabled.description = "Disabled Hopper"
165 def_hopper_disabled._doc_items_create_entry = false
166 def_hopper_disabled.groups.not_in_creative_inventory = 1
167 def_hopper_disabled.drop = "mcl_hoppers:hopper"
168 def_hopper_disabled.mesecons = {
169 effector = {
170 action_off = function(pos, node)
171 minetest.swap_node(pos, {name="mcl_hoppers:hopper", param2=node.param2})
172 end,
173 rules = redstone_rules,
177 minetest.register_node("mcl_hoppers:hopper_disabled", def_hopper_disabled)
181 local on_rotate
182 if minetest.get_modpath("screwdriver") then
183 on_rotate = screwdriver.rotate_simple
186 -- Sidewars hopper (base definition)
187 local def_hopper_side = {
188 _doc_items_create_entry = false,
189 drop = "mcl_hoppers:hopper",
190 groups = {pickaxey=1, container=2,not_in_creative_inventory=1},
191 drawtype = "nodebox",
192 paramtype = "light",
193 sunlight_propagates = true,
194 paramtype2 = "facedir",
195 tiles = {"mcl_hoppers_hopper_inside.png^mcl_hoppers_hopper_top.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_inside.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_outside.png"},
196 node_box = {
197 type = "fixed",
198 fixed = {
199 --funnel walls
200 {-0.5, 0.0, 0.4, 0.5, 0.5, 0.5},
201 {0.4, 0.0, -0.5, 0.5, 0.5, 0.5},
202 {-0.5, 0.0, -0.5, -0.4, 0.5, 0.5},
203 {-0.5, 0.0, -0.5, 0.5, 0.5, -0.4},
204 --funnel base
205 {-0.5, 0.0, -0.5, 0.5, 0.1, 0.5},
206 --spout
207 {-0.3, -0.3, -0.3, 0.3, 0.0, 0.3},
208 {-0.5, -0.3, -0.1, 0.1, -0.1, 0.1},
211 selection_box = {
212 type = "fixed",
213 fixed = {
214 --funnel
215 {-0.5, 0.0, -0.5, 0.5, 0.5, 0.5},
216 --spout
217 {-0.3, -0.3, -0.3, 0.3, 0.0, 0.3},
218 {-0.5, -0.3, -0.1, 0.1, -0.1, 0.1},
221 is_ground_content = false,
223 on_construct = function(pos)
224 local meta = minetest.get_meta(pos)
225 meta:set_string("formspec", mcl_hoppers_formspec)
226 local inv = meta:get_inventory()
227 inv:set_size("main", 5)
228 end,
230 after_dig_node = function(pos, oldnode, oldmetadata, digger)
231 local meta = minetest.get_meta(pos)
232 local meta2 = meta
233 meta:from_table(oldmetadata)
234 local inv = meta:get_inventory()
235 for i=1,inv:get_size("main") do
236 local stack = inv:get_stack("main", i)
237 if not stack:is_empty() then
238 local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
239 minetest.add_item(p, stack)
242 meta:from_table(meta2:to_table())
243 end,
244 on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
245 minetest.log("action", player:get_player_name()..
246 " moves stuff in mcl_hoppers at "..minetest.pos_to_string(pos))
247 end,
248 on_metadata_inventory_put = function(pos, listname, index, stack, player)
249 minetest.log("action", player:get_player_name()..
250 " moves stuff to mcl_hoppers at "..minetest.pos_to_string(pos))
251 end,
252 on_metadata_inventory_take = function(pos, listname, index, stack, player)
253 minetest.log("action", player:get_player_name()..
254 " takes stuff from mcl_hoppers at "..minetest.pos_to_string(pos))
255 end,
256 on_rotate = on_rotate,
257 sounds = mcl_sounds.node_sound_metal_defaults(),
259 _mcl_blast_resistance = 24,
260 _mcl_hardness = 3,
263 local def_hopper_side_enabled = table.copy(def_hopper_side)
264 def_hopper_side_enabled.description = "Side Hopper"
265 def_hopper_side_enabled.mesecons = {
266 effector = {
267 action_on = function(pos, node)
268 minetest.swap_node(pos, {name="mcl_hoppers:hopper_side_disabled", param2=node.param2})
269 end,
270 rules = redstone_rules,
273 minetest.register_node("mcl_hoppers:hopper_side", def_hopper_side_enabled)
275 local def_hopper_side_disabled = table.copy(def_hopper_side)
276 def_hopper_side_disabled.description = "Disabled Side Hopper"
277 def_hopper_side_disabled.mesecons = {
278 effector = {
279 action_off = function(pos, node)
280 minetest.swap_node(pos, {name="mcl_hoppers:hopper_side", param2=node.param2})
281 end,
282 rules = redstone_rules,
285 minetest.register_node("mcl_hoppers:hopper_side_disabled", def_hopper_side_disabled)
287 --[[ END OF NODE DEFINITIONS ]]
289 --[[ BEGIN OF ABM DEFINITONS ]]
291 -- Make hoppers suck in dropped items
292 minetest.register_abm({
293 label = "Hoppers suck in dropped items",
294 nodenames = {"mcl_hoppers:hopper","mcl_hoppers:hopper_side"},
295 interval = 1.0,
296 chance = 1,
297 action = function(pos, node, active_object_count, active_object_count_wider)
298 local abovenode = minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z})
299 if not minetest.registered_items[abovenode.name] then return end
300 -- Don't bother checking item enties if node above is a container (should save some CPU)
301 if minetest.registered_items[abovenode.name].groups.container then
302 return
304 local meta = minetest.get_meta(pos)
305 local inv = meta:get_inventory()
307 for _,object in ipairs(minetest.get_objects_inside_radius(pos, 2)) do
308 if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" then
309 if inv and inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then
310 -- Item must get sucked in when the item just TOUCHES the block above the hopper
311 -- This is the reason for the Y calculation.
312 -- Test: Items on farmland and slabs get sucked, but items on full blocks don't
313 local posob = object:getpos()
314 local posob_miny = posob.y + object:get_properties().collisionbox[2]
315 if math.abs(posob.x-pos.x) <= 0.5 and (posob_miny-pos.y < 1.5 and posob.y-pos.y >= 0.3) then
316 inv:add_item("main", ItemStack(object:get_luaentity().itemstring))
317 object:get_luaentity().itemstring = ""
318 object:remove()
323 end,
326 -- Returns true if itemstack is fuel, but not for lava bucket if destination already has one
327 local is_transferrable_fuel = function(itemstack, src_inventory, src_list, dst_inventory, dst_list)
328 if mcl_util.is_fuel(itemstack) then
329 if itemstack:get_name() == "mcl_buckets:bucket_lava" then
330 return dst_inventory:is_empty(dst_list)
331 else
332 return true
334 else
335 return false
341 minetest.register_abm({
342 label = "Hopper/container item exchange",
343 nodenames = {"mcl_hoppers:hopper"},
344 neighbors = {"group:container"},
345 interval = 1.0,
346 chance = 1,
347 action = function(pos, node, active_object_count, active_object_count_wider)
348 -- Get node pos' for item transfer
349 local uppos = {x=pos.x,y=pos.y+1,z=pos.z}
350 local downpos = {x=pos.x,y=pos.y-1,z=pos.z}
352 -- Suck an item from the container above into the hopper
353 local upnode = minetest.get_node(uppos)
354 if not minetest.registered_nodes[upnode.name] then return end
355 local g = minetest.registered_nodes[upnode.name].groups.container
356 local sucked = mcl_util.move_item_container(uppos, pos)
358 -- Also suck in non-fuel items from furnace fuel slot
359 if not sucked and g == 4 then
360 local finv = minetest.get_inventory({type="node", pos=uppos})
361 if finv and not mcl_util.is_fuel(finv:get_stack("fuel", 1)) then
362 mcl_util.move_item_container(uppos, pos, "fuel")
366 -- Move an item from the hopper into container below
367 local downnode = minetest.get_node(downpos)
368 if not minetest.registered_nodes[downnode.name] then return end
369 g = minetest.registered_nodes[downnode.name].groups.container
370 mcl_util.move_item_container(pos, downpos)
371 end,
374 minetest.register_abm({
375 label = "Side-hopper/container item exchange",
376 nodenames = {"mcl_hoppers:hopper_side"},
377 neighbors = {"group:container"},
378 interval = 1.0,
379 chance = 1,
380 action = function(pos, node, active_object_count, active_object_count_wider)
381 -- Determine to which side the hopper is facing, get nodes
382 local face = minetest.get_node(pos).param2
383 local front = {}
384 if face == 0 then
385 front = {x=pos.x-1,y=pos.y,z=pos.z}
386 elseif face == 1 then
387 front = {x=pos.x,y=pos.y,z=pos.z+1}
388 elseif face == 2 then
389 front = {x=pos.x+1,y=pos.y,z=pos.z}
390 elseif face == 3 then
391 front = {x=pos.x,y=pos.y,z=pos.z-1}
393 local above = {x=pos.x,y=pos.y+1,z=pos.z}
395 local frontnode = minetest.get_node(front)
396 if not minetest.registered_nodes[frontnode.name] then return end
398 -- Suck an item from the container above into the hopper
399 local abovenode = minetest.get_node(above)
400 if not minetest.registered_nodes[abovenode.name] then return end
401 local g = minetest.registered_nodes[abovenode.name].groups.container
402 mcl_util.move_item_container(above, pos)
404 -- Move an item from the hopper into the container to which the hopper points to
405 local g = minetest.registered_nodes[frontnode.name].groups.container
406 if g == 2 or g == 3 or g == 5 or g == 6 then
407 mcl_util.move_item_container(pos, front)
408 elseif g == 4 then
409 -- Put fuel into fuel slot
410 local sinv = minetest.get_inventory({type="node", pos = pos})
411 local dinv = minetest.get_inventory({type="node", pos = front})
412 local slot_id, stack = mcl_util.get_eligible_transfer_item_slot(sinv, "main", dinv, "fuel", is_transferrable_fuel)
413 if slot_id then
414 mcl_util.move_item_container(pos, front, nil, slot_id, "fuel")
420 minetest.register_craft({
421 output = "mcl_hoppers:hopper",
422 recipe = {
423 {"mcl_core:iron_ingot","","mcl_core:iron_ingot"},
424 {"mcl_core:iron_ingot","mcl_chests:chest","mcl_core:iron_ingot"},
425 {"","mcl_core:iron_ingot",""},
429 -- Add entry aliases for the Help
430 if minetest.get_modpath("doc") then
431 doc.add_entry_alias("nodes", "mcl_hoppers:hopper", "nodes", "mcl_hoppers:hopper_side")
434 -- Legacy
435 minetest.register_alias("mcl_hoppers:hopper_item", "mcl_hoppers:hopper")