diff options
author | FaceDeer <derksenmobile@gmail.com> | 2017-01-08 01:23:10 -0700 |
---|---|---|
committer | FaceDeer <derksenmobile@gmail.com> | 2017-01-08 01:23:10 -0700 |
commit | d7e5309833bc7044447982424dc8431d84d7a1a0 (patch) | |
tree | db1081d61fd650db0593ef190b0bb32f93a19b3a /util_layout.lua | |
parent | e704249735372a3232883cc8b7f25679f4949a8d (diff) |
Added a rotation controller
Well that was a lot of work. Also, switched the "waiting" timer
management to the actual on_timer system - I noticed that minetest.after
wasn't persisting through server shutdown and restart, that could put a
controller in a "broken" state.
Diffstat (limited to 'util_layout.lua')
-rw-r--r-- | util_layout.lua | 250 |
1 files changed, 249 insertions, 1 deletions
diff --git a/util_layout.lua b/util_layout.lua index bae75fd..5b68919 100644 --- a/util_layout.lua +++ b/util_layout.lua @@ -108,4 +108,252 @@ digtron.get_all_digtron_neighbours = function(pos, player) end return layout -end
\ No newline at end of file +end + +-- Rotation magic +-------------------------------------------------------------------------------------------------------- + +local facedir_rotate = { + ['x'] = { + [-1] = {[0]=4, 5, 6, 7, 22, 23, 20, 21, 0, 1, 2, 3, 13, 14, 15, 12, 19, 16, 17, 18, 10, 11, 8, 9}, -- 270 degrees + [1] = {[0]=8, 9, 10, 11, 0, 1, 2, 3, 22, 23, 20, 21, 15, 12, 13, 14, 17, 18, 19, 16, 6, 7, 4, 5}, -- 90 degrees + }, + ['y'] = { + [-1] = {[0]=3, 0, 1, 2, 19, 16, 17, 18, 15, 12, 13, 14, 7, 4, 5, 6, 11, 8, 9, 10, 21, 22, 23, 20}, -- 270 degrees + [1] = {[0]=1, 2, 3, 0, 13, 14, 15, 12, 17, 18, 19, 16, 9, 10, 11, 8, 5, 6, 7, 4, 23, 20, 21, 22}, -- 90 degrees + }, + ['z'] = { + [-1] = {[0]=16, 17, 18, 19, 5, 6, 7, 4, 11, 8, 9, 10, 0, 1, 2, 3, 20, 21, 22, 23, 12, 13, 14, 15}, -- 270 degrees + [1] = {[0]=12, 13, 14, 15, 7, 4, 5, 6, 9, 10, 11, 8, 20, 21, 22, 23, 0, 1, 2, 3, 16, 17, 18, 19}, -- 90 degrees + } +} + +local wallmounted_rotate = { + ['x'] = { + [-1] = {[0]=4, 5, 2, 3, 1, 0}, -- 270 degrees + [1] = {[0]=5, 4, 2, 3, 0, 1}, -- 90 degrees + }, + ['y'] = { + [-1] = {[0]=0, 1, 4, 5, 3, 2}, -- 270 degrees + [1] = {[0]=0, 1, 5, 4, 2, 3}, -- 90 degrees + }, + ['z'] = { + [-1] = {[0]=3, 2, 0, 1, 4, 5}, -- 270 degrees + [1] = {[0]=2, 3, 1, 0, 4, 5}, -- 90 degrees + } +} + + --90 degrees CW about x-axis: (x, y, z) -> (x, -z, y) + --90 degrees CCW about x-axis: (x, y, z) -> (x, z, -y) + --90 degrees CW about y-axis: (x, y, z) -> (-z, y, x) + --90 degrees CCW about y-axis: (x, y, z) -> (z, y, -x) + --90 degrees CW about z-axis: (x, y, z) -> (y, -x, z) + --90 degrees CCW about z-axis: (x, y, z) -> (-y, x, z) +local rotate_pos = function(axis, direction, pos) + if axis == "x" then + if direction < 0 then + return {x= pos.x, y= -pos.z, z= pos.y} + else + return {x= pos.x, y= pos.z, z= -pos.y} + end + elseif axis == "y" then + if direction < 0 then + return {x= -pos.z, y= pos.y, z= pos.x} + else + return {x= pos.z, y= pos.y, z= -pos.x} + end + else + if direction < 0 then + return {x= -pos.y, y= pos.x, z= pos.z} + else + return {x= pos.y, y= -pos.x, z= pos.z} + end + end +end + +local get_node_image = function(pos, node) + local node_image = {node=node, pos={x=pos.x, y=pos.y, z=pos.z}} + node_image.paramtype2 = minetest.registered_nodes[node.name].paramtype2 + local meta = minetest.get_meta(pos) + node_image.meta = meta:to_table() + + -- Record what kind of thing we've got in a builder node so its facing can be rotated properly + if minetest.get_item_group(node.name, "digtron") == 4 then + local build_item = node_image.meta.inventory.main[1] + if build_item ~= "" then + local build_item_def = minetest.registered_nodes[ItemStack(build_item):get_name()] + node_image.build_item_paramtype2 = build_item_def.paramtype2 + end + end + return node_image +end + +local rotate_node_image = function(node_image, origin, axis, direction, old_pos_pointset) + -- Facings + if node_image.paramtype2 == "wallmounted" then + node_image.node.param2 = wallmounted_rotate[axis][direction][node_image.node.param2] + elseif node_image.paramtype2 == "facedir" then + node_image.node.param2 = facedir_rotate[axis][direction][node_image.node.param2] + end + + if node_image.build_item_paramtype2 == "wallmounted" then + node_image.meta.fields.build_facing = wallmounted_rotate[axis][direction][node_image.meta.fields.build_facing] + elseif node_image.build_item_paramtype2 == "facedir" then + node_image.meta.fields.build_facing = facedir_rotate[axis][direction][node_image.meta.fields.build_facing] + end + + node_image.meta.fields.waiting = nil -- If we're rotating a controller that's in the "waiting" state, clear it. Otherwise it may stick like that. + + -- record the old location so we can destroy the old node if the rotation operation is possible + old_pos_pointset:set(node_image.pos.x, node_image.pos.y, node_image.pos.z, true) + + -- position in space relative to origin + local pos = vector.subtract(node_image.pos, origin) + pos = rotate_pos(axis, direction, pos) + -- Move back to original reference frame + node_image.pos = vector.add(pos, origin) + + return node_image +end + +digtron.rotate_layout_image = function(layout_image, facedir) + -- To convert this into the direction the "top" of the axel node is pointing in: + -- 0, 1, 2, 3 == (0,1,0) + -- 4, 5, 6, 7 == (0,0,1) + -- 8, 9, 10, 11 == (0,0,-1) + -- 12, 13, 14, 15 == (1,0,0) + -- 16, 17, 18, 19 == (-1,0,0) + -- 20, 21, 22, 23== (0,-1,0) + + local top = { + [0]={axis="y", dir=-1}, + {axis="z", dir=1}, + {axis="z", dir=-1}, + {axis="x", dir=1}, + {axis="x", dir=-1}, + {axis="y", dir=1}, + } + local params = top[math.floor(facedir/4)] + + layout_image.old_pos_pointset = Pointset:create() + + for k, node_image in pairs(layout_image.all) do + rotate_node_image(node_image, layout_image.controller, params.axis, params.dir, layout_image.old_pos_pointset) + end + return layout_image +end + +digtron.can_write_layout_image = function(layout_image, player) + for k, node_image in pairs(layout_image.all) do + if not layout_image.old_pos_pointset:get(node_image.pos.x, node_image.pos.y, node_image.pos.z) + and not minetest.registered_nodes[minetest.get_node(node_image.pos).name].buildable_to then + return false + elseif minetest.is_protected(node_image.pos, player:get_player_name()) and not minetest.check_player_privs(player, "protection_bypass") then + return false + end + end + return true +end + +digtron.write_layout_image = function(layout_image) + -- destroy the old digtron + local oldpos, _ = layout_image.old_pos_pointset:pop() + while oldpos ~= nil do + local old_def = minetest.registered_nodes[minetest.get_node(oldpos).name] + minetest.remove_node(oldpos) + if old_def.after_dig_node ~= nil then + old_def.after_dig_node(oldpos) + end + oldpos, _ = layout_image.old_pos_pointset:pop() + end + + -- create the new one + for k, node_image in pairs(layout_image.all) do + minetest.add_node(node_image.pos, node_image.node) + minetest.get_meta(node_image.pos):from_table(node_image.meta) + + local new_def = minetest.registered_nodes[node_image.node.name] + if new_def.after_place_node ~= nil then + new_def.after_place_node(node_image.pos) + end + end +end + +-- Similar to get_layout, but far more comprehensive. This produces a data structure plus a set of temporary inventories that will allow the digtron to be rotated and then recreated. +digtron.get_layout_image = function(pos, player) + + local image = {} + --initialize. We're assuming that the start position is a controller digtron, should be a safe assumption since only the controller node should call this + image.all = {} + image.extents = {} + image.controller = {x=pos.x, y=pos.y, z=pos.z} --Make a deep copy of the pos parameter just in case the calling code wants to play silly buggers with it + image.contains_protected_node = false -- used to indicate if at least one node in this digtron array is protected from the player. + + table.insert(image.all, get_node_image(pos, minetest.get_node(pos))) + + image.extents.max_x = pos.x + image.extents.min_x = pos.x + image.extents.max_y = pos.y + image.extents.min_y = pos.y + image.extents.max_z = pos.z + image.extents.min_z = pos.z + + -- temporary pointsets used while searching + local to_test = Pointset.create() + local tested = Pointset.create() + + tested:set(pos.x, pos.y, pos.z, true) + to_test:set(pos.x + 1, pos.y, pos.z, true) + to_test:set(pos.x - 1, pos.y, pos.z, true) + to_test:set(pos.x, pos.y + 1, pos.z, true) + to_test:set(pos.x, pos.y - 1, pos.z, true) + to_test:set(pos.x, pos.y, pos.z + 1, true) + to_test:set(pos.x, pos.y, pos.z - 1, true) + + if minetest.is_protected(pos, player:get_player_name()) and not minetest.check_player_privs(player, "protection_bypass") then + image.contains_protected_node = true + end + + -- Do a loop on to_test positions, adding new to_test positions as we find digtron nodes. This is a flood fill operation + -- that follows node faces (no diagonals) + local testpos, _ = to_test:pop() + while testpos ~= nil do + tested:set(testpos.x, testpos.y, testpos.z, true) -- track nodes we've looked at to prevent infinite loops + local node = minetest.get_node(testpos) + + if node.name == "ignore" then + --buildtron array is next to unloaded nodes, too dangerous to do anything. Abort. + return nil + end + + local group_number = minetest.get_item_group(node.name, "digtron") + if group_number > 0 then + --found one. Add it to the digtrons output + table.insert(image.all, get_node_image(testpos, node)) + + if minetest.is_protected(pos, player:get_player_name()) and not minetest.check_player_privs(player, "protection_bypass") then + image.contains_protected_node = true + end + + -- update extents + image.extents.max_x = math.max(image.extents.max_x, testpos.x) + image.extents.min_x = math.min(image.extents.min_x, testpos.x) + image.extents.max_y = math.max(image.extents.max_y, testpos.y) + image.extents.min_y = math.min(image.extents.min_y, testpos.y) + image.extents.max_z = math.max(image.extents.max_z, testpos.z) + image.extents.min_z = math.min(image.extents.min_z, testpos.z) + + --queue up potential new test points adjacent to this digtron node + to_test:set_if_not_in(tested, testpos.x + 1, testpos.y, testpos.z, true) + to_test:set_if_not_in(tested, testpos.x - 1, testpos.y, testpos.z, true) + to_test:set_if_not_in(tested, testpos.x, testpos.y + 1, testpos.z, true) + to_test:set_if_not_in(tested, testpos.x, testpos.y - 1, testpos.z, true) + to_test:set_if_not_in(tested, testpos.x, testpos.y, testpos.z + 1, true) + to_test:set_if_not_in(tested, testpos.x, testpos.y, testpos.z - 1, true) + end + + testpos, _ = to_test:pop() + end + + return image +end |