|
|
@ -102,10 +102,7 @@ end |
|
|
|
function contraption.find(pos) |
|
|
|
function contraption.find(pos) |
|
|
|
-- First check if the node at `pos` is a contraption block. |
|
|
|
-- First check if the node at `pos` is a contraption block. |
|
|
|
for _, c in pairs(active_contraptions) do |
|
|
|
for _, c in pairs(active_contraptions) do |
|
|
|
if c.region:contains(pos) then |
|
|
|
if c:get_node_data(pos) then return c end |
|
|
|
local rel_pos = c.region:to_relative(pos) |
|
|
|
|
|
|
|
if c.nodes[rel_pos:hash()] then return c end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
end |
|
|
|
return nil |
|
|
|
return nil |
|
|
|
end |
|
|
|
end |
|
|
@ -127,13 +124,13 @@ contraption.metatable = metatable |
|
|
|
function contraption.create(r) |
|
|
|
function contraption.create(r) |
|
|
|
r = region.copy(r) |
|
|
|
r = region.copy(r) |
|
|
|
|
|
|
|
|
|
|
|
local nodes = {} |
|
|
|
|
|
|
|
local num_nodes = 0 |
|
|
|
local num_nodes = 0 |
|
|
|
for pos in r:iter_node_positions() do |
|
|
|
local node_data = {} |
|
|
|
local node = minetest.get_node(pos) |
|
|
|
for abs_pos in r:iter_node_positions() do |
|
|
|
|
|
|
|
local node = minetest.get_node(abs_pos) |
|
|
|
if nodecore.match(node, PLANK) then |
|
|
|
if nodecore.match(node, PLANK) then |
|
|
|
local offset = r:to_relative(pos) |
|
|
|
local rel_pos = r:to_relative(abs_pos) |
|
|
|
nodes[offset:hash()] = CONTRAPTION_WOOD |
|
|
|
node_data[rel_pos:hash()] = { rel_pos = rel_pos, node = CONTRAPTION_WOOD } |
|
|
|
num_nodes = num_nodes + 1 |
|
|
|
num_nodes = num_nodes + 1 |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
@ -146,37 +143,33 @@ function contraption.create(r) |
|
|
|
|
|
|
|
|
|
|
|
-- TODO: Flood fill algorithm to make sure all nodes are connected. |
|
|
|
-- TODO: Flood fill algorithm to make sure all nodes are connected. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Change nodes from base nodes to their contraption version. |
|
|
|
|
|
|
|
for _, data in pairs(node_data) do |
|
|
|
|
|
|
|
minetest.set_node(data.pos, data.node) |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
id_counter = id_counter + 1 |
|
|
|
id_counter = id_counter + 1 |
|
|
|
local result = setmetatable({ |
|
|
|
local result = setmetatable({ |
|
|
|
id = id_counter, |
|
|
|
id = id_counter, |
|
|
|
region = r, |
|
|
|
region = r, |
|
|
|
nodes = nodes, |
|
|
|
|
|
|
|
num_nodes = num_nodes, |
|
|
|
num_nodes = num_nodes, |
|
|
|
|
|
|
|
node_data = node_data, |
|
|
|
}, metatable) |
|
|
|
}, metatable) |
|
|
|
|
|
|
|
|
|
|
|
-- Change nodes from base nodes to their contraption version. |
|
|
|
|
|
|
|
for pos_hash, node in pairs(nodes) do |
|
|
|
|
|
|
|
local rel_pos = vector.from_hash(pos_hash) |
|
|
|
|
|
|
|
local abs_pos = r:to_absolute(rel_pos) |
|
|
|
|
|
|
|
minetest.set_node(abs_pos, node) |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result:on_initialize() |
|
|
|
|
|
|
|
active_contraptions[result.id] = result |
|
|
|
active_contraptions[result.id] = result |
|
|
|
|
|
|
|
result:on_initialize() |
|
|
|
return result |
|
|
|
return result |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
function contraption:on_initialize() |
|
|
|
function contraption:on_initialize() |
|
|
|
local above_nodes = {} |
|
|
|
|
|
|
|
for orig_pos_hash, node in pairs(self.nodes) do |
|
|
|
|
|
|
|
local orig_pos = vector.from_hash(orig_pos_hash) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Calculate the nodes above the contraption that it |
|
|
|
-- Calculate the nodes above the contraption that it |
|
|
|
-- could potentially pull along with it as it moves. |
|
|
|
-- could potentially pull along with it as it moves. |
|
|
|
local above_pos = orig_pos:offset(0, 1, 0) |
|
|
|
local above_nodes = {} |
|
|
|
if not self.nodes[above_pos:hash()] then |
|
|
|
for pos_hash, data in pairs(self.node_data) do |
|
|
|
|
|
|
|
local above_pos = data.pos:offset(0, 1, 0) |
|
|
|
|
|
|
|
if not self:get_node_data(above_pos) then |
|
|
|
-- Non-contraption block above this one. Move items here along. |
|
|
|
-- Non-contraption block above this one. Move items here along. |
|
|
|
table_insert(above_nodes, above_pos) |
|
|
|
above_nodes[pos_hash] = { pos = above_pos } |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
self.above_nodes = above_nodes |
|
|
|
self.above_nodes = above_nodes |
|
|
@ -190,11 +183,21 @@ function contraption.load_from_string(id, str) |
|
|
|
return nil |
|
|
|
return nil |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- TODO: Remove this temporary backward-compatibility. |
|
|
|
|
|
|
|
obj.node_data = obj.node_data or obj.nodes |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Recover "unnecessary" data. |
|
|
|
|
|
|
|
obj.region = region.copy(obj.region) |
|
|
|
|
|
|
|
for pos_hash, node in pairs(obj.node_data) do |
|
|
|
|
|
|
|
local rel_pos = vector.from_hash(pos_hash) |
|
|
|
|
|
|
|
obj.node_data[pos_hash] = { rel_pos = rel_pos, node = node } |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
local result = setmetatable({ |
|
|
|
local result = setmetatable({ |
|
|
|
id = id, |
|
|
|
id = id, |
|
|
|
|
|
|
|
|
|
|
|
region = region.copy(obj.region), |
|
|
|
region = obj.region, |
|
|
|
nodes = obj.nodes, |
|
|
|
node_data = obj.node_data, |
|
|
|
num_nodes = obj.num_nodes, |
|
|
|
num_nodes = obj.num_nodes, |
|
|
|
|
|
|
|
|
|
|
|
motion = obj.motion and vector.copy(obj.motion ) or nil, |
|
|
|
motion = obj.motion and vector.copy(obj.motion ) or nil, |
|
|
@ -207,12 +210,16 @@ function contraption.load_from_string(id, str) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
function contraption:save_to_string() |
|
|
|
function contraption:save_to_string() |
|
|
|
|
|
|
|
local node_data = {} -- Clear out unnecessary data. |
|
|
|
|
|
|
|
for pos_hash, data in pairs(self.node_data) do |
|
|
|
|
|
|
|
node_data[pos_hash] = data.node end |
|
|
|
|
|
|
|
|
|
|
|
local to_save = { |
|
|
|
local to_save = { |
|
|
|
version = 1, |
|
|
|
version = 1, |
|
|
|
|
|
|
|
|
|
|
|
region = self.region, |
|
|
|
region = self.region, |
|
|
|
nodes = self.nodes, |
|
|
|
|
|
|
|
num_nodes = self.num_nodes, |
|
|
|
num_nodes = self.num_nodes, |
|
|
|
|
|
|
|
node_data = node_data, |
|
|
|
|
|
|
|
|
|
|
|
motion = self.motion, |
|
|
|
motion = self.motion, |
|
|
|
partial = self.partial, |
|
|
|
partial = self.partial, |
|
|
@ -223,25 +230,32 @@ end |
|
|
|
function contraption:destroy() |
|
|
|
function contraption:destroy() |
|
|
|
active_contraptions[self.id] = nil |
|
|
|
active_contraptions[self.id] = nil |
|
|
|
|
|
|
|
|
|
|
|
for pos_hash, node in pairs(self.nodes) do |
|
|
|
for _, data in pairs(self.node_data) do |
|
|
|
local rel_pos = vector.from_hash(pos_hash) |
|
|
|
local def = minetest.registered_nodes[data.node.name] |
|
|
|
local abs_pos = self.region:to_absolute(rel_pos) |
|
|
|
minetest.set_node(data.pos, def.drop_in_place) |
|
|
|
local def = minetest.registered_nodes[node.name] |
|
|
|
|
|
|
|
minetest.set_node(abs_pos, def.drop_in_place) |
|
|
|
|
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function contraption:get_node_data(pos, is_relative) |
|
|
|
|
|
|
|
if not is_relative then pos = self.region:to_relative(pos) end |
|
|
|
|
|
|
|
return self.node_data[pos:hash()] |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
function contraption:push(pusher, dir) |
|
|
|
function contraption:push(pusher, dir) |
|
|
|
|
|
|
|
local can_move = not not self:simulate_move(dir) |
|
|
|
|
|
|
|
local has_moved = not not self:move(dir) |
|
|
|
|
|
|
|
minetest.chat_send_all("can_move=" .. tostring(can_move) .. " :: has_moved=" .. tostring(has_moved)) |
|
|
|
|
|
|
|
|
|
|
|
-- See if the same player has already pushed this contraption recently. |
|
|
|
-- See if the same player has already pushed this contraption recently. |
|
|
|
-- If so, do not apply the full amount of motion from the push. |
|
|
|
-- If so, do not apply the full amount of motion from the push. |
|
|
|
local pusher_name = pusher.get_player_name and pusher:get_player_name() or "" |
|
|
|
-- local pusher_name = pusher.get_player_name and pusher:get_player_name() or "" |
|
|
|
self.recent_pusher = self.recent_pusher or {} |
|
|
|
-- self.recent_pusher = self.recent_pusher or {} |
|
|
|
local delay = self.recent_pusher[pusher_name] |
|
|
|
-- local delay = self.recent_pusher[pusher_name] |
|
|
|
if delay then dir = dir * (1 - math.min(1, delay)) end |
|
|
|
-- if delay then dir = dir * (1 - math.min(1, delay)) end |
|
|
|
self.recent_pusher[pusher_name] = 1.0 -- seconds |
|
|
|
-- self.recent_pusher[pusher_name] = 1.0 -- seconds |
|
|
|
|
|
|
|
|
|
|
|
self.motion = self.motion or vector.zero() |
|
|
|
-- self.motion = self.motion or vector.zero() |
|
|
|
self.motion = self.motion + dir |
|
|
|
-- self.motion = self.motion + dir |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
function contraption:update(delta_time) |
|
|
|
function contraption:update(delta_time) |
|
|
@ -325,6 +339,252 @@ function contraption:update(delta_time) |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function contraption:simulate_move(offset) |
|
|
|
|
|
|
|
local success = true |
|
|
|
|
|
|
|
local affected = { |
|
|
|
|
|
|
|
-- [pos_hash] = { |
|
|
|
|
|
|
|
-- type = "fixed" -- Part of the contraption, must move. |
|
|
|
|
|
|
|
-- or "dragged" -- Dragged on top, may or may not move. |
|
|
|
|
|
|
|
-- or "pushed" -- Pushed in front, must move. |
|
|
|
|
|
|
|
-- or "replace" -- Replaced by another node. |
|
|
|
|
|
|
|
-- or "blocks" -- Blocks movement of the contraption. |
|
|
|
|
|
|
|
-- rel_pos = vector |
|
|
|
|
|
|
|
-- abs_pos = vector |
|
|
|
|
|
|
|
-- node = node |
|
|
|
|
|
|
|
-- meta = true or nil |
|
|
|
|
|
|
|
-- target = <affected> |
|
|
|
|
|
|
|
-- success = bool or nil when not calculated yet |
|
|
|
|
|
|
|
-- } |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for pos_hash, data in pairs(self.node_data) do |
|
|
|
|
|
|
|
local abs_pos = self.region:to_absolute(data.rel_pos); |
|
|
|
|
|
|
|
affected[pos_hash] = { |
|
|
|
|
|
|
|
type = "fixed", |
|
|
|
|
|
|
|
rel_pos = data.rel_pos, |
|
|
|
|
|
|
|
abs_pos = abs_pos, |
|
|
|
|
|
|
|
node = data.node, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for pos_hash, data in pairs(self.above_nodes) do |
|
|
|
|
|
|
|
local abs_pos = self.region:to_absolute(data.rel_pos); |
|
|
|
|
|
|
|
local node = minetest.get_node(data.abs_pos) |
|
|
|
|
|
|
|
if contraption.is_pushable(node) then |
|
|
|
|
|
|
|
affected[pos_hash] = { |
|
|
|
|
|
|
|
type = "dragged", |
|
|
|
|
|
|
|
rel_pos = data.rel_pos, |
|
|
|
|
|
|
|
abs_pos = abs_pos, |
|
|
|
|
|
|
|
node = node, |
|
|
|
|
|
|
|
meta = true, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Collect additional relevant nodes. |
|
|
|
|
|
|
|
local newly_affected = {} |
|
|
|
|
|
|
|
local function check_target(data) |
|
|
|
|
|
|
|
local offset_rel_pos = data.rel_pos + offset |
|
|
|
|
|
|
|
local offset_abs_pos = data.abs_pos + offset |
|
|
|
|
|
|
|
local offset_pos_hash = offset_rel_pos:hash() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local target = affected[offset_pos_hash] |
|
|
|
|
|
|
|
if target then |
|
|
|
|
|
|
|
-- If the node ahead is "fixed" (part of the contraption), |
|
|
|
|
|
|
|
-- then it has to be able to move for the whole to move. |
|
|
|
|
|
|
|
-- We can consider this node to be able to move forward. |
|
|
|
|
|
|
|
data.target = target |
|
|
|
|
|
|
|
if target.type == "fixed" then |
|
|
|
|
|
|
|
data.success = true |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
target = { rel_pos = offset_rel_pos, abs_pos = offset_abs_pos, |
|
|
|
|
|
|
|
node = minetest.get_node(offset_abs_pos) } |
|
|
|
|
|
|
|
newly_affected[offset_pos_hash] = target |
|
|
|
|
|
|
|
data.target = target |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if data.type == "fixed" and contraption.is_pushable(target.node) then |
|
|
|
|
|
|
|
target.type = "pushed" |
|
|
|
|
|
|
|
target.meta = true |
|
|
|
|
|
|
|
check_target(target) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Target node can be replaced, so do so. |
|
|
|
|
|
|
|
elseif nodecore.buildable_to(target.node) then |
|
|
|
|
|
|
|
target.type = "replace" |
|
|
|
|
|
|
|
data.success = true |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Target node blocks movement. |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
target.type = "blocks" |
|
|
|
|
|
|
|
data.success = false |
|
|
|
|
|
|
|
success = false |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
for _, data in pairs(affected) do check_target(data) end |
|
|
|
|
|
|
|
for pos_hash, data in newly_affected do affected[pos_hash] = data |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for _, data in pairs(affected) do |
|
|
|
|
|
|
|
local offset_rel_pos = data.rel_pos + offset |
|
|
|
|
|
|
|
local offset_abs_pos = data.abs.pos + offset |
|
|
|
|
|
|
|
local offset_pos_hash = offset_rel_pos:hash() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data.target = affected[offset_pos_hash] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if data.target then |
|
|
|
|
|
|
|
if data.target.type == "fixed" then |
|
|
|
|
|
|
|
data.success = true |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
-- TODO: Need to check of the node in front can move. |
|
|
|
|
|
|
|
-- Probably gonna add a set of nodes to check. |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
data.target = { rel_pos = offset_rel_pos, abs_pos = offset_abs_pos, |
|
|
|
|
|
|
|
node = minetest.get_node(offset_abs_pos) } |
|
|
|
|
|
|
|
newly_affected[offset_pos_hash] = data.target |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if data.type == "fixed" then |
|
|
|
|
|
|
|
if contraption.is_pushable(data.target.node) then |
|
|
|
|
|
|
|
data.target.type = "pushed" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
offset_rel_pos = offset_rel_pos + offset |
|
|
|
|
|
|
|
offset_abs_pos = offset_abs_pos + offset |
|
|
|
|
|
|
|
offset_pos_hash = offset_rel_pos:hash() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data.target.target = affected[offset_pos_hash] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if data.target.target then |
|
|
|
|
|
|
|
if data.target.type == "fixed" then |
|
|
|
|
|
|
|
data.success = true |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
data.target.target = { rel_pos = offset_rel_pos, abs_pos = offset_abs_pos, |
|
|
|
|
|
|
|
node = minetest.get_node(offset_abs_pos) } |
|
|
|
|
|
|
|
newly_affected[offset_pos_hash] = data.target.target |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if nodecore.buildable_to(data.target.node) then |
|
|
|
|
|
|
|
data.target.type = "replace" |
|
|
|
|
|
|
|
data.success = true |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
data.target.type = "blocks" |
|
|
|
|
|
|
|
data.success = false |
|
|
|
|
|
|
|
success = false |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if data.success == nil then |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local function try_move(pos_hash, rel_pos, data, can_push) |
|
|
|
|
|
|
|
local offset_rel_pos = rel_pos + offset |
|
|
|
|
|
|
|
local offset_abs_pos = data.pos + offset |
|
|
|
|
|
|
|
local offset_pos_hash = offset_rel_pos:hash() |
|
|
|
|
|
|
|
local offset_data |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local result_entry = result[offset_pos_hash] |
|
|
|
|
|
|
|
if result_entry then |
|
|
|
|
|
|
|
-- `result` already has an entry at the offset position. |
|
|
|
|
|
|
|
-- This means that node has already been checked and can move. |
|
|
|
|
|
|
|
offset_data = result_entry.from |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
offset_data = { pos = offset_abs_pos } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local contraption_node = self.nodes[offset_pos_hash] |
|
|
|
|
|
|
|
if contraption_node then |
|
|
|
|
|
|
|
-- There's a contraption at the offset position. |
|
|
|
|
|
|
|
-- If the whole can move, this node will be able to as well. |
|
|
|
|
|
|
|
offset_data.node = contraption_node |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
local pushable_data = to_push[offset_pos_hash] |
|
|
|
|
|
|
|
if pushable_data then |
|
|
|
|
|
|
|
-- Node is pushable and was going to be pushed anyway. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- TODO: Get rid of this nonsense. |
|
|
|
|
|
|
|
-- Node has already been processed by another pushable node. |
|
|
|
|
|
|
|
if pushable_data.can_move ~= nil then return pushable_data.can_move end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
offset_data = pushable_data |
|
|
|
|
|
|
|
local success = try_move(offset_pos_hash, offset_rel_pos, offset_data, false) |
|
|
|
|
|
|
|
if to_push_iterating then pushable_data.can_move = success |
|
|
|
|
|
|
|
else to_push[offset_pos_hash] = nil end |
|
|
|
|
|
|
|
if not success then return false end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
offset_data.node = minetest.get_node(offset_abs_pos) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if can_push and contraption.is_pushable(offset_data.node) then |
|
|
|
|
|
|
|
-- There's a pushable node in the way, so try to push it. |
|
|
|
|
|
|
|
-- Only one up to one node can be pushed this way. |
|
|
|
|
|
|
|
offset_data.meta = true -- Grab meta data for the pushed node later. |
|
|
|
|
|
|
|
if not try_move(offset_pos_hash, offset_rel_pos, offset_data, false) then return false end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
elseif nodecore.buildable_to(offset_data.node) then |
|
|
|
|
|
|
|
-- Offset node can be replaced by this one. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
-- Bumped into something. |
|
|
|
|
|
|
|
return false |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Successfully moved, add to result. |
|
|
|
|
|
|
|
result[pos_hash] = { from = data, to = offset_data } |
|
|
|
|
|
|
|
return true |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Attempt to move nodes that are part of the contraption. |
|
|
|
|
|
|
|
-- If any of them can't be moved, the contraption can't move. |
|
|
|
|
|
|
|
for pos_hash, node in pairs(self.nodes) do |
|
|
|
|
|
|
|
local rel_pos = vector.from_hash(pos_hash) |
|
|
|
|
|
|
|
local abs_pos = self.region:to_absolute(rel_pos) |
|
|
|
|
|
|
|
local data = { pos = abs_pos, node = node } |
|
|
|
|
|
|
|
if not try_move(pos_hash, rel_pos, data, true) then return nil end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- `to_push` contains "lose" nodes that are not directly |
|
|
|
|
|
|
|
-- moved by the contraption, and may or may not be pushed. |
|
|
|
|
|
|
|
to_push_iterating = true |
|
|
|
|
|
|
|
for pos_hash, pushable_data in pairs(to_push) do |
|
|
|
|
|
|
|
if result[pos_hash] then |
|
|
|
|
|
|
|
local rel_pos = vector.from_hash(pos_hash) |
|
|
|
|
|
|
|
local data = { pos = pushable_data.abs_pos, node = pushable_data.node, meta = true } |
|
|
|
|
|
|
|
pushable_data.can_move = try_move(pos_hash, rel_pos, data, false) |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Fill in the meta for each entry where requested. |
|
|
|
|
|
|
|
-- It's not needed until we know the contraption can move. |
|
|
|
|
|
|
|
for _, data in pairs(result) do |
|
|
|
|
|
|
|
if data.meta == true then |
|
|
|
|
|
|
|
data.meta = minetest.get_meta(data.pos):to_table() |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return result |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function contraption.update_world(moved_nodes) |
|
|
|
|
|
|
|
local to_set = {} -- Nodes to set because they have changed. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for pos_hash, data in pairs(moved_nodes) do |
|
|
|
|
|
|
|
to_set[pos_hash] = { node = AIR } |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for _, data in pairs(moved_nodes) do |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
function contraption:move(offset) |
|
|
|
function contraption:move(offset) |
|
|
|
-- Hash table of absolute positions before moved to nodes that have been moved by the |
|
|
|
-- Hash table of absolute positions before moved to nodes that have been moved by the |
|
|
|
-- contraption, either because they're part of it, or have been pushed as a result. |
|
|
|
-- contraption, either because they're part of it, or have been pushed as a result. |
|
|
@ -340,23 +600,13 @@ function contraption:move(offset) |
|
|
|
|
|
|
|
|
|
|
|
-- Assume that every node this contraption occupies needs to be cleared. |
|
|
|
-- 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. |
|
|
|
-- We'll remove entries from `to_clear` when we know a node is moved there. |
|
|
|
for pos_hash in pairs(self.nodes) do to_clear[pos_hash] = AIR end |
|
|
|
for pos_hash in pairs(self.node_data) do to_clear[pos_hash] = AIR end |
|
|
|
|
|
|
|
|
|
|
|
for _, rel_pos in ipairs(self.above_nodes) do |
|
|
|
|
|
|
|
local abs_pos = self.region:to_absolute(rel_pos) |
|
|
|
|
|
|
|
local node = minetest.get_node(abs_pos) |
|
|
|
|
|
|
|
if contraption.is_pushable(node) then |
|
|
|
|
|
|
|
to_push[rel_pos:hash()] = { |
|
|
|
|
|
|
|
node = node, |
|
|
|
|
|
|
|
meta = minetest.get_meta(abs_pos):to_table() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- TODO: Redo this loop to work similar to the `to_push` loop below. |
|
|
|
-- TODO: Redo this loop to work similar to the `to_push` loop below. |
|
|
|
for old_rel_pos_hash, node in pairs(self.nodes) do |
|
|
|
for _, data in pairs(self.node_data) do |
|
|
|
local old_rel_pos = vector.from_hash(old_rel_pos_hash) |
|
|
|
local node = data.node |
|
|
|
local old_abs_pos = self.region:to_absolute(old_rel_pos) |
|
|
|
local old_abs_pos = data.pos |
|
|
|
|
|
|
|
local old_rel_pos = self.region:to_relative(old_abs_pos) |
|
|
|
|
|
|
|
|
|
|
|
local new_abs_pos = old_abs_pos + offset |
|
|
|
local new_abs_pos = old_abs_pos + offset |
|
|
|
local new_rel_pos = old_rel_pos + offset |
|
|
|
local new_rel_pos = old_rel_pos + offset |
|
|
@ -365,10 +615,10 @@ function contraption:move(offset) |
|
|
|
-- Node is being moved to this position, no need to clear here. |
|
|
|
-- Node is being moved to this position, no need to clear here. |
|
|
|
to_clear[new_rel_pos_hash] = nil |
|
|
|
to_clear[new_rel_pos_hash] = nil |
|
|
|
|
|
|
|
|
|
|
|
local self_overlap = self.nodes[new_rel_pos_hash] |
|
|
|
local self_overlap = self.node_data[new_rel_pos_hash] |
|
|
|
if self_overlap then |
|
|
|
if self_overlap then |
|
|
|
moved_nodes[old_abs_pos:hash()] = { node = node } |
|
|
|
moved_nodes[old_abs_pos:hash()] = { node = node } |
|
|
|
if not nodecore.match(node, self_overlap) then |
|
|
|
if not nodecore.match(node, self_overlap.node) then |
|
|
|
table_insert(to_set, { pos = new_abs_pos, node = node }) |
|
|
|
table_insert(to_set, { pos = new_abs_pos, node = node }) |
|
|
|
end |
|
|
|
end |
|
|
|
else |
|
|
|
else |
|
|
@ -394,7 +644,7 @@ function contraption:move(offset) |
|
|
|
table_insert(to_set, nodecore.underride({ pos = new_abs_pos }, new_node)) |
|
|
|
table_insert(to_set, nodecore.underride({ pos = new_abs_pos }, new_node)) |
|
|
|
to_push[new_rel_pos_hash] = nil |
|
|
|
to_push[new_rel_pos_hash] = nil |
|
|
|
new_node = pushable |
|
|
|
new_node = pushable |
|
|
|
elseif self.nodes[new_rel_pos_hash] then |
|
|
|
elseif self.node_data[new_rel_pos_hash] then |
|
|
|
-- The next node is part of the contraption. |
|
|
|
-- The next node is part of the contraption. |
|
|
|
-- If it can move, this pushable node can move. |
|
|
|
-- If it can move, this pushable node can move. |
|
|
|
moved_nodes[old_abs_pos:hash()] = new_node |
|
|
|
moved_nodes[old_abs_pos:hash()] = new_node |
|
|
@ -436,7 +686,7 @@ function contraption:move(offset) |
|
|
|
|
|
|
|
|
|
|
|
local forward_abs_pos = self.region:to_absolute(forward_rel_pos) |
|
|
|
local forward_abs_pos = self.region:to_absolute(forward_rel_pos) |
|
|
|
|
|
|
|
|
|
|
|
if self.nodes[forward_rel_pos] then |
|
|
|
if self.node_data[forward_rel_pos] then |
|
|
|
-- Do not clear this node by the contraption moving. |
|
|
|
-- Do not clear this node by the contraption moving. |
|
|
|
to_clear[forward_rel_pos] = nil |
|
|
|
to_clear[forward_rel_pos] = nil |
|
|
|
else |
|
|
|
else |
|
|
|