diff --git a/contraption.lua b/contraption.lua index 2cc5b32..773c9d5 100755 --- a/contraption.lua +++ b/contraption.lua @@ -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).