|  |  |  | @ -311,9 +311,12 @@ function contraption:update(delta_time) | 
			
		
	
		
			
				
					|  |  |  |  | end | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | function contraption:move(offset) | 
			
		
	
		
			
				
					|  |  |  |  |     local to_clear = {} -- Nodes to be cleared (to AIR). | 
			
		
	
		
			
				
					|  |  |  |  |     local to_push  = {} -- Nodes that want to be pushed. | 
			
		
	
		
			
				
					|  |  |  |  |     local to_set   = {} -- Nodes that will be modified. | 
			
		
	
		
			
				
					|  |  |  |  |     -- `to_clear` and `to_push` use a relative position hash | 
			
		
	
		
			
				
					|  |  |  |  |     -- as their key, since entries will need to be removed. | 
			
		
	
		
			
				
					|  |  |  |  |     local to_clear = {} -- Table of nodes to be cleared (to `AIR`). | 
			
		
	
		
			
				
					|  |  |  |  |     local to_push  = {} -- Table of nodes that want to be pushed. | 
			
		
	
		
			
				
					|  |  |  |  |     -- `to_set` is an append-only array with `{ pos, node, [meta] }` entries. | 
			
		
	
		
			
				
					|  |  |  |  |     local to_set   = {} -- Array of nodes that will be modified. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     -- Assume that every node this contraption occupies needs to be cleared. | 
			
		
	
		
			
				
					|  |  |  |  |     -- We'll remove entries from `to_clear` when we know a node is moved there. | 
			
		
	
	
		
			
				
					|  |  |  | @ -342,34 +345,35 @@ function contraption:move(offset) | 
			
		
	
		
			
				
					|  |  |  |  |         local self_overlap = self.nodes[new_rel_pos_hash] | 
			
		
	
		
			
				
					|  |  |  |  |         if self_overlap then | 
			
		
	
		
			
				
					|  |  |  |  |             if not nodecore.match(node, self_overlap) then | 
			
		
	
		
			
				
					|  |  |  |  |                 to_set[new_rel_pos_hash] = node | 
			
		
	
		
			
				
					|  |  |  |  |                 table.insert(to_set, { pos = new_abs_pos, node = node }) | 
			
		
	
		
			
				
					|  |  |  |  |             end | 
			
		
	
		
			
				
					|  |  |  |  |         else | 
			
		
	
		
			
				
					|  |  |  |  |             local new_node = minetest.get_node(new_abs_pos) | 
			
		
	
		
			
				
					|  |  |  |  |             -- TODO: Could also just check `to_push` first instead of checking the node. | 
			
		
	
		
			
				
					|  |  |  |  |             if contraption.is_pushable(new_node) then | 
			
		
	
		
			
				
					|  |  |  |  |                 new_node = { node = new_node, meta = minetest.get_meta(new_abs_pos):to_table() } | 
			
		
	
		
			
				
					|  |  |  |  |                 to_set[new_rel_pos_hash] = node | 
			
		
	
		
			
				
					|  |  |  |  |                 table.insert(to_set, { pos = new_abs_pos, node = node }) | 
			
		
	
		
			
				
					|  |  |  |  |                 while true do | 
			
		
	
		
			
				
					|  |  |  |  |                     new_abs_pos = new_abs_pos + offset | 
			
		
	
		
			
				
					|  |  |  |  |                     new_rel_pos = new_rel_pos + offset | 
			
		
	
		
			
				
					|  |  |  |  |                     new_rel_pos_hash = minetest.hash_node_position(new_rel_pos) | 
			
		
	
		
			
				
					|  |  |  |  |                     local pushable = to_push[new_rel_pos_hash] | 
			
		
	
		
			
				
					|  |  |  |  |                     if pushable then | 
			
		
	
		
			
				
					|  |  |  |  |                         -- The next node is a pushable node that was going to | 
			
		
	
		
			
				
					|  |  |  |  |                         -- get pushed by the contraption anyway, so continue. | 
			
		
	
		
			
				
					|  |  |  |  |                         to_set[new_rel_pos_hash] = new_node | 
			
		
	
		
			
				
					|  |  |  |  |                         table.insert(to_set, nodecore.underride({ pos = new_abs_pos }, new_node)) | 
			
		
	
		
			
				
					|  |  |  |  |                         to_push[new_rel_pos_hash] = nil | 
			
		
	
		
			
				
					|  |  |  |  |                         new_node = pushable | 
			
		
	
		
			
				
					|  |  |  |  |                     elseif self.nodes[new_rel_pos_hash] then | 
			
		
	
		
			
				
					|  |  |  |  |                         -- The next node is part of the contraption. | 
			
		
	
		
			
				
					|  |  |  |  |                         -- If it can move, this pushable node can move. | 
			
		
	
		
			
				
					|  |  |  |  |                         to_set[new_rel_pos_hash] = new_node | 
			
		
	
		
			
				
					|  |  |  |  |                         table.insert(to_set, nodecore.underride({ pos = new_abs_pos }, new_node)) | 
			
		
	
		
			
				
					|  |  |  |  |                         -- Do not clear this node by the contraption moving. | 
			
		
	
		
			
				
					|  |  |  |  |                         to_clear[new_rel_pos_hash] = nil | 
			
		
	
		
			
				
					|  |  |  |  |                         break | 
			
		
	
		
			
				
					|  |  |  |  |                     elseif nodecore.buildable_to(self.region.min + new_rel_pos) then | 
			
		
	
		
			
				
					|  |  |  |  |                     elseif nodecore.buildable_to(new_abs_pos) then | 
			
		
	
		
			
				
					|  |  |  |  |                         -- The next node is replaceable, so we can replace it. | 
			
		
	
		
			
				
					|  |  |  |  |                         to_set[new_rel_pos_hash] = new_node | 
			
		
	
		
			
				
					|  |  |  |  |                         table.insert(to_set, nodecore.underride({ pos = new_abs_pos }, new_node)) | 
			
		
	
		
			
				
					|  |  |  |  |                         break | 
			
		
	
		
			
				
					|  |  |  |  |                     else | 
			
		
	
		
			
				
					|  |  |  |  |                         -- There's a node in the way, abort! | 
			
		
	
	
		
			
				
					|  |  |  | @ -379,7 +383,7 @@ function contraption:move(offset) | 
			
		
	
		
			
				
					|  |  |  |  |                     end | 
			
		
	
		
			
				
					|  |  |  |  |                 end | 
			
		
	
		
			
				
					|  |  |  |  |             elseif nodecore.buildable_to(new_node) then | 
			
		
	
		
			
				
					|  |  |  |  |                 to_set[new_rel_pos_hash] = node | 
			
		
	
		
			
				
					|  |  |  |  |                 table.insert(to_set, { pos = new_abs_pos, node = node }) | 
			
		
	
		
			
				
					|  |  |  |  |             else | 
			
		
	
		
			
				
					|  |  |  |  |                 -- We bumped into a wall or something. | 
			
		
	
		
			
				
					|  |  |  |  |                 return false | 
			
		
	
	
		
			
				
					|  |  |  | @ -395,20 +399,21 @@ function contraption:move(offset) | 
			
		
	
		
			
				
					|  |  |  |  |         local forward_rel_pos_hash = minetest.hash_node_position(forward_rel_pos) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         -- If there's a node to push ahead of this one, skip. | 
			
		
	
		
			
				
					|  |  |  |  |         -- The node ahead will take care of pulling the ones behind it along with it. | 
			
		
	
		
			
				
					|  |  |  |  |         -- The node ahead will take care of pulling the ones behind along with it. | 
			
		
	
		
			
				
					|  |  |  |  |         if to_push[forward_rel_pos_hash] then goto continue end | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         local forward_abs_pos = self.region.min + forward_rel_pos | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         if self.nodes[forward_rel_pos] then | 
			
		
	
		
			
				
					|  |  |  |  |             -- Do not clear this node by the contraption moving. | 
			
		
	
		
			
				
					|  |  |  |  |             to_clear[forward_rel_pos] = nil | 
			
		
	
		
			
				
					|  |  |  |  |         else | 
			
		
	
		
			
				
					|  |  |  |  |             -- If the node(s) can't be pushed, then just don't, but continue | 
			
		
	
		
			
				
					|  |  |  |  |             -- moving the rest of the contraption and other pushable nodes. | 
			
		
	
		
			
				
					|  |  |  |  |             local forward_abs_pos = vector.add(self.region.min, forward_rel_pos) | 
			
		
	
		
			
				
					|  |  |  |  |             if not nodecore.buildable_to(forward_abs_pos) then goto continue end | 
			
		
	
		
			
				
					|  |  |  |  |         end | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         to_set[forward_rel_pos_hash] = node | 
			
		
	
		
			
				
					|  |  |  |  |         table.insert(to_set, nodecore.underride({ pos = forward_abs_pos }, node)) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         while true do | 
			
		
	
		
			
				
					|  |  |  |  |             local backward_rel_pos = rel_pos - offset | 
			
		
	
	
		
			
				
					|  |  |  | @ -416,7 +421,8 @@ function contraption:move(offset) | 
			
		
	
		
			
				
					|  |  |  |  |             node = to_push[backward_rel_pos_hash] | 
			
		
	
		
			
				
					|  |  |  |  |             if not node then break end | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |             to_set[pos_hash] = node | 
			
		
	
		
			
				
					|  |  |  |  |             local abs_pos = self.region.min + rel_pos | 
			
		
	
		
			
				
					|  |  |  |  |             table.insert(to_set, nodecore.underride({ pos = abs_pos }, node)) | 
			
		
	
		
			
				
					|  |  |  |  |             rel_pos = backward_rel_pos | 
			
		
	
		
			
				
					|  |  |  |  |             pos_hash = backward_rel_pos_hash | 
			
		
	
		
			
				
					|  |  |  |  |         end | 
			
		
	
	
		
			
				
					|  |  |  | @ -426,12 +432,9 @@ function contraption:move(offset) | 
			
		
	
		
			
				
					|  |  |  |  |     end | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     -- Set nodes that need to be changed. | 
			
		
	
		
			
				
					|  |  |  |  |     -- TODO: Can this use a vector key instead of hash, or an array? | 
			
		
	
		
			
				
					|  |  |  |  |     for pos_hash, node in pairs(to_set) do | 
			
		
	
		
			
				
					|  |  |  |  |         local rel_pos = vector.copy(minetest.get_position_from_hash(pos_hash)) | 
			
		
	
		
			
				
					|  |  |  |  |         local abs_pos = self.region.min + rel_pos | 
			
		
	
		
			
				
					|  |  |  |  |         minetest.set_node(abs_pos, node.node or node) | 
			
		
	
		
			
				
					|  |  |  |  |         if node.meta then minetest.get_meta(abs_pos):from_table(node.meta) end | 
			
		
	
		
			
				
					|  |  |  |  |     for _, e in ipairs(to_set) do | 
			
		
	
		
			
				
					|  |  |  |  |         minetest.set_node(e.pos, e.node) | 
			
		
	
		
			
				
					|  |  |  |  |         if e.meta then minetest.get_meta(e.pos):from_table(e.meta) end | 
			
		
	
		
			
				
					|  |  |  |  |     end | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     -- Clear nodes that need to be cleared (to AIR). | 
			
		
	
	
		
			
				
					|  |  |  | 
 |