1-- Minetest: builtin/item.lua
2
3local builtin_shared = ...
4
5local function copy_pointed_thing(pointed_thing)
6	return {
7		type  = pointed_thing.type,
8		above = vector.new(pointed_thing.above),
9		under = vector.new(pointed_thing.under),
10		ref   = pointed_thing.ref,
11	}
12end
13
14--
15-- Item definition helpers
16--
17
18function core.inventorycube(img1, img2, img3)
19	img2 = img2 or img1
20	img3 = img3 or img1
21	return "[inventorycube"
22			.. "{" .. img1:gsub("%^", "&")
23			.. "{" .. img2:gsub("%^", "&")
24			.. "{" .. img3:gsub("%^", "&")
25end
26
27function core.get_pointed_thing_position(pointed_thing, above)
28	if pointed_thing.type == "node" then
29		if above then
30			-- The position where a node would be placed
31			return pointed_thing.above
32		end
33		-- The position where a node would be dug
34		return pointed_thing.under
35	elseif pointed_thing.type == "object" then
36		return pointed_thing.ref and pointed_thing.ref:get_pos()
37	end
38end
39
40function core.dir_to_facedir(dir, is6d)
41	--account for y if requested
42	if is6d and math.abs(dir.y) > math.abs(dir.x) and math.abs(dir.y) > math.abs(dir.z) then
43
44		--from above
45		if dir.y < 0 then
46			if math.abs(dir.x) > math.abs(dir.z) then
47				if dir.x < 0 then
48					return 19
49				else
50					return 13
51				end
52			else
53				if dir.z < 0 then
54					return 10
55				else
56					return 4
57				end
58			end
59
60		--from below
61		else
62			if math.abs(dir.x) > math.abs(dir.z) then
63				if dir.x < 0 then
64					return 15
65				else
66					return 17
67				end
68			else
69				if dir.z < 0 then
70					return 6
71				else
72					return 8
73				end
74			end
75		end
76
77	--otherwise, place horizontally
78	elseif math.abs(dir.x) > math.abs(dir.z) then
79		if dir.x < 0 then
80			return 3
81		else
82			return 1
83		end
84	else
85		if dir.z < 0 then
86			return 2
87		else
88			return 0
89		end
90	end
91end
92
93-- Table of possible dirs
94local facedir_to_dir = {
95	{x= 0, y=0,  z= 1},
96	{x= 1, y=0,  z= 0},
97	{x= 0, y=0,  z=-1},
98	{x=-1, y=0,  z= 0},
99	{x= 0, y=-1, z= 0},
100	{x= 0, y=1,  z= 0},
101}
102-- Mapping from facedir value to index in facedir_to_dir.
103local facedir_to_dir_map = {
104	[0]=1, 2, 3, 4,
105	5, 2, 6, 4,
106	6, 2, 5, 4,
107	1, 5, 3, 6,
108	1, 6, 3, 5,
109	1, 4, 3, 2,
110}
111function core.facedir_to_dir(facedir)
112	return facedir_to_dir[facedir_to_dir_map[facedir % 32]]
113end
114
115function core.dir_to_wallmounted(dir)
116	if math.abs(dir.y) > math.max(math.abs(dir.x), math.abs(dir.z)) then
117		if dir.y < 0 then
118			return 1
119		else
120			return 0
121		end
122	elseif math.abs(dir.x) > math.abs(dir.z) then
123		if dir.x < 0 then
124			return 3
125		else
126			return 2
127		end
128	else
129		if dir.z < 0 then
130			return 5
131		else
132			return 4
133		end
134	end
135end
136
137-- table of dirs in wallmounted order
138local wallmounted_to_dir = {
139	[0] = {x = 0, y = 1, z = 0},
140	{x =  0, y = -1, z =  0},
141	{x =  1, y =  0, z =  0},
142	{x = -1, y =  0, z =  0},
143	{x =  0, y =  0, z =  1},
144	{x =  0, y =  0, z = -1},
145}
146function core.wallmounted_to_dir(wallmounted)
147	return wallmounted_to_dir[wallmounted % 8]
148end
149
150function core.dir_to_yaw(dir)
151	return -math.atan2(dir.x, dir.z)
152end
153
154function core.yaw_to_dir(yaw)
155	return {x = -math.sin(yaw), y = 0, z = math.cos(yaw)}
156end
157
158function core.is_colored_paramtype(ptype)
159	return (ptype == "color") or (ptype == "colorfacedir") or
160		(ptype == "colorwallmounted")
161end
162
163function core.strip_param2_color(param2, paramtype2)
164	if not core.is_colored_paramtype(paramtype2) then
165		return nil
166	end
167	if paramtype2 == "colorfacedir" then
168		param2 = math.floor(param2 / 32) * 32
169	elseif paramtype2 == "colorwallmounted" then
170		param2 = math.floor(param2 / 8) * 8
171	end
172	-- paramtype2 == "color" requires no modification.
173	return param2
174end
175
176function core.get_node_drops(node, toolname)
177	-- Compatibility, if node is string
178	local nodename = node
179	local param2 = 0
180	-- New format, if node is table
181	if (type(node) == "table") then
182		nodename = node.name
183		param2 = node.param2
184	end
185	local def = core.registered_nodes[nodename]
186	local drop = def and def.drop
187	local ptype = def and def.paramtype2
188	-- get color, if there is color (otherwise nil)
189	local palette_index = core.strip_param2_color(param2, ptype)
190	if drop == nil then
191		-- default drop
192		if palette_index then
193			local stack = ItemStack(nodename)
194			stack:get_meta():set_int("palette_index", palette_index)
195			return {stack:to_string()}
196		end
197		return {nodename}
198	elseif type(drop) == "string" then
199		-- itemstring drop
200		return drop ~= "" and {drop} or {}
201	elseif drop.items == nil then
202		-- drop = {} to disable default drop
203		return {}
204	end
205
206	-- Extended drop table
207	local got_items = {}
208	local got_count = 0
209	for _, item in ipairs(drop.items) do
210		local good_rarity = true
211		local good_tool = true
212		if item.rarity ~= nil then
213			good_rarity = item.rarity < 1 or math.random(item.rarity) == 1
214		end
215		if item.tools ~= nil then
216			good_tool = false
217		end
218		if item.tools ~= nil and toolname then
219			for _, tool in ipairs(item.tools) do
220				if tool:sub(1, 1) == '~' then
221					good_tool = toolname:find(tool:sub(2)) ~= nil
222				else
223					good_tool = toolname == tool
224				end
225				if good_tool then
226					break
227				end
228			end
229		end
230		if good_rarity and good_tool then
231			got_count = got_count + 1
232			for _, add_item in ipairs(item.items) do
233				-- add color, if necessary
234				if item.inherit_color and palette_index then
235					local stack = ItemStack(add_item)
236					stack:get_meta():set_int("palette_index", palette_index)
237					add_item = stack:to_string()
238				end
239				got_items[#got_items+1] = add_item
240			end
241			if drop.max_items ~= nil and got_count == drop.max_items then
242				break
243			end
244		end
245	end
246	return got_items
247end
248
249local function user_name(user)
250	return user and user:get_player_name() or ""
251end
252
253-- Returns a logging function. For empty names, does not log.
254local function make_log(name)
255	return name ~= "" and core.log or function() end
256end
257
258function core.item_place_node(itemstack, placer, pointed_thing, param2,
259		prevent_after_place)
260	local def = itemstack:get_definition()
261	if def.type ~= "node" or pointed_thing.type ~= "node" then
262		return itemstack, nil
263	end
264
265	local under = pointed_thing.under
266	local oldnode_under = core.get_node_or_nil(under)
267	local above = pointed_thing.above
268	local oldnode_above = core.get_node_or_nil(above)
269	local playername = user_name(placer)
270	local log = make_log(playername)
271
272	if not oldnode_under or not oldnode_above then
273		log("info", playername .. " tried to place"
274			.. " node in unloaded position " .. core.pos_to_string(above))
275		return itemstack, nil
276	end
277
278	local olddef_under = core.registered_nodes[oldnode_under.name]
279	olddef_under = olddef_under or core.nodedef_default
280	local olddef_above = core.registered_nodes[oldnode_above.name]
281	olddef_above = olddef_above or core.nodedef_default
282
283	if not olddef_above.buildable_to and not olddef_under.buildable_to then
284		log("info", playername .. " tried to place"
285			.. " node in invalid position " .. core.pos_to_string(above)
286			.. ", replacing " .. oldnode_above.name)
287		return itemstack, nil
288	end
289
290	-- Place above pointed node
291	local place_to = {x = above.x, y = above.y, z = above.z}
292
293	-- If node under is buildable_to, place into it instead (eg. snow)
294	if olddef_under.buildable_to then
295		log("info", "node under is buildable to")
296		place_to = {x = under.x, y = under.y, z = under.z}
297	end
298
299	if core.is_protected(place_to, playername) then
300		log("action", playername
301				.. " tried to place " .. def.name
302				.. " at protected position "
303				.. core.pos_to_string(place_to))
304		core.record_protection_violation(place_to, playername)
305		return itemstack, nil
306	end
307
308	local oldnode = core.get_node(place_to)
309	local newnode = {name = def.name, param1 = 0, param2 = param2 or 0}
310
311	-- Calculate direction for wall mounted stuff like torches and signs
312	if def.place_param2 ~= nil then
313		newnode.param2 = def.place_param2
314	elseif (def.paramtype2 == "wallmounted" or
315			def.paramtype2 == "colorwallmounted") and not param2 then
316		local dir = {
317			x = under.x - above.x,
318			y = under.y - above.y,
319			z = under.z - above.z
320		}
321		newnode.param2 = core.dir_to_wallmounted(dir)
322	-- Calculate the direction for furnaces and chests and stuff
323	elseif (def.paramtype2 == "facedir" or
324			def.paramtype2 == "colorfacedir") and not param2 then
325		local placer_pos = placer and placer:get_pos()
326		if placer_pos then
327			local dir = {
328				x = above.x - placer_pos.x,
329				y = above.y - placer_pos.y,
330				z = above.z - placer_pos.z
331			}
332			newnode.param2 = core.dir_to_facedir(dir)
333			log("info", "facedir: " .. newnode.param2)
334		end
335	end
336
337	local metatable = itemstack:get_meta():to_table().fields
338
339	-- Transfer color information
340	if metatable.palette_index and not def.place_param2 then
341		local color_divisor = nil
342		if def.paramtype2 == "color" then
343			color_divisor = 1
344		elseif def.paramtype2 == "colorwallmounted" then
345			color_divisor = 8
346		elseif def.paramtype2 == "colorfacedir" then
347			color_divisor = 32
348		end
349		if color_divisor then
350			local color = math.floor(metatable.palette_index / color_divisor)
351			local other = newnode.param2 % color_divisor
352			newnode.param2 = color * color_divisor + other
353		end
354	end
355
356	-- Check if the node is attached and if it can be placed there
357	if core.get_item_group(def.name, "attached_node") ~= 0 and
358		not builtin_shared.check_attached_node(place_to, newnode) then
359		log("action", "attached node " .. def.name ..
360			" can not be placed at " .. core.pos_to_string(place_to))
361		return itemstack, nil
362	end
363
364	log("action", playername .. " places node "
365		.. def.name .. " at " .. core.pos_to_string(place_to))
366
367	-- Add node and update
368	core.add_node(place_to, newnode)
369
370	-- Play sound if it was done by a player
371	if playername ~= "" and def.sounds and def.sounds.place then
372		core.sound_play(def.sounds.place, {
373			pos = place_to,
374			exclude_player = playername,
375		}, true)
376	end
377
378	local take_item = true
379
380	-- Run callback
381	if def.after_place_node and not prevent_after_place then
382		-- Deepcopy place_to and pointed_thing because callback can modify it
383		local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
384		local pointed_thing_copy = copy_pointed_thing(pointed_thing)
385		if def.after_place_node(place_to_copy, placer, itemstack,
386				pointed_thing_copy) then
387			take_item = false
388		end
389	end
390
391	-- Run script hook
392	for _, callback in ipairs(core.registered_on_placenodes) do
393		-- Deepcopy pos, node and pointed_thing because callback can modify them
394		local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
395		local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2}
396		local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2}
397		local pointed_thing_copy = copy_pointed_thing(pointed_thing)
398		if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack, pointed_thing_copy) then
399			take_item = false
400		end
401	end
402
403	if take_item then
404		itemstack:take_item()
405	end
406	return itemstack, place_to
407end
408
409-- deprecated, item_place does not call this
410function core.item_place_object(itemstack, placer, pointed_thing)
411	local pos = core.get_pointed_thing_position(pointed_thing, true)
412	if pos ~= nil then
413		local item = itemstack:take_item()
414		core.add_item(pos, item)
415	end
416	return itemstack
417end
418
419function core.item_place(itemstack, placer, pointed_thing, param2)
420	-- Call on_rightclick if the pointed node defines it
421	if pointed_thing.type == "node" and placer and
422			not placer:get_player_control().sneak then
423		local n = core.get_node(pointed_thing.under)
424		local nn = n.name
425		if core.registered_nodes[nn] and core.registered_nodes[nn].on_rightclick then
426			return core.registered_nodes[nn].on_rightclick(pointed_thing.under, n,
427					placer, itemstack, pointed_thing) or itemstack, nil
428		end
429	end
430
431	-- Place if node, otherwise do nothing
432	if itemstack:get_definition().type == "node" then
433		return core.item_place_node(itemstack, placer, pointed_thing, param2)
434	end
435	return itemstack, nil
436end
437
438function core.item_secondary_use(itemstack, placer)
439	return itemstack
440end
441
442function core.item_drop(itemstack, dropper, pos)
443	local dropper_is_player = dropper and dropper:is_player()
444	local p = table.copy(pos)
445	local cnt = itemstack:get_count()
446	if dropper_is_player then
447		p.y = p.y + 1.2
448	end
449	local item = itemstack:take_item(cnt)
450	local obj = core.add_item(p, item)
451	if obj then
452		if dropper_is_player then
453			local dir = dropper:get_look_dir()
454			dir.x = dir.x * 2.9
455			dir.y = dir.y * 2.9 + 2
456			dir.z = dir.z * 2.9
457			obj:set_velocity(dir)
458			obj:get_luaentity().dropped_by = dropper:get_player_name()
459		end
460		return itemstack
461	end
462	-- If we reach this, adding the object to the
463	-- environment failed
464end
465
466function core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
467	for _, callback in pairs(core.registered_on_item_eats) do
468		local result = callback(hp_change, replace_with_item, itemstack, user, pointed_thing)
469		if result then
470			return result
471		end
472	end
473	local def = itemstack:get_definition()
474	if itemstack:take_item() ~= nil then
475		user:set_hp(user:get_hp() + hp_change)
476
477		if def and def.sound and def.sound.eat then
478			core.sound_play(def.sound.eat, {
479				pos = user:get_pos(),
480				max_hear_distance = 16
481			}, true)
482		end
483
484		if replace_with_item then
485			if itemstack:is_empty() then
486				itemstack:add_item(replace_with_item)
487			else
488				local inv = user:get_inventory()
489				-- Check if inv is null, since non-players don't have one
490				if inv and inv:room_for_item("main", {name=replace_with_item}) then
491					inv:add_item("main", replace_with_item)
492				else
493					local pos = user:get_pos()
494					pos.y = math.floor(pos.y + 0.5)
495					core.add_item(pos, replace_with_item)
496				end
497			end
498		end
499	end
500	return itemstack
501end
502
503function core.item_eat(hp_change, replace_with_item)
504	return function(itemstack, user, pointed_thing)  -- closure
505		if user then
506			return core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
507		end
508	end
509end
510
511function core.node_punch(pos, node, puncher, pointed_thing)
512	-- Run script hook
513	for _, callback in ipairs(core.registered_on_punchnodes) do
514		-- Copy pos and node because callback can modify them
515		local pos_copy = vector.new(pos)
516		local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
517		local pointed_thing_copy = pointed_thing and copy_pointed_thing(pointed_thing) or nil
518		callback(pos_copy, node_copy, puncher, pointed_thing_copy)
519	end
520end
521
522function core.handle_node_drops(pos, drops, digger)
523	-- Add dropped items to object's inventory
524	local inv = digger and digger:get_inventory()
525	local give_item
526	if inv then
527		give_item = function(item)
528			return inv:add_item("main", item)
529		end
530	else
531		give_item = function(item)
532			-- itemstring to ItemStack for left:is_empty()
533			return ItemStack(item)
534		end
535	end
536
537	for _, dropped_item in pairs(drops) do
538		local left = give_item(dropped_item)
539		if not left:is_empty() then
540			local p = {
541				x = pos.x + math.random()/2-0.25,
542				y = pos.y + math.random()/2-0.25,
543				z = pos.z + math.random()/2-0.25,
544			}
545			core.add_item(p, left)
546		end
547	end
548end
549
550function core.node_dig(pos, node, digger)
551	local diggername = user_name(digger)
552	local log = make_log(diggername)
553	local def = core.registered_nodes[node.name]
554	-- Copy pos because the callback could modify it
555	if def and (not def.diggable or
556			(def.can_dig and not def.can_dig(vector.new(pos), digger))) then
557		log("info", diggername .. " tried to dig "
558			.. node.name .. " which is not diggable "
559			.. core.pos_to_string(pos))
560		return false
561	end
562
563	if core.is_protected(pos, diggername) then
564		log("action", diggername
565				.. " tried to dig " .. node.name
566				.. " at protected position "
567				.. core.pos_to_string(pos))
568		core.record_protection_violation(pos, diggername)
569		return false
570	end
571
572	log('action', diggername .. " digs "
573		.. node.name .. " at " .. core.pos_to_string(pos))
574
575	local wielded = digger and digger:get_wielded_item()
576	local drops = core.get_node_drops(node, wielded and wielded:get_name())
577
578	if wielded then
579		local wdef = wielded:get_definition()
580		local tp = wielded:get_tool_capabilities()
581		local dp = core.get_dig_params(def and def.groups, tp)
582		if wdef and wdef.after_use then
583			wielded = wdef.after_use(wielded, digger, node, dp) or wielded
584		else
585			-- Wear out tool
586			if not core.is_creative_enabled(diggername) then
587				wielded:add_wear(dp.wear)
588				if wielded:get_count() == 0 and wdef.sound and wdef.sound.breaks then
589					core.sound_play(wdef.sound.breaks, {
590						pos = pos,
591						gain = 0.5
592					}, true)
593				end
594			end
595		end
596		digger:set_wielded_item(wielded)
597	end
598
599	-- Check to see if metadata should be preserved.
600	if def and def.preserve_metadata then
601		local oldmeta = core.get_meta(pos):to_table().fields
602		-- Copy pos and node because the callback can modify them.
603		local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
604		local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
605		local drop_stacks = {}
606		for k, v in pairs(drops) do
607			drop_stacks[k] = ItemStack(v)
608		end
609		drops = drop_stacks
610		def.preserve_metadata(pos_copy, node_copy, oldmeta, drops)
611	end
612
613	-- Handle drops
614	core.handle_node_drops(pos, drops, digger)
615
616	local oldmetadata = nil
617	if def and def.after_dig_node then
618		oldmetadata = core.get_meta(pos):to_table()
619	end
620
621	-- Remove node and update
622	core.remove_node(pos)
623
624	-- Play sound if it was done by a player
625	if diggername ~= "" and def and def.sounds and def.sounds.dug then
626		core.sound_play(def.sounds.dug, {
627			pos = pos,
628			exclude_player = diggername,
629		}, true)
630	end
631
632	-- Run callback
633	if def and def.after_dig_node then
634		-- Copy pos and node because callback can modify them
635		local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
636		local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
637		def.after_dig_node(pos_copy, node_copy, oldmetadata, digger)
638	end
639
640	-- Run script hook
641	for _, callback in ipairs(core.registered_on_dignodes) do
642		local origin = core.callback_origins[callback]
643		if origin then
644			core.set_last_run_mod(origin.mod)
645		end
646
647		-- Copy pos and node because callback can modify them
648		local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
649		local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
650		callback(pos_copy, node_copy, digger)
651	end
652
653	return true
654end
655
656function core.itemstring_with_palette(item, palette_index)
657	local stack = ItemStack(item) -- convert to ItemStack
658	stack:get_meta():set_int("palette_index", palette_index)
659	return stack:to_string()
660end
661
662function core.itemstring_with_color(item, colorstring)
663	local stack = ItemStack(item) -- convert to ItemStack
664	stack:get_meta():set_string("color", colorstring)
665	return stack:to_string()
666end
667
668-- This is used to allow mods to redefine core.item_place and so on
669-- NOTE: This is not the preferred way. Preferred way is to provide enough
670--       callbacks to not require redefining global functions. -celeron55
671local function redef_wrapper(table, name)
672	return function(...)
673		return table[name](...)
674	end
675end
676
677--
678-- Item definition defaults
679--
680
681local default_stack_max = tonumber(core.settings:get("default_stack_max")) or 99
682
683core.nodedef_default = {
684	-- Item properties
685	type="node",
686	-- name intentionally not defined here
687	description = "",
688	groups = {},
689	inventory_image = "",
690	wield_image = "",
691	wield_scale = {x=1,y=1,z=1},
692	stack_max = default_stack_max,
693	usable = false,
694	liquids_pointable = false,
695	tool_capabilities = nil,
696	node_placement_prediction = nil,
697
698	-- Interaction callbacks
699	on_place = redef_wrapper(core, 'item_place'), -- core.item_place
700	on_drop = redef_wrapper(core, 'item_drop'), -- core.item_drop
701	on_use = nil,
702	can_dig = nil,
703
704	on_punch = redef_wrapper(core, 'node_punch'), -- core.node_punch
705	on_rightclick = nil,
706	on_dig = redef_wrapper(core, 'node_dig'), -- core.node_dig
707
708	on_receive_fields = nil,
709
710	-- Node properties
711	drawtype = "normal",
712	visual_scale = 1.0,
713	-- Don't define these because otherwise the old tile_images and
714	-- special_materials wouldn't be read
715	--tiles ={""},
716	--special_tiles = {
717	--	{name="", backface_culling=true},
718	--	{name="", backface_culling=true},
719	--},
720	post_effect_color = {a=0, r=0, g=0, b=0},
721	paramtype = "none",
722	paramtype2 = "none",
723	is_ground_content = true,
724	sunlight_propagates = false,
725	walkable = true,
726	pointable = true,
727	diggable = true,
728	climbable = false,
729	buildable_to = false,
730	floodable = false,
731	liquidtype = "none",
732	liquid_alternative_flowing = "",
733	liquid_alternative_source = "",
734	liquid_viscosity = 0,
735	drowning = 0,
736	light_source = 0,
737	damage_per_second = 0,
738	selection_box = {type="regular"},
739	legacy_facedir_simple = false,
740	legacy_wallmounted = false,
741}
742
743core.craftitemdef_default = {
744	type="craft",
745	-- name intentionally not defined here
746	description = "",
747	groups = {},
748	inventory_image = "",
749	wield_image = "",
750	wield_scale = {x=1,y=1,z=1},
751	stack_max = default_stack_max,
752	liquids_pointable = false,
753	tool_capabilities = nil,
754
755	-- Interaction callbacks
756	on_place = redef_wrapper(core, 'item_place'), -- core.item_place
757	on_drop = redef_wrapper(core, 'item_drop'), -- core.item_drop
758	on_secondary_use = redef_wrapper(core, 'item_secondary_use'),
759	on_use = nil,
760}
761
762core.tooldef_default = {
763	type="tool",
764	-- name intentionally not defined here
765	description = "",
766	groups = {},
767	inventory_image = "",
768	wield_image = "",
769	wield_scale = {x=1,y=1,z=1},
770	stack_max = 1,
771	liquids_pointable = false,
772	tool_capabilities = nil,
773
774	-- Interaction callbacks
775	on_place = redef_wrapper(core, 'item_place'), -- core.item_place
776	on_secondary_use = redef_wrapper(core, 'item_secondary_use'),
777	on_drop = redef_wrapper(core, 'item_drop'), -- core.item_drop
778	on_use = nil,
779}
780
781core.noneitemdef_default = {  -- This is used for the hand and unknown items
782	type="none",
783	-- name intentionally not defined here
784	description = "",
785	groups = {},
786	inventory_image = "",
787	wield_image = "",
788	wield_scale = {x=1,y=1,z=1},
789	stack_max = default_stack_max,
790	liquids_pointable = false,
791	tool_capabilities = nil,
792
793	-- Interaction callbacks
794	on_place = redef_wrapper(core, 'item_place'),
795	on_secondary_use = redef_wrapper(core, 'item_secondary_use'),
796	on_drop = nil,
797	on_use = nil,
798}
799