1-- Minetest: builtin/item.lua
2
3--
4-- Falling stuff
5--
6
7function node_drop(np, remove_fast)
8			local n2 = minetest.get_node(np)
9			-- If it's not air or liquid, remove node and replace it with
10			-- it's drops
11			if n2.name ~= "air" and (not core.registered_nodes[n2.name] or
12					core.registered_nodes[n2.name].liquidtype == "none") then
13				core.remove_node(np, remove_fast)
14				if core.registered_nodes[n2.name].buildable_to == false then
15					-- Add dropped items
16					local drops = core.get_node_drops(n2.name, "")
17					local _, dropped_item
18					for _, dropped_item in ipairs(drops) do
19						core.add_item(np, dropped_item)
20					end
21				end
22				-- Run script hook
23				local _, callback
24				for _, callback in ipairs(core.registered_on_dignodes) do
25					callback(np, n2, nil)
26				end
27			end
28end
29
30local remove_fast = 0
31
32core.register_entity(":__builtin:falling_node", {
33	initial_properties = {
34		physical = true,
35		collide_with_objects = false,
36		collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
37		visual = "wielditem",
38		textures = {},
39		visual_size = {x=0.667, y=0.667},
40	},
41
42	node = {},
43
44	set_node = function(self, node)
45		self.node = node
46		local stack = ItemStack(node.name)
47		local itemtable = stack:to_table()
48		local itemname = nil
49		if itemtable then
50			itemname = stack:to_table().name
51		end
52		local item_texture = nil
53		local item_type = ""
54		if core.registered_items[itemname] then
55			item_texture = core.registered_items[itemname].inventory_image
56			item_type = core.registered_items[itemname].type
57		end
58		prop = {
59			is_visible = true,
60			textures = {node.name},
61		}
62		self.object:set_properties(prop)
63	end,
64
65	get_staticdata = function(self)
66		return self.node.name
67	end,
68
69	on_activate = function(self, staticdata)
70		self.object:set_armor_groups({immortal=1})
71		self:set_node({name=staticdata})
72	end,
73
74	on_step = function(self, dtime)
75		if dtime > 0.2 then remove_fast = 2 else remove_fast = 0 end
76		-- Set gravity
77		self.object:setacceleration({x=0, y=-10, z=0})
78		-- Turn to actual sand when collides to ground or just move
79		local pos = self.object:getpos()
80		local bcp = {x=pos.x, y=pos.y-0.7, z=pos.z} -- Position of bottom center point
81		local bcn = core.get_node(bcp)
82		local bcd = core.registered_nodes[bcn.name]
83		-- Note: walkable is in the node definition, not in item groups
84		if not bcd or
85				(bcd.walkable or
86				(core.get_item_group(self.node.name, "float") ~= 0 and
87				bcd.liquidtype ~= "none")) then
88			if bcd and bcd.leveled and bcd.leveled > 0 and
89					bcn.name == self.node.name then
90				local addlevel = self.node.level
91				if addlevel == nil or addlevel <= 0 then
92					addlevel = bcd.leveled
93				end
94				if core.add_node_level(bcp, addlevel) == 0 then
95					self.object:remove()
96					return
97				end
98			elseif bcd and bcd.buildable_to and
99					(core.get_item_group(self.node.name, "float") == 0 or
100					bcd.liquidtype == "none") then
101				core.remove_node(bcp, remove_fast)
102				return
103			end
104			local np = {x=bcp.x, y=bcp.y+1, z=bcp.z}
105			-- Check what's here
106			local n2 = core.get_node(np)
107		-- remove node and replace it with it's drops
108				node_drop(np, remove_fast)
109			-- Create node and remove entity
110			core.add_node(np, self.node)
111			self.object:remove()
112			nodeupdate(np)
113			return
114		end
115		local vel = self.object:getvelocity()
116		if vector.equals(vel, {x=0,y=0,z=0}) then
117			local npos = self.object:getpos()
118			self.object:setpos(vector.round(npos))
119		end
120	end
121})
122
123function spawn_falling_node(p, node)
124	obj = core.add_entity(p, "__builtin:falling_node")
125	if not obj then return end
126	obj:get_luaentity():set_node(node)
127end
128
129function drop_attached_node(p)
130	local nn = core.get_node(p).name
131	core.remove_node(p, remove_fast)
132	for _,item in ipairs(core.get_node_drops(nn, "")) do
133		local pos = {
134			x = p.x + math.random()/2 - 0.25,
135			y = p.y + math.random()/2 - 0.25,
136			z = p.z + math.random()/2 - 0.25,
137		}
138		core.add_item(pos, item)
139	end
140end
141
142function check_attached_node(p, n)
143	local def = core.registered_nodes[n.name]
144	local d = {x=0, y=0, z=0}
145	if def.paramtype2 == "wallmounted" then
146		if n.param2 == 0 then
147			d.y = 1
148		elseif n.param2 == 1 then
149			d.y = -1
150		elseif n.param2 == 2 then
151			d.x = 1
152		elseif n.param2 == 3 then
153			d.x = -1
154		elseif n.param2 == 4 then
155			d.z = 1
156		elseif n.param2 == 5 then
157			d.z = -1
158		end
159	else
160		d.y = -1
161	end
162	local p2 = {x=p.x+d.x, y=p.y+d.y, z=p.z+d.z}
163	local nn = core.get_node(p2).name
164	local def2 = core.registered_nodes[nn]
165	if def2 and not def2.walkable then
166		return false
167	end
168	return true
169end
170
171--
172-- Some common functions
173--
174
175function nodeupdate_single(p, delay)
176	n = core.get_node(p)
177	if core.get_item_group(n.name, "falling_node") ~= 0 then
178		p_bottom = {x=p.x, y=p.y-1, z=p.z}
179		n_bottom = core.get_node(p_bottom)
180		-- Note: walkable is in the node definition, not in item groups
181		if core.registered_nodes[n_bottom.name] and
182				(core.get_item_group(n.name, "float") == 0 or
183					core.registered_nodes[n_bottom.name].liquidtype == "none") and
184				(n.name ~= n_bottom.name or (core.registered_nodes[n_bottom.name].leveled and
185					core.get_node_level(p_bottom) < core.get_node_max_level(p_bottom))) and
186				(not core.registered_nodes[n_bottom.name].walkable or
187					core.registered_nodes[n_bottom.name].buildable_to) then
188			if delay then
189				core.after(0.1, nodeupdate_single, {x=p.x, y=p.y, z=p.z}, false)
190			else
191				n.level = core.get_node_level(p)
192				core.remove_node(p, remove_fast)
193				spawn_falling_node(p, n)
194				nodeupdate(p)
195			end
196		end
197	end
198
199	if core.get_item_group(n.name, "attached_node") ~= 0 then
200		if not check_attached_node(p, n) then
201			drop_attached_node(p)
202			nodeupdate(p)
203		end
204	end
205end
206
207function nodeupdate(p, delay)
208	-- Round p to prevent falling entities to get stuck
209	p.x = math.floor(p.x+0.5)
210	p.y = math.floor(p.y+0.5)
211	p.z = math.floor(p.z+0.5)
212
213	for x = -1,1 do
214	for y = -1,1 do
215	for z = -1,1 do
216		nodeupdate_single({x=p.x+x, y=p.y+y, z=p.z+z}, delay or not (x==0 and y==0 and z==0))
217	end
218	end
219	end
220end
221
222--
223-- Global callbacks
224--
225
226function on_placenode(p, node)
227	nodeupdate(p)
228end
229core.register_on_placenode(on_placenode)
230
231function on_dignode(p, node)
232	nodeupdate(p)
233end
234core.register_on_dignode(on_dignode)
235