diff --git a/.gitignore b/.gitignore index cd2946a..b46bf61 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,9 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk +# Blender files +*.blend + # ========================= # Operating System Files # ========================= diff --git a/README.md b/README.md deleted file mode 100644 index 5530b68..0000000 --- a/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Modular Tunnel Boring Machine -## aka The Almighty Digtron - - -This mod contains a set of blocks that can be used to construct highly customizable and modular tunnel-boring machines, bridge-builders, road-pavers, wall-o-matics, and other such construction/destruction contraptions. - -A digging machine's components must be connected to the control block via a path leading through the faces of the blocks - diagonal connections across edges and corners don't count. - -The basic block types that can be assembled into a functioning digging machine are: - -* Digger heads, which excavate material in front of them when the machine is triggered -* Builder heads, which build a user-configured node in the direction they're facing -* Inventory modules, which hold material produced by the digger and provide material to the builders -* Fuel modules, which holds flammable materials to feed the beast -* Control block, used to trigger the machine and move it in a particular direction. - -Diggers mine out blocks and shunt them into the Digtron's inventory, or drop them on the ground if there isn't room in the inventory to store them. - -Builder heads can be used to lay down a solid surface as the Digtron moves, useful for situations where a tunnel-borer intersects a cavern. Builder heads can be set to construct their target block "intermittently", allowing for regularly-spaced structures to be constructed. Common uses include building support arches at regular intervals in a tunnel, adding a torch on the wall at regular intervals, laying rails with regularly-spaced powered rails interspersed, and adding stairs to vertical shafts. - -The auto-controller block is able to trigger automatically for a user-selected number of cycles. A player can ride their Digtron as it goes. - -Other specialized Digtron blocks include: - -* An "axle" block that allows an assembled Digtron to be rotated into new orientations without needing to be rebuilt block-by-block -* A crate that can store an assembled Digtron and allow the player to transport it to a new location -* A duplicator that can create a copy of an existing Digtron (if provided with enough spare parts) -* An item ejector to clear Digtron's inventory of excavated materials and inject it into pipeworks tubes if that mod is installed. -* A light that can be mounted on a Digtron to illuminate the workspace as it moves -* Structural components to make it look cool - -The Digtron mod only depends on the default mod, but includes optional support for several other mods: - -* [https://forum.minetest.net/viewtopic.php?t=15912](doc), an in-game documentation mod. Detailed documentation for all of the Digtron's individual blocks are included as well as pages of general concepts and design tips. -* [https://forum.minetest.net/viewtopic.php?id=2155](pipeworks), a set of pneumatic tubes that allows inventory items to be extracted from or inserted into Digtron inventories. -* [https://forum.minetest.net/viewtopic.php?&t=12379](hopper), a different mod for inserting into and extracting from Digtron inventories. Note that only [https://github.com/minetest-mods/hopper](the most recent version of hopper is Digtron-capable,) earlier versions lack a suitable API. -* [https://forum.minetest.net/viewtopic.php?&t=4870](awards), a mod that adds achievements to the game. Over thirty Digtron-specific achievements are included. -* [https://forum.minetest.net/viewtopic.php?f=11&t=2538](technic), which adds rechargeable batteries and power cables to the game that Digtron can use instead of fuel. \ No newline at end of file diff --git a/awards.lua b/awards.lua deleted file mode 100644 index 074fe3b..0000000 --- a/awards.lua +++ /dev/null @@ -1,436 +0,0 @@ -if not minetest.get_modpath("awards") then - digtron.award_item_dug = function (items, name, count) end - digtron.award_layout = function (layout, name) end - digtron.award_item_built = function(item_name, name) end - digtron.award_crate = function (layout, name) end - return -end - ---------------------------------------------------------------------------- - --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - -awards.register_trigger("digtron_dig", { - type = "counted_key", - progress = "@1/@2 excavated", - auto_description = {"Excavate 1 @2 using a Digtron.", "Excavate @1 @2 using a Digtron."}, - auto_description_total = {"Excavate @1 block using a Digtron.", "Excavate @1 blocks using a Digtron."}, - get_key = function(self, def) - return minetest.registered_aliases[def.trigger.node] or def.trigger.node - end, - key_is_item = true, -}) - -digtron.award_item_dug = function(items_dropped, player) - if #items_dropped == 0 or not player then - return - end - for _, item in pairs(items_dropped) do - awards.notify_digtron_dig(player, item) - end -end - -awards.register_trigger("digtron_build", { - type = "counted_key", - progress = "@1/@2 built", - auto_description = {"Build 1 @2 using a Digtron.", "Build @1 @2 using a Digtron."}, - auto_description_total = {"Build @1 block using a Digtron.", "Build @1 blocks using a Digtron."}, - get_key = function(self, def) - return minetest.registered_aliases[def.trigger.node] or def.trigger.node - end, - key_is_item = true, -}) - -digtron.award_item_built = function(item_name, player) - if not player then - return - end - awards.notify_digtron_build(player, item_name) -end - -digtron.award_layout = function(layout, player) - if layout == nil or not player then - return - end - - local name = player:get_player_name() - - if layout.water_touching then - awards.unlock(name, "digtron_water") - end - if layout.lava_touching then - awards.unlock(name, "digtron_lava") - end - if table.getn(layout.all) > 9 then - awards.unlock(name, "digtron_size10") - if table.getn(layout.all) > 99 then - awards.unlock(name, "digtron_size100") - end - end - if layout.diggers ~= nil and table.getn(layout.diggers) > 24 then - awards.unlock(name, "digtron_digger25") - end - if layout.builders ~= nil and table.getn(layout.builders) > 24 then - awards.unlock(name, "digtron_builder25") - end - - if layout.controller.y > 100 then - awards.unlock(name, "digtron_height100") - if layout.controller.y > 1000 then - awards.unlock(name, "digtron_height1000") - end - elseif layout.controller.y < -100 then - awards.unlock(name, "digtron_depth100") - if layout.controller.y < -1000 then - awards.unlock(name, "digtron_depth1000") - if layout.controller.y < -2000 then - awards.unlock(name, "digtron_depth2000") - if layout.controller.y < -4000 then - awards.unlock(name, "digtron_depth4000") - if layout.controller.y < -8000 then - awards.unlock(name, "digtron_depth8000") - if layout.controller.y < -16000 then - awards.unlock(name, "digtron_depth16000") - if layout.controller.y < -30000 then - awards.unlock(name, "digtron_depth30000") - end - end - end - end - end - end - end -end - -digtron.award_crate = function(layout, name) - if layout == nil or not name or name == "" then - return - end - - -- Note that we're testing >10 rather than >9 because this layout includes the crate node - if table.getn(layout.all) > 10 then - awards.unlock(name, "digtron_crate10") - if table.getn(layout.all) > 100 then - awards.unlock(name, "digtron_crate100") - end - end -end - -awards.register_award("digtron_water",{ - title = S("Deep Blue Digtron"), - description = S("Encounter water while operating a Digtron."), - background = "awards_bg_mining.png", - icon = "default_water.png^digtron_digger_yb_frame.png", -}) - -awards.register_award("digtron_lava",{ - title = S("Digtrons of Fire"), - description = S("Encounter lava while operating a Digtron."), - background = "awards_bg_mining.png", - icon = "default_lava.png^digtron_digger_yb_frame.png", -}) - -awards.register_award("digtron_size10",{ - title = S("Bigtron"), - description = S("Operate a Digtron with 10 or more component blocks."), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^digtron_crate.png", -}) - -awards.register_award("digtron_size100",{ - title = S("Really Bigtron"), - description = S("Operate a Digtron with 100 or more component blocks."), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^digtron_crate.png", -- TODO: Visually distinguish this from Bigtron -}) - -awards.register_award("digtron_builder25",{ - title = S("Buildtron"), - description = S("Operate a Digtron with 25 or more builder modules."), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^digtron_builder.png^digtron_crate.png", -}) - -awards.register_award("digtron_digger25",{ - title = S("Digging Leviathan"), - description = S("Operate a Digtron with 25 or more digger heads."), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^digtron_motor.png^digtron_crate.png", -}) - -awards.register_award("digtron_height1000",{ - title = S("Digtron In The Sky"), - description = S("Operate a Digtron above 1000m elevation"), - background = "awards_bg_mining.png", - icon = "default_river_water.png^default_snow_side.png^[transformR180^digtron_digger_yb_frame.png", -}) - -awards.register_award("digtron_height100",{ - title = S("Digtron High"), - description = S("Operate a Digtron above 100m elevation"), - background = "awards_bg_mining.png", - icon = "default_river_water.png^default_snow_side.png^digtron_digger_yb_frame.png", -}) - -awards.register_award("digtron_depth100",{ - title = S("Scratching the Surface"), - description = S("Operate a Digtron 100m underground"), - background = "awards_bg_mining.png", - icon = "default_cobble.png^digtron_digger_yb_frame.png^awards_level1.png", -}) - -awards.register_award("digtron_depth1000",{ - title = S("Digging Deeper"), - description = S("Operate a Digtron 1,000m underground"), - background = "awards_bg_mining.png", - icon = "default_cobble.png^[colorize:#0002^digtron_digger_yb_frame.png^awards_level2.png", -}) - -awards.register_award("digtron_depth2000",{ - title = S("More Than a Mile"), - description = S("Operate a Digtron 2,000m underground"), - background = "awards_bg_mining.png", - icon = "default_cobble.png^[colorize:#0004^digtron_digger_yb_frame.png^awards_level3.png", -}) - -awards.register_award("digtron_depth4000",{ - title = S("Digging Below Plausibility"), - description = S("The deepest mine in the world is only 3.9 km deep, you operated a Digtron below 4km"), - background = "awards_bg_mining.png", - icon = "default_cobble.png^[colorize:#0006^digtron_digger_yb_frame.png^awards_level4.png", -}) - -awards.register_award("digtron_depth8000",{ - title = S("Double Depth"), - description = S("Operate a Digtron 8,000m underground"), - background = "awards_bg_mining.png", - icon = "default_cobble.png^[colorize:#0008^digtron_digger_yb_frame.png^awards_level5.png", -}) - -awards.register_award("digtron_depth16000",{ - title = S("Halfway to the Core"), - description = S("Operate a Digtron 16,000m underground"), - background = "awards_bg_mining.png", - icon = "default_cobble.png^[colorize:#000A^digtron_digger_yb_frame.png^awards_level6.png", -}) - -awards.register_award("digtron_depth30000",{ - title = S("Nowhere To Go But Up"), - description = S("Operate a Digtron 30,000m underground"), - background = "awards_bg_mining.png", - icon = "default_cobble.png^[colorize:#000C^digtron_digger_yb_frame.png^awards_level7.png", -}) - -awards.register_award("digtron_100mese_dug",{ - title = S("Mese Master"), - description = S("Mine 100 Mese crystals with a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^default_mese_crystal.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - node = "default:mese_crystal", - target = 100, - } -}) - -awards.register_award("digtron_100diamond_dug",{ - title = S("Diamond Vs. Diamond"), - description = S("Mine 100 diamonds with a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^default_diamond.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - node = "default:diamond", - target = 100, - } -}) - -awards.register_award("digtron_1000dirt_dug",{ - title = S("Strip Mining"), - description = S("Excavate 1000 units of dirt with a Digtron"), - background = "awards_bg_mining.png", - icon = "default_dirt.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - node = "default:dirt", - target = 1000, - } -}) - -awards.register_award("digtron_1000_dug",{ - title = S("Digtron Miner"), - description = S("Excavate 1000 blocks using a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^default_tool_bronzepick.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - target = 1000, - } -}) - -awards.register_award("digtron_10000_dug",{ - title = S("Digtron Expert Miner"), - description = S("Excavate 10,000 blocks using a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^default_tool_steelpick.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - target = 10000, - } -}) - -awards.register_award("digtron_100000_dug",{ - title = S("Digtron Master Miner"), - description = S("Excavate 100,000 blocks using a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^default_tool_diamondpick.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - target = 100000, - } -}) - -awards.register_award("digtron_1000000_dug",{ - title = S("DIGTRON MEGAMINER"), - description = S("Excavate over a million blocks using a Digtron!"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^default_tool_mesepick.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - target = 1000000, - } -}) - -awards.register_award("digtron_1000wood_dug",{ - title = S("Clear Cutting"), - description = S("Chop down 1000 units of tree with a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^default_sapling.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - node = "group:tree", - target = 1000, - } -}) - -awards.register_award("digtron_10000wood_dug",{ - title = S("Digtron Deforestation"), - description = S("Chop down 10,000 units of tree with a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^default_sapling.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - node = "group:tree", - target = 10000, - } -}) - -awards.register_award("digtron_1000grass_dug",{ - title = S("Lawnmower"), - description = S("Harvest 1000 units of grass with a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^default_grass_5.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - node = "group:grass", - target = 1000, - } -}) - -awards.register_award("digtron_1000iron_dug",{ - title = S("Iron Digtron"), - description = S("Excavate 1000 units of iron ore with a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^default_steel_ingot.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - node = "default:iron_lump", - target = 1000, - } -}) - -awards.register_award("digtron_1000copper_dug",{ - title = S("Copper Digtron"), - description = S("Excavate 1000 units of copper ore with a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^default_copper_ingot.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - node = "default:copper_lump", - target = 1000, - } -}) - -awards.register_award("digtron_1000coal_dug",{ - title = S("Coal Digtron"), - description = S("Excavate 1,000 units if coal with a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^default_coal_lump.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - node = "default:coal_lump", - target = 1000, - } -}) - -awards.register_award("digtron_10000coal_dug",{ - title = S("Bagger 288"), - description = S("Excavate 10,000 units of coal with a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^default_coal_block.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - node = "default:coal_lump", - target = 10000, - } -}) - -awards.register_award("digtron_100gold_dug",{ - title = S("Digtron 49er"), - description = S("Excavate 100 units of gold with a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^default_gold_ingot.png^digtron_digger_yb_frame.png", - trigger = { - type = "digtron_dig", - node = "default:gold_lump", - target = 100, - } -}) - -awards.register_award("digtron_1000_built",{ - title = S("Constructive Digging"), - description = S("Build 1,000 blocks with a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^digtron_builder.png", - trigger = { - type = "digtron_build", - target = 1000, - } -}) - -awards.register_award("digtron_10000_built",{ - title = S("Highly Constructive Digging"), - description = S("Build 10,000 blocks with a Digtron"), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^digtron_axel_side.png^[transformR90^digtron_builder.png", - trigger = { - type = "digtron_build", - target = 10000, - } -}) - -awards.register_award("digtron_crate10",{ - title = S("Digtron Packrat"), - description = S("Stored 10 or more Digtron blocks in one crate."), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^digtron_crate.png", -- TODO: Visually distinguish this from Bigtron -}) - -awards.register_award("digtron_crate100",{ - title = S("Digtron Hoarder"), - description = S("Stored 100 or more Digtron blocks in one crate."), - background = "awards_bg_mining.png", - icon = "digtron_plate.png^digtron_crate.png", -- TODO: Visually distinguish this from Bigtron -}) diff --git a/class_layout.lua b/class_layout.lua deleted file mode 100644 index f0478b1..0000000 --- a/class_layout.lua +++ /dev/null @@ -1,532 +0,0 @@ -DigtronLayout = {} -DigtronLayout.__index = DigtronLayout - -local modpath_awards = minetest.get_modpath("awards") - -------------------------------------------------------------------------- --- Creation - -local get_node_image = function(pos, node) - local node_image = {node=node, pos={x=pos.x, y=pos.y, z=pos.z}} - local node_def = minetest.registered_nodes[node.name] - node_image.paramtype2 = node_def.paramtype2 - local meta = minetest.get_meta(pos) - node_image.meta = meta:to_table() - if node_image.meta ~= nil and node_def._digtron_formspec ~= nil then - node_image.meta.fields.formspec = node_def._digtron_formspec(pos, meta) -- causes formspec to be automatically upgraded whenever Digtron moves - end - - local group = minetest.get_item_group(node.name, "digtron") - -- group 1 has no special metadata - if group > 1 and group < 10 then - assert(node_image ~= nil and node_image.meta ~= nil, "[Digtron] Digtron failed to get a metadata table for a Digtron node in group " - .. tostring(group) .. ". This error should not be possible. Please see https://github.com/minetest/minetest/issues/8067") - -- These groups have inventories - if group == 2 or (group > 3 and group < 8) then - assert(node_image.meta.inventory ~= nil, "[Digtron] Digtron failed to get a metadata inventory table for a Digtron node in group " - .. tostring(group) .. ". This error should not be possible. Please see https://github.com/minetest/minetest/issues/8067") - end - end - - -- Record what kind of thing we've got in a builder node so its facing can be rotated properly - if group == 4 then - local build_item = "" - if node_image.meta.inventory.main then - build_item = node_image.meta.inventory.main[1] - end - if build_item ~= "" then - local build_item_def = minetest.registered_nodes[ItemStack(build_item):get_name()] - if build_item_def ~= nil then - node_image.build_item_paramtype2 = build_item_def.paramtype2 - end - end - end - return node_image -end - --- temporary pointsets used while searching -local to_test = Pointset.create() -local tested = Pointset.create() - -function DigtronLayout.create(pos, player) - local self = {} - setmetatable(self, DigtronLayout) - - --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 - self.traction = 0 - self.all = {} - self.water_touching = false - self.lava_touching = false - self.protected = Pointset.create() -- if any nodes we look at are protected, make note of that. That way we don't need to keep re-testing protection state later. - self.old_pos_pointset = Pointset.create() -- For tracking original location of nodes if we do transformations on the Digtron - self.nodes_dug = Pointset.create() -- For tracking adjacent nodes that will have been dug by digger heads in future - self.contains_protected_node = false -- used to indicate if at least one node in this digtron array is protected from the player. - self.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 - - table.insert(self.all, get_node_image(pos, minetest.get_node(pos))) -- We never visit the source node, so insert it into the all table a priori. Revisit this design decision if a controller node is created that contains fuel or inventory or whatever. - - self.extents_max_x = pos.x - self.extents_min_x = pos.x - self.extents_max_y = pos.y - self.extents_min_y = pos.y - self.extents_max_z = pos.z - self.extents_min_z = pos.z - - 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 - self.protected:set(pos.x, pos.y, pos.z, true) - self.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 - --digtron array is next to unloaded nodes, too dangerous to do anything. Abort. - self.ignore_touching = true - end - - if minetest.get_item_group(node.name, "water") ~= 0 then - self.water_touching = true - elseif minetest.get_item_group(node.name, "lava") ~= 0 then - self.lava_touching = true - if digtron.config.lava_impassible then - self.protected:set(testpos.x, testpos.y, testpos.z, true) - end - end - - local is_protected = false - if minetest.is_protected(testpos, player:get_player_name()) and not minetest.check_player_privs(player, "protection_bypass") then - self.protected:set(testpos.x, testpos.y, testpos.z, true) - is_protected = true - end - - local group_number = minetest.get_item_group(node.name, "digtron") - if group_number > 0 then - --found one. Add it to the digtrons output - local node_image = get_node_image(testpos, node) - - table.insert(self.all, node_image) - - -- add a reference to this node's position to special node lists - if group_number == 2 then - self.inventories = self.inventories or {} - table.insert(self.inventories, node_image) - elseif group_number == 3 then - self.diggers = self.diggers or {} - table.insert(self.diggers, node_image) - elseif group_number == 4 then - self.builders = self.builders or {} - table.insert(self.builders, node_image) - elseif group_number == 5 then - self.fuelstores = self.fuelstores or {} - table.insert(self.fuelstores, node_image) - elseif group_number == 6 then - self.inventories = self.inventories or {} - self.fuelstores = self.fuelstores or {} - table.insert(self.inventories, node_image) - table.insert(self.fuelstores, node_image) - elseif group_number == 7 then -- technic batteries - self.battery_holders = self.battery_holders or {} - table.insert(self.battery_holders, node_image) - elseif group_number == 8 then - self.power_connectors = self.power_connectors or {} - table.insert(self.power_connectors, node_image) -- technic power connectors - elseif group_number == 9 and node_image.meta.fields["autoeject"] == "true" then - self.auto_ejectors = self.auto_ejectors or {} - table.insert(self.auto_ejectors, node_image) - end - - if is_protected then - self.contains_protected_node = true - end - - -- update extents - self.extents_max_x = math.max(self.extents_max_x, testpos.x) - self.extents_min_x = math.min(self.extents_min_x, testpos.x) - self.extents_max_y = math.max(self.extents_max_y, testpos.y) - self.extents_min_y = math.min(self.extents_min_y, testpos.y) - self.extents_max_z = math.max(self.extents_max_z, testpos.z) - self.extents_min_z = math.min(self.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) - elseif not minetest.registered_nodes[node.name] or minetest.registered_nodes[node.name].buildable_to ~= true then - -- Tracks whether the digtron is hovering in mid-air. If any part of the digtron array touches something solid it gains traction. - -- Allowing unknown nodes to provide traction, since they're not buildable_to either - self.traction = self.traction + 1 - end - - testpos, _ = to_test:pop() - end - - digtron.award_layout(self, player) -- hook for achievements mod - - to_test:clear() - tested:clear() - - return self -end - ------------------------------------------------------------------------- --- Rotation - -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) --- operates directly on the pos vector -local rotate_pos = function(axis, direction, pos) - if axis == "x" then - if direction < 0 then - local temp_z = pos.z - pos.z = pos.y - pos.y = -temp_z - else - local temp_z = pos.z - pos.z = -pos.y - pos.y = temp_z - end - elseif axis == "y" then - if direction < 0 then - local temp_x = pos.x - pos.x = -pos.z - pos.z = temp_x - else - local temp_x = pos.x - pos.x = pos.z - pos.z = -temp_x - end - else - if direction < 0 then - local temp_x = pos.x - pos.x = -pos.y - pos.y = temp_x - else - local temp_x = pos.x - pos.x = pos.y - pos.y = -temp_x - end - end - return pos -end - --- operates directly on the pos vector -local subtract_in_place = function(pos, subtract) - pos.x = pos.x - subtract.x - pos.y = pos.y - subtract.y - pos.z = pos.z - subtract.z - return pos -end -local add_in_place = function(pos, add) - pos.x = pos.x + add.x - pos.y = pos.y + add.y - pos.z = pos.z + add.z - return pos -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][tonumber(node_image.meta.fields.build_facing)] - elseif node_image.build_item_paramtype2 == "facedir" then - node_image.meta.fields.build_facing = facedir_rotate[axis][direction][tonumber(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 = subtract_in_place(node_image.pos, origin) - pos = rotate_pos(axis, direction, pos) - -- Move back to original reference frame - node_image.pos = add_in_place(pos, origin) - - return node_image -end - - -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}, -} --- Rotates 90 degrees widdershins around the axis defined by facedir (which in this case is pointing out the front of the node, so it needs to be converted into an upward-pointing axis internally) -function DigtronLayout.rotate_layout_image(self, facedir) - - if self == nil or self.all == nil or self.controller == nil or self.old_pos_pointset == nil then - -- this should not be possible, but if it is then abort. - return false - end - - -- To convert this into the direction the "top" of the axle 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 params = top[math.floor(facedir/4)] - - for k, node_image in pairs(self.all) do - rotate_node_image(node_image, self.controller, params.axis, params.dir, self.old_pos_pointset) - end - return self -end - ------------------------------------------------------------------------------------------------ --- Translation - -function DigtronLayout.move_layout_image(self, dir) - self.extents_max_x = self.extents_max_x + dir.x - self.extents_min_x = self.extents_min_x + dir.x - self.extents_max_y = self.extents_max_y + dir.y - self.extents_min_y = self.extents_min_y + dir.y - self.extents_max_z = self.extents_max_z + dir.z - self.extents_min_z = self.extents_min_z + dir.z - - for k, node_image in pairs(self.all) do - self.old_pos_pointset:set(node_image.pos.x, node_image.pos.y, node_image.pos.z, true) - node_image.pos = add_in_place(node_image.pos, dir) - self.nodes_dug:set(node_image.pos.x, node_image.pos.y, node_image.pos.z, false) -- we've moved a digtron node into this space, mark it so that we don't dig it. - end -end - ------------------------------------------------------------------------------------------------ --- Writing to world - -function DigtronLayout.can_write_layout_image(self) - for k, node_image in pairs(self.all) do - --check if we're moving into a protected node - if self.protected:get(node_image.pos.x, node_image.pos.y, node_image.pos.z) then - return false - end - -- check if the target node is buildable_to or is marked as part of the digtron that's moving - if not ( - self.old_pos_pointset:get(node_image.pos.x, node_image.pos.y, node_image.pos.z) - or minetest.registered_nodes[minetest.get_node(node_image.pos).name].buildable_to - ) then - return false - end - end - return true -end - --- We need to call on_dignode and on_placenode for dug and placed nodes, --- but that triggers falling nodes (sand and whatnot) and destroys Digtrons --- if done during mid-write. So we need to defer the calls until after the --- Digtron has been fully written. - --- using local counters and shared tables like this allows us to avoid some needless allocating and garbage-collecting of tables -local dug_nodes_count = 0 -local dug_node_pos = {} -local dug_node = {} -local dug_node_meta = {} - -local placed_nodes_count = 0 -local placed_node_pos = {} -local placed_new_node = {} -local placed_old_node = {} - -local node_callbacks = function(player) - if dug_nodes_count > 0 then - for i = 1, dug_nodes_count do - local old_pos = dug_node_pos[i] - local old_node = dug_node[i] - local old_meta = dug_node_meta[i] - - for _, callback in ipairs(minetest.registered_on_dignodes) do - -- Copy pos and node because callback can modify them - local pos_copy = {x=old_pos.x, y=old_pos.y, z=old_pos.z} - local oldnode_copy = {name=old_node.name, param1=old_node.param1, param2=old_node.param2} - callback(pos_copy, oldnode_copy, digtron.fake_player) - end - - local old_def = minetest.registered_nodes[old_node.name] - if old_def ~= nil and old_def.after_dig_node ~= nil then - old_def.after_dig_node(old_pos, old_node, old_meta, player) - end - end - end - - if placed_nodes_count > 0 then - for i = 1, placed_nodes_count do - local new_pos = placed_node_pos[i] - local new_node = placed_new_node[i] - local old_node = placed_old_node[i] - - for _, callback in ipairs(minetest.registered_on_placenodes) do - -- Copy pos and node because callback can modify them - local pos_copy = {x=new_pos.x, y=new_pos.y, z=new_pos.z} - local oldnode_copy = {name=old_node.name, param1=old_node.param1, param2=old_node.param2} - local newnode_copy = {name=new_node.name, param1=new_node.param1, param2=new_node.param2} - callback(pos_copy, newnode_copy, digtron.fake_player, oldnode_copy) - end - - local new_def = minetest.registered_nodes[new_node.name] - if new_def ~= nil and new_def.after_place_node ~= nil then - new_def.after_place_node(new_pos, player) - end - end - end -end - -local set_node_with_retry = function(pos, node) - local start_time = minetest.get_us_time() - while not minetest.set_node(pos, node) do - if minetest.get_us_time() - start_time > 1000000 then -- 1 second in useconds - return false - end - end - return true -end - -local set_meta_with_retry = function(meta, meta_table) - local start_time = minetest.get_us_time() - while not meta:from_table(meta_table) do - if minetest.get_us_time() - start_time > 1000000 then -- 1 second in useconds - return false - end - end - return true -end - -local air_node = {name="air"} -function DigtronLayout.write_layout_image(self, player) - -- destroy the old digtron - local oldpos, _ = self.old_pos_pointset:pop() - while oldpos ~= nil do - local old_node = minetest.get_node(oldpos) - local old_meta = minetest.get_meta(oldpos) - - if not set_node_with_retry(oldpos, air_node) then - minetest.log("error", "DigtronLayout.write_layout_image failed to destroy old Digtron node, aborting write.") - return false - end - - dug_nodes_count = dug_nodes_count + 1 - dug_node_pos[dug_nodes_count] = oldpos - dug_node[dug_nodes_count] = old_node - dug_node_meta[dug_nodes_count] = old_meta - oldpos, _ = self.old_pos_pointset:pop() - end - - -- create the new one - for k, node_image in pairs(self.all) do - local new_pos = node_image.pos - local new_node = node_image.node - local old_node = minetest.get_node(new_pos) - - if not (set_node_with_retry(new_pos, new_node) and set_meta_with_retry(minetest.get_meta(new_pos), node_image.meta)) then - minetest.log("error", "DigtronLayout.write_layout_image failed to write a Digtron node, aborting write.") - return false - end - - placed_nodes_count = placed_nodes_count + 1 - placed_node_pos[placed_nodes_count] = new_pos - placed_new_node[placed_nodes_count] = new_node - placed_old_node[placed_nodes_count] = old_node - end - - -- fake_player will be passed to callbacks to prevent actual player from "taking the blame" for this action. - -- For example, the hunger mod shouldn't be making the player hungry when he moves Digtron. - digtron.fake_player:update(self.controller, player:get_player_name()) - -- note that the actual player is still passed to the per-node after_place_node and after_dig_node, should they exist. - node_callbacks(player) - dug_nodes_count = 0 - placed_nodes_count = 0 - return true -end - - ---------------------------------------------------------------------------------------------- --- Serialization. Currently only serializes the data that is needed by the crate, upgrade this function if more is needed - -function DigtronLayout.serialize(self) - -- serialize can't handle ItemStack objects, convert them to strings. - for _, node_image in pairs(self.all) do - for k, inv in pairs(node_image.meta.inventory) do - for index, item in pairs(inv) do - inv[index] = item:to_string() - end - end - end - - return minetest.serialize({controller=self.controller, all=self.all}) -end - -function DigtronLayout.deserialize(layout_string) - local self = {} - setmetatable(self, DigtronLayout) - - if not layout_string or layout_string == "" then - return nil - end - local deserialized_layout = minetest.deserialize(layout_string) - - self.all = deserialized_layout.all - self.controller = deserialized_layout.controller - self.old_pos_pointset = Pointset.create() -- needed by the write_layout method, leave empty - - return self -end diff --git a/class_pointset.lua b/class_pointset.lua deleted file mode 100644 index 40663d6..0000000 --- a/class_pointset.lua +++ /dev/null @@ -1,77 +0,0 @@ --- A simple special-purpose class, this is used for building up sets of three-dimensional points for fast reference - -Pointset = {} -Pointset.__index = Pointset - --- from builtin\game\misc.lua, modified to take values directly to avoid creating an intermediate vector -local hash_node_position_values = function(x, y, z) - return (z + 32768) * 65536 * 65536 - + (y + 32768) * 65536 - + x + 32768 -end - -function Pointset.create() - local set = {} - setmetatable(set,Pointset) - set.points = {} - return set -end - -function Pointset:clear() - local points = self.points - for k, v in pairs(points) do - points[k] = nil - end -end - -function Pointset:set(x, y, z, value) - -- sets a value in the 3D array "points". - self.points[hash_node_position_values(x,y,z)] = value -end - -function Pointset:set_if_not_in(excluded, x, y, z, value) - -- If a value is not already set for this point in the 3D array "excluded", set it in "points" - if excluded:get(x, y, z) ~= nil then - return - end - self:set(x, y, z, value) -end - -function Pointset:get(x, y, z) - -- return a value from the 3D array "points" - return self.points[hash_node_position_values(x,y,z)] -end - -function Pointset:set_pos(pos, value) - self:set(pos.x, pos.y, pos.z, value) -end - -function Pointset:set_pos_if_not_in(excluded, pos, value) - self:set_if_not_in(excluded, pos.x, pos.y, pos.z, value) -end - -function Pointset:get_pos(pos) - return self:get(pos.x, pos.y, pos.z) -end - -function Pointset:pop() - -- returns a point that's in the 3D array, and then removes it. - local hash, value = next(self.points) - if hash == nil then return nil end - local pos = minetest.get_position_from_hash(hash) - self.points[hash] = nil - return pos, value -end - -function Pointset:get_pos_list(value) - -- Returns a list of all points with the given value in standard Minetest vector format. If no value is provided, returns all points - local outlist = {} - for hash, pointsval in pairs(self.points) do - if value == nil or pointsval == value then - table.insert(outlist, minetest.get_position_from_hash(hash)) - end - end - return outlist -end - - \ No newline at end of file diff --git a/config.lua b/config.lua index 47026fb..26575c9 100644 --- a/config.lua +++ b/config.lua @@ -25,11 +25,7 @@ end setting("bool", "uses_resources", true, "Digtron uses resources when active") setting("bool", "lava_impassible", true, "Lava counts as a protected node") -setting("bool", "damage_creatures", true, "Diggers damage creatures") -- TODO: legacy setting, remove eventually setting("int", "damage_hp", 8, "Damage diggers do") -setting("int", "size_limit", 1000, "Digtron size limit in nodes per moving digtron") - -if digtron.config.damage_creatures == false then digtron.config.damage_hp = 0 end -- TODO: remove when damage_creatures is removed -- Enables the spray of particles out the back of a digger head and puffs of smoke from the controller local particle_effects = minetest.settings:get_bool("enable_particles") @@ -47,7 +43,7 @@ setting("float", "traction_factor", 3.0, "Traction factor") -- one book is 3 units -- how much fuel is required to dig a node if not in one of the following groups. -setting("float", "dig_cost_default", 3.0, "Default dig cost") +setting("float", "dig_cost_default", 0.5, "Default dig cost") -- eg, stone setting("float", "dig_cost_cracky", 1.0, "Cracky dig cost") -- eg, dirt, sand diff --git a/controller.lua b/controller.lua new file mode 100644 index 0000000..d86dc20 --- /dev/null +++ b/controller.lua @@ -0,0 +1,983 @@ +local S = digtron.S + +local position_and_anchor = "position[0.025,0.1]anchor[0,0]" + +local listname_to_title = +{ + ["main"] = S("Main Inventory"), + ["fuel"] = S("Fuel"), +} + +--------------------------------------------------------------------------------------------------------------------- + +-- Sequencer commands +-- Yes, this indexing scheme is complicated. It's necessary in order to make sequences +-- language-agnostic, and saves a bit of storage space in the process by shortening and +-- standardizing command tags. +-- Also makes it future-proof, we can add new commands without disrupting existing stored sequences +local sequencer_commands = +{ + seq = S("Sequence"), + dmb = S("Dig Move Build"), + dmd = S("Dig Move Down"), + mup = S("Move Up"), + mdn = S("Move Down"), + mlt = S("Move Left"), + mrt = S("Move Right"), + mfw = S("Move Forward"), + mbk = S("Move Back"), + rlt = S("Yaw Left"), + rrt = S("Yaw Right"), + rup = S("Pitch Up"), + rdn = S("Pitch Down"), + rcl = S("Roll Clockwise"), + rcc = S("Roll Widdershins"), +} + +-- This sets the order in which they'll be shown on the dropdown menu +local sequencer_dropdown_order = +{ + "seq", "dmb", "dmd", "mup", "mdn", "mlt", "mrt", "mfw", "mbk", "rlt", "rrt", "rup", "rdn", "rcl", "rcc" +} +-- Allows commands to be turned into an index into the dropdown order, needed for defaulting to the current command when building the formspec +local sequencer_dropdown_order_reverse = {} +for i, command in ipairs(sequencer_dropdown_order) do + sequencer_dropdown_order_reverse[command] = i +end + +-- Build the actual text string used in the formspec dropdown +local sequencer_dropdown_list = {} +for _, cmd in ipairs(sequencer_dropdown_order) do + table.insert(sequencer_dropdown_list, sequencer_commands[cmd]) +end +sequencer_dropdown_list = table.concat(sequencer_dropdown_list, ",") + +-- Reverse lookup from human-readable command to internal tag, used in on_recieve_fields processing +local sequencer_commands_reverse = {} +for cmd, command in pairs(sequencer_commands) do + sequencer_commands_reverse[command] = cmd +end + +-- Creates a default sequence +-- Root must always be a seq command +digtron.default_sequence = function() + return {cmd="seq", cnt=1, cur=1, seq={{cmd="dmb", cnt=1, cur=1}}} +end + +----------------------------------------------------------------------------------------- +--- Manipulating sequences + +-- searches down through the sequence tree to find the next command that can be executed +local find_next_item_to_execute = function(sequence) + local target = sequence + while target and target.cur > 0 do + if target.cmd ~= "seq" then + return target + else + local found = false + for i, command in ipairs(target.seq) do + if command.cur > 0 then + target = command + found = true + break + end + end + if not found then return nil end -- Sequence is finished + end + end +end + +-- recurses down through the sequence tree to modify the current counts of sequence items +-- with target_item having just been executed. +local decrement_sequence +decrement_sequence = function(sequence, target_item) + local found = false + for i, command in ipairs(sequence.seq) do + if found then + -- there's further items in the curent sequence's list after the target_item, + -- return without decrementing its parent + return "found" + elseif command == target_item then + target_item.cur = target_item.cur - 1 -- decrement the remaining count on the target item + found = true -- note that we've found the target item. + elseif command.cmd == "seq" then + local subsequence_result = decrement_sequence(command, target_item) -- recurse + if subsequence_result == "decrement_parent" then + -- the item was in the subsequence and the subsequence's list of commands are finished + -- so decrement the subsequence's current count and reset its constituents to their defaults + command.cur = command.cur - 1 + for _, subcommand in ipairs(command.seq) do + subcommand.cur = subcommand.cnt + end + found = true + target_item = command + end + end + end + if found and target_item.cur == 0 then + -- the item was found and was the last in the list and it's at 0. + -- Tell the previous call that it needs to decrement its sequence count. + return "decrement_parent" + end +end + +-- recurses through the whole sequence setting all current counts to the base value +local reset_sequence +reset_sequence = function(sequence) + for _, command in ipairs(sequence.seq) do + command.cur = command.cnt + if command.cmd == "seq" then + reset_sequence(command) + end + end +end + +---------------------------------------------------------------------------------------------- +-- Finding, adding, removing + +-- all field prefixes in the above create_sequence_list should be of this length +-- saves computation in trying to find the index part of the field name +local sequencer_field_length = string.len("sequencer_com:") +-- find an item based on the index string at the end of its field +local find_item = function(field, sequence) + local index_list = field:sub(sequencer_field_length+1):split(":") + local target = sequence + for i = 1, #index_list do + if target.seq then + target = target.seq[tonumber(index_list[i])] + else + return nil + end + if target == nil then + --minetest.log("error", "[Digtron] find_item failed to find a sequence item.") + return nil + end + end + return target +end + +local delete_item = function(field, sequence) + local index_list = field:sub(sequencer_field_length+1):split(":") + local target = sequence + for i = 1, #index_list-1 do + target = target.seq[tonumber(index_list[i])] + if target == nil then + minetest.log("error", "[Digtron] delete_item failed to find a sequence item.") + return nil + end + end + table.remove(target.seq, tonumber(index_list[#index_list])) +end + +-- recurses through sequences ensuring there are tables for seq commands +local clean_subsequences +clean_subsequences = function(sequence) + for i, val in ipairs(sequence.seq) do + if val.cmd == "seq" then + if val.seq == nil then + val.seq = {} + else + clean_subsequences(val) + end + else + val.seq = nil + end + end +end + +----------------------------------------------------------------------------- +-- Executing + +local cycling_digtrons = {} +local start_command = function(digtron_id, command, count, player_name) + cycling_digtrons[digtron_id] = { + command = command, + count = count, + last_action = minetest.get_gametime(), + player_name = player_name, + } +end +local cancel_command = function(digtron_id) + cycling_digtrons[digtron_id] = nil +end +local is_cycling = function(digtron_id) + return cycling_digtrons[digtron_id] ~= nil +end + +local execute_command +local refresh_open_formspec + +local command_functions = { + mup = function(digtron_id, pos, facedir, player_name) + return digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_up(facedir)), player_name) end, + mdn = function(digtron_id, pos, facedir, player_name) + return digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_up(facedir), -1)), player_name) end, + mlt = function(digtron_id, pos, facedir, player_name) + return digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_right(facedir), -1)), player_name) end, + mrt = function(digtron_id, pos, facedir, player_name) + return digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_right(facedir)), player_name) end, + mfw = function(digtron_id, pos, facedir, player_name) + return digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_dir(facedir)), player_name) end, + mbk = function(digtron_id, pos, facedir, player_name) + return digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_dir(facedir), -1)), player_name) end, + rlt = function(digtron_id, pos, facedir, player_name) + return digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_up(facedir), -1), player_name) end, + rrt = function(digtron_id, pos, facedir, player_name) + return digtron.rotate(digtron_id, digtron.facedir_to_up(facedir), player_name) end, + rup = function(digtron_id, pos, facedir, player_name) + return digtron.rotate(digtron_id, digtron.facedir_to_right(facedir), player_name) end, + rdn = function(digtron_id, pos, facedir, player_name) + return digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_right(facedir), -1), player_name) end, + rcl = function(digtron_id, pos, facedir, player_name) + return digtron.rotate(digtron_id, digtron.facedir_to_dir(facedir), player_name) end, + rcc = function(digtron_id, pos, facedir, player_name) + return digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_dir(facedir), -1), player_name) end, + dmb = function(digtron_id, pos, facedir, player_name) + return digtron.execute_dig_move_build_cycle(digtron_id, player_name) end, + dmd = function(digtron_id, pos, facedir, player_name) + return digtron.execute_dig_move_build_cycle(digtron_id, player_name, true) end, + seq = function(digtron_id, pos, facedir, player_name) + local sequence = digtron.get_sequence(digtron_id) + local target = find_next_item_to_execute(sequence) + if target == nil then + return false + end + local success = execute_command(digtron_id, target.cmd, player_name) + if success then + local decrement_result = decrement_sequence(sequence, target) + if decrement_result == "decrement_parent" then + sequence.cur = sequence.cur - 1 + reset_sequence(sequence) + end + return true + else + return false + end + end +} + +execute_command = function(digtron_id, command, player_name) + local pos = digtron.get_pos(digtron_id) + local node = minetest.get_node(pos) + if node.name ~= "digtron:controller" then + minetest.log("error", "[Digtron] execute_command was given the command " .. command .. " for " .. digtron_id + .. " by " .. player_name .. " but the node at " .. minetest.pos_to_string(pos) .. " was a " .. node.name) + return + end + local facedir = node.param2 + + local func = command_functions[command] + if func then + return func(digtron_id, pos, facedir, player_name) + else + minetest.log("error", "[Digtron] " .. digtron_id .. " was given command " .. command .. " but " + .. " that command was not recognized by execute_command.") + return false + end +end + +local done_cycling = {} +minetest.register_globalstep(function(dtime) + local gametime = minetest.get_gametime() + for digtron_id, data in pairs(cycling_digtrons) do + if data.last_action < gametime then + local success = execute_command(digtron_id, data.command, data.player_name) + refresh_open_formspec(digtron_id) + local new_count = data.count + if data.command ~= "seq" then + -- It's somewhat hacky having two different counters, + -- but I'm getting tired of laborious elegance at this point. + -- sequences handle their own counters internally and that's fine. + new_count = new_count - 1 + end + if new_count < 1 or not success then + table.insert(done_cycling, digtron_id) + else + data.count = new_count + data.last_action = gametime + end + end + end + + while next(done_cycling) ~= nil do + cycling_digtrons[table.remove(done_cycling)] = nil + end +end) + +------------------------------------------------------------------------------------------------------------------- +-- Sequence tab formspec + +-- Recursively builds a formspec representation. Dropdowns and buttons are indexed with : delimiters, eg: +--:1 +--:2 +--:2:1 +--:2:2 +--:2:2:1 +--:3 +local create_sequence_list +create_sequence_list = function(sequence_in, list_out, root_index, x, y) + if sequence_in == nil then + minetest.log("error", "[Digtron] create_sequence_list was given a nil sequence_in parameter") + return y + end + root_index = root_index or "" + x = x or 0 + y = y or 0 + for i, val in ipairs(sequence_in) do + local index = root_index .. ":" .. i + if val.cur == 0 then + table.insert(list_out, "box[" .. x+2.6 .. "," .. y .. ";0.7,0.5;#FF000088]") + end + + table.insert(list_out, "dropdown[".. x ..","..y..";1.75,0.5;sequencer_com"..index..";"..sequencer_dropdown_list..";" + .. sequencer_dropdown_order_reverse[val.cmd].."]field[" + .. x+1.8 ..",".. y ..";0.75,0.5;sequencer_cnt"..index..";;"..val.cnt.."]" + .. "field_close_on_enter[sequencer_cnt"..index..";false]" + .. "label[".. x+2.65 .."," .. y+0.25 .. ";" .. S("@1 left", val.cur) .. "]" + .. "button[".. x+3.3 .. ","..y ..";0.75,0.5;sequencer_del"..index..";"..S("Delete").."]") + if val.cmd == "seq" then + table.insert(list_out, "button[".. x+4.1 ..","..y ..";0.75,0.5;sequencer_ins"..index..";"..S("Insert").."]") + y = y + 0.6 + -- Recurse into sub-sequence + y = create_sequence_list(val.seq, list_out, index, x+0.25, y) + else + y = y + 0.6 + end + end + return y +end + +local sequence_tab = function(digtron_id) + local sequence = digtron.get_sequence(digtron_id) + local list_out = {"size[5.75,12]" + .. position_and_anchor + .. "real_coordinates[true]" + + .. "container[0.2,0.2]" + .. "field[0,0.1;0.7,0.5;cycles;"..S("Cycles")..";" .. sequence.cnt .."]" + .. "field_close_on_enter[cycles;false]" + } + if sequence.cur == 0 then + table.insert(list_out, "box[0.75,0.1;0.7,0.5;#FF000088]") + end + if is_cycling(digtron_id) then + table.insert(list_out, "button[1.5,0.1;1,0.5;stop;"..S("Stop").."]") + else + table.insert(list_out, "button[1.5,0.1;1,0.5;execute;"..S("Execute").."]") + end + + table.insert(list_out, + "label[0.8,0.35;" .. S("@1 left", sequence.cur) .."]" + .. "button[2.5,0.1;1,0.5;reset;"..S("Reset").."]" + .. "container_end[]" + .. "container[0.2,1]" + ) + local y = create_sequence_list(sequence.seq, list_out) + table.insert(list_out, + "button[0,".. y ..";1,0.5;sequencer_insert_end;"..S("New\nCommand").."]" + .. "container_end[]" + ) + return table.concat(list_out) +end + +-- Handles returned fields for the sequence tab +local update_sequence = function(digtron_id, fields, player_name) + local sequence = digtron.get_sequence(digtron_id) + local delete_field = nil + local insert_field = nil + for field, value in pairs(fields) do + -- Go through all fields submitted to find the ones that are for changing commands in the sequence list + local command_type = field:sub(1,sequencer_field_length) + if command_type == "sequencer_com:" then + local seq_item = find_item(field, sequence) + local new_cmd = sequencer_commands_reverse[value] + if seq_item.cmd ~= new_cmd then + seq_item.cmd = new_cmd + end + elseif command_type == "sequencer_cnt:" then + local val_int = tonumber(value) + if val_int then + val_int = math.floor(val_int) + local seq_item = find_item(field, sequence) + if seq_item.cnt ~= val_int then + seq_item.cnt = val_int + seq_item.cur = val_int + end + end + + --Save these to do last so as to not invalidate indices + --Adding and removing items makes find_item not work + elseif command_type == "sequencer_del:" then + delete_field = field + elseif command_type == "sequencer_ins:" then + insert_field = field + end + end + + if insert_field and delete_field then + minetest.log("error", "[Digtron] somehow an insertion and a deletion were both registered for a single update_sequence call. " + .. "ignoring the deletion.") + delete_field = nil + end + + if insert_field then + local item = find_item(insert_field, sequence) + -- adds a "dig move build" command as the default new item + item.seq = item.seq or {} -- just in case something went wrong with clean_subsequence + table.insert(item.seq, {cmd="dmb",cnt=1,cur=1}) + elseif delete_field then + delete_item(delete_field, sequence) + end + + if fields.sequencer_insert_end then + -- adds a "dig move build" command as the default new item + table.insert(sequence.seq, {cmd="dmb",cnt=1,cur=1}) + end + + if fields.cycles then + local new_cycles = tonumber(fields.cycles) + if new_cycles ~= nil then + new_cycles = math.floor(new_cycles) + if new_cycles ~= sequence.cnt then + sequence.cnt = new_cycles + sequence.cur = sequence.cnt + end + end + end + + if fields.execute and sequence.cur > 0 then + start_command(digtron_id, "seq", 1, player_name) + end + + if fields.stop and is_cycling(digtron_id) then + cancel_command(digtron_id) + end + + if fields.reset then + cancel_command(digtron_id) + reset_sequence(sequence) + sequence.cur = sequence.cnt + end + + clean_subsequences(sequence) -- if commands were changed to or away from "seq", ensure they have the right .seq member + digtron.set_sequence(digtron_id, sequence) +end + +------------------------------------------------------------------------------------------------------- +-- Controls tab + +local cycles_cache = {} -- TODO something's not saving right here + +local controls_tab = function(digtron_id) + return "size[4.2,5]" + .. position_and_anchor + .. "container[0,0]" + .. "button_exit[0,0;1,1;disassemble;"..S("Disassemble").."]" + .. "field[1.2,0.3;1.75,1;digtron_name;"..S("Digtron name")..";" + .. minetest.formspec_escape(digtron.get_name(digtron_id)).."]" + .. "field_close_on_enter[digtron_name;false]" + .. "field[2.9,0.3;0.7,1;cycles;"..S("Cycles")..";".. (cycles_cache[digtron_id] or 1) .."]" + .. "field_close_on_enter[cycles;false]" + .. "button[3.3,0;1,1;execute;"..S("Execute").."]" + .. "container_end[]" + + .. "container[0,1]" + .. "box[0,0;4,2;#DDDDDD]" + .. "label[1.8,0.825;"..S("Move").."]" + .. "button[1.1,0.1;1,1;move_up;"..S("Up").."]" + .. "button[1.1,1.1;1,1;move_down;"..S("Down").."]" + .. "button[2.1,0.1;1,1;move_forward;"..S("Forward").."]" + .. "button[2.1,1.1;1,1;move_back;"..S("Back").."]" + .. "button[0.1,0.6;1,1;move_left;"..S("Left").."]" + .. "button[3.1,0.6;1,1;move_right;"..S("Right").."]" + .. "container_end[]" + + .. "container[0.5,3.2]" + .. "box[0,0;3,2;#DDDDDD]" + .. "label[1.3,0.825;"..S("Rotate").."]" + .. "button[0.1,0.1;1,1;rot_counterclockwise;"..S("Roll\nWiddershins").."]" + .. "button[2.1,0.1;1,1;rot_clockwise;"..S("Roll\nClockwise").."]" + .. "button[1.1,0.1;1,1;rot_up;"..S("Pitch Up").."]" + .. "button[1.1,1.1;1,1;rot_down;"..S("Pitch Down").."]" + .. "button[0.1,1.1;1,1;rot_left;"..S("Yaw Left").."]" + .. "button[2.1,1.1;1,1;rot_right;"..S("Yaw Right").."]" + .. "container_end[]" +end + +local update_controls = function(digtron_id, pos, player_name, facedir, fields) + local refresh = false + + if fields.disassemble then + local pos = digtron.disassemble(digtron_id, player_name) + end + + local cycles = math.max(math.floor(tonumber(fields.cycles) or 1), 1) + cycles_cache[digtron_id] = cycles + + -- Translation + if fields.move_forward then + start_command(digtron_id, "mfw", cycles, player_name) + elseif fields.move_back then + start_command(digtron_id, "mbk", cycles, player_name) + elseif fields.move_up then + start_command(digtron_id, "mup", cycles, player_name) + elseif fields.move_down then + start_command(digtron_id, "mdn", cycles, player_name) + elseif fields.move_left then + start_command(digtron_id, "mlt", cycles, player_name) + elseif fields.move_right then + start_command(digtron_id, "mrt", cycles, player_name) + -- Rotation + elseif fields.rot_counterclockwise then + start_command(digtron_id, "rcc", cycles, player_name) + elseif fields.rot_clockwise then + start_command(digtron_id, "rcl", cycles, player_name) + elseif fields.rot_up then + start_command(digtron_id, "rup", cycles, player_name) + elseif fields.rot_down then + start_command(digtron_id, "rdn", cycles, player_name) + elseif fields.rot_left then + start_command(digtron_id, "rlt", cycles, player_name) + elseif fields.rot_right then + start_command(digtron_id, "rrt", cycles, player_name) + end + + if fields.execute then + start_command(digtron_id, "dmb", cycles, player_name) + refresh = true + end + + if fields.key_enter_field == "digtron_name" or fields.digtron_name then + local pos = digtron.get_pos(digtron_id) + if pos then + local meta = minetest.get_meta(pos) + meta:set_string("infotext", fields.digtron_name) + digtron.set_name(digtron_id, fields.digtron_name) + refresh = true + end + end + + return refresh +end + +-------------------------------------------------------------------------------------------------- +-- Inventory tab formspec + +local inv_tab = function(digtron_id, inv_tab_context, player_context) + local inv_list = inv_tab_context.listname + local pages = inv_tab_context.pages + local current_page = player_context.inv_page[inv_list] + local starting_index = (current_page - 1) * 8 * 4 + local paging_controls = "" + if pages > 1 then + paging_controls = "button[0,0;1,1;page_back;<<]" + .. "label[1.125,0.25;"..S("Page @1/@2", current_page, pages) .. "]" + .. "button[2,0;1,1;page_forward;>>]" + end + + return "size[8,9]" + .. position_and_anchor + .. "container[0,0]" + .. "list[detached:" .. digtron_id .. ";"..inv_list..";0,0;8,4;"..starting_index.."]" + .. "container_end[]" + .. "container[2.5,4]" .. paging_controls .. "container_end[]" + .. "container[0,5]list[current_player;main;0,0;8,1;]list[current_player;main;0,1.25;8,3;8]container_end[]" + .. "listring[current_player;main]" + .. "listring[detached:" .. digtron_id .. ";"..inv_list.."]" +end + +------------------------------------------------------------------------------------------------------ + +-- This allows us to know which digtron the player has a formspec open for without +-- sending the digtron_id over the network +local player_interacting_with_digtron_id = {} +local get_context = function(digtron_id, player_name) + local digtron_context = player_interacting_with_digtron_id[digtron_id] or {players={}} + local player_context = digtron_context.players[player_name] or {current_tab = 1} + digtron_context.players[player_name] = player_context + player_interacting_with_digtron_id[digtron_id] = digtron_context + return player_context, digtron_context +end +local get_context_for_player = function(player_name) + for digtron_id, digtron_context in pairs(player_interacting_with_digtron_id) do + local player_context = digtron_context.players[player_name] + if player_context and player_context.open then + return player_context, digtron_context, digtron_id + end + end +end +-- TODO: dispose of cached digtron context when a digtron is disposed + +local get_controller_assembled_formspec = function(digtron_id, player_name) + local player_context, digtron_context = get_context(digtron_id, player_name) + local inv = digtron.get_inventory(digtron_id) -- ensures the detatched inventory exists and is populated + + if digtron_context.tabs == nil then + digtron_context.tabs = {} + local lists = inv:get_lists() + for listname, contents in pairs(lists) do + table.insert(digtron_context.tabs, { + tab_type = "inventory", + listname = listname, + size = #contents, + pages = math.floor(#contents/(8*4)) + 1}) + end + end + if player_context.inv_page == nil then + player_context.inv_page = {} + local lists = inv:get_lists() + for listname, contents in pairs(lists) do + player_context.inv_page[listname] = player_context.inv_page[listname] or 1 + end + end + + local tabs = "tabheader[0,0;tab_header;"..S("Controls")..","..S("Sequence") + for _, tab in ipairs(digtron_context.tabs) do + tabs = tabs .. "," .. listname_to_title[tab.listname] or tab.listname + end + tabs = tabs .. ";" .. player_context.current_tab .. "]" + + if player_context.current_tab == 1 then + return controls_tab(digtron_id) .. tabs + elseif player_context.current_tab == 2 then + return sequence_tab(digtron_id) .. tabs + else + local inv_tab_context = digtron_context.tabs[player_context.current_tab - 2] + return inv_tab(digtron_id, inv_tab_context, player_context) .. tabs + end +end + +-- For now, only refresh the UI if it's open to tab 2 (the sequencer). Other tabs +-- don't have things that are changed "on the fly" by Digtron operation. +refresh_open_formspec = function(digtron_id) + local digtron_context = player_interacting_with_digtron_id[digtron_id] + if digtron_context == nil then + return + end + for player_name, player_context in pairs(digtron_context.players) do + if player_context.open and player_context.current_tab == 2 then + minetest.show_formspec(player_name, + "digtron:controller_assembled", + get_controller_assembled_formspec(digtron_id, player_name)) + end + end +end + +-- Controlling a fully armed and operational Digtron +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname ~= "digtron:controller_assembled" then + return + end + local player_name = player:get_player_name() + + -- Get and validate various values + local player_context, digtron_context, digtron_id = get_context_for_player(player_name) + if player_context == nil then + minetest.log("error", "[Digtron] player_interacting_with_digtron_id context not found for " .. player_name) + return + end + local pos = digtron.get_pos(digtron_id) + if pos == nil then + minetest.log("error", "[Digtron] controller was unable to look up a position for digtron id for " .. digtron_id) + return + end + local node = minetest.get_node(pos) + if node.name ~= "digtron:controller" then + minetest.log("error", "[Digtron] player " .. player_name .. " interacted with the controller for " + .. digtron_id .. " but the node at " .. minetest.pos_to_string(pos) .. " was a " ..node.name + .. " rather than a digtron:controller") + return + end + + local current_tab = player_context.current_tab + local refresh = false + if fields.tab_header then + local new_tab = tonumber(fields.tab_header) + if new_tab <= #(digtron_context.tabs) + 2 then + player_context.current_tab = new_tab + refresh = true + else + minetest.log("error", "[Digtron] digtron:controller_assembled formspec returned the out-of-range tab index " + .. new_tab) + end + end + + if current_tab == 1 then + -- Controls + refresh = update_controls(digtron_id, pos, player_name, node.param2, fields) + + elseif current_tab == 2 then + --Sequencer + update_sequence(digtron_id, fields, player_name) + refresh = true + else -- inventory tabs + local tab_context = digtron_context.tabs[current_tab - 2] + local current_page = player_context.inv_page[tab_context.listname] + if fields.page_forward then + if current_page < tab_context.pages then + player_context.inv_page[tab_context.listname] = current_page + 1 + refresh = true + end + end + if fields.page_back then + if current_page > 1 then + player_context.inv_page[tab_context.listname] = current_page - 1 + refresh = true + end + end + end + + if fields.quit then + player_context.open = false + end + + if refresh then + minetest.show_formspec(player_name, + "digtron:controller_assembled", + get_controller_assembled_formspec(digtron_id, player_name)) + end +end) + +-- Doesn't deep-copy +local combine_defs = function(base_def, override_content) + local out = {} + for key, value in pairs(base_def) do + out[key] = value + end + for key, value in pairs(override_content) do + out[key] = value + end + return out +end + +local base_def = { + _doc_items_longdesc = nil, + _doc_items_usagehelp = nil, + -- Note: this is not in the "digtron" group because we do not want it to be incorporated + -- into digtrons by mere adjacency; it must be the root node and only one root node is allowed. + groups = {cracky = 3, oddly_breakable_by_hand = 3}, + paramtype = "light", + paramtype2= "facedir", + is_ground_content = false, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-0.3125, -0.3125, -0.3125, 0.3125, 0.3125, 0.3125}, -- Core + {-0.1875, 0.3125, -0.1875, 0.1875, 0.5, 0.1875}, -- +y_connector + {-0.1875, -0.5, -0.1875, 0.1875, -0.3125, 0.1875}, -- -y_Connector + {0.3125, -0.1875, -0.1875, 0.5, 0.1875, 0.1875}, -- +x_connector + {-0.5, -0.1875, -0.1875, -0.3125, 0.1875, 0.1875}, -- -x_connector + {-0.1875, -0.1875, 0.3125, 0.1875, 0.1875, 0.5}, -- +z_connector + {-0.5, 0.125, -0.5, -0.125, 0.5, -0.3125}, -- back_connector_3 + {0.125, 0.125, -0.5, 0.5, 0.5, -0.3125}, -- back_connector_1 + {0.125, -0.5, -0.5, 0.5, -0.125, -0.3125}, -- back_connector_2 + {-0.5, -0.5, -0.5, -0.125, -0.125, -0.3125}, -- back_connector_4 + }, + }, + sounds = default.node_sound_metal_defaults(), + on_blast = digtron.on_blast, +} + +minetest.register_node("digtron:controller_unassembled", combine_defs(base_def, { + description = S("Digtron Control Module"), + _digtron_assembled_node = "digtron:controller", + tiles = { + "digtron_plate.png^[transformR90", + "digtron_plate.png^[transformR270", + "digtron_plate.png", + "digtron_plate.png^[transformR180", + "digtron_plate.png", + "digtron_plate.png^digtron_control.png", + }, + + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + local returnstack, success = digtron.on_rightclick(pos, node, clicker, itemstack, pointed_thing) + if returnstack then + return returnstack, success + end + + if clicker == nil then return end + + local player_name = clicker:get_player_name() + local digtron_id = digtron.assemble(pos, player_name) + if digtron_id then + local player_context = get_context(digtron_id, player_name) + player_context.open = true + minetest.show_formspec(player_name, + "digtron:controller_assembled", + get_controller_assembled_formspec(digtron_id, player_name)) + end + end +})) + +minetest.register_node("digtron:controller", combine_defs(base_def, { + description = S("Digtron Assembly"), -- Will normally be obscured by the player-set Digtron name + tiles = { + "digtron_plate.png^[transformR90", + "digtron_plate.png^[transformR270", + "digtron_plate.png", + "digtron_plate.png^[transformR180", + "digtron_plate.png", + "digtron_plate.png^digtron_control.png^digtron_intermittent.png", + }, + _digtron_disassembled_node = "digtron:controller_unassembled", + groups = {cracky = 3, oddly_breakable_by_hand = 3, not_in_creative_inventory = 1}, + + on_dig = function(pos, node, digger) + local player_name + if digger then + player_name = digger:get_player_name() + end + local meta = minetest.get_meta(pos) + local digtron_id = meta:get_string("digtron_id") + local digtron_layout_node_id = meta:get_int("digtron_layout_node_id") + + local stack = ItemStack({name=node.name, count=1, wear=0}) + local stack_meta = stack:get_meta() + stack_meta:set_string("digtron_id", digtron_id) + stack_meta:set_int("digtron_layout_node_id", digtron_layout_node_id) + stack_meta:set_string("description", digtron.get_name(digtron_id)) + local inv = digger:get_inventory() + local stack = inv:add_item("main", stack) + if stack:get_count() > 0 then + minetest.add_item(pos, stack) + end + -- TODO call on_dignodes callback + if digtron_id ~= "" then + local removed = digtron.remove_from_world(digtron_id, player_name) + if removed then + for _, removed_pos in ipairs(removed) do + minetest.check_for_falling(removed_pos) + end + else + minetest.remove_node(pos) + end + else + minetest.remove_node(pos) + end + end, + + preserve_metadata = function(pos, oldnode, oldmeta, drops) + for _, dropped in ipairs(drops) do + if dropped:get_name() == "digtron:controller" then + local stack_meta = dropped:get_meta() + stack_meta:set_string("digtron_id", oldmeta:get_string("digtron_id")) + stack_meta:set_int("digtron_layout_node_id", oldmeta:get_int("digtron_layout_node_id")) + stack_meta:set_string("description", oldmeta:get_string("infotext")) + return + end + end + end, + + on_place = function(itemstack, placer, pointed_thing) + -- Shall place item and return the leftover itemstack. + -- The placer may be any ObjectRef or nil. + local player_name + if placer then player_name = placer:get_player_name() end + + local stack_meta = itemstack:get_meta() + local digtron_id = stack_meta:get_string("digtron_id") + if digtron_id ~= "" then + local target_pos + local below_node = minetest.get_node(pointed_thing.under) + local below_def = minetest.registered_nodes[below_node.name] + if below_def.buildable_to then + target_pos = pointed_thing.under + else + target_pos = pointed_thing.above + end + -- TODO rotate layout based on player orientation + + -- move up so that the lowest y-coordinate on the Digtron is + -- at the y-coordinate of the place clicked on and test again. + local bbox = digtron.get_bounding_box(digtron_id) + if bbox then + target_pos.y = target_pos.y + math.abs(bbox.minp.y) + + if target_pos then + local success, succeeded, failed = digtron.is_buildable_to(digtron_id, nil, target_pos, player_name) + if success then + local built_positions = digtron.build_to_world(digtron_id, nil, target_pos, player_name) + for _, built_pos in ipairs(built_positions) do + minetest.check_for_falling(built_pos) + end + + local meta = minetest.get_meta(target_pos) + meta:set_string("infotext", digtron.get_name(digtron_id)) + + minetest.sound_play("digtron_machine_assemble", {gain = 0.5, pos=target_pos}) + -- Note: DO NOT RESPECT CREATIVE MODE here. + -- If we allow multiple copies of a Digtron running around with the same digtron_id, + -- human sacrifice, dogs and cats living together, mass hysteria + return ItemStack("") + else + -- if that fails, show ghost of Digtron and fail to place. + digtron.show_buildable_nodes(succeeded, failed) + minetest.sound_play("digtron_buzzer", {gain = 0.5, pos=target_pos}) + end + end + else + minetest.log("error", "[Digtron] digtron:controller on_place failed to find data for " .. digtron_id + .. ", placing an unassembled controller.") + itemstack:set_name("digtron:controller_unassembled") + return minetest.item_place(itemstack, placer, pointed_thing) + end + return itemstack + else + -- Should be impossible to have a controller without an ID, but if it happens place an unassembled node + itemstack:set_name("digtron:controller_unassembled") + return minetest.item_place(itemstack, placer, pointed_thing) + end + end, + + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + local returnstack, success = digtron.on_rightclick(pos, node, clicker, itemstack, pointed_thing) + if returnstack then + return returnstack, success + end + + if clicker == nil then return end + + local meta = minetest.get_meta(pos) + local digtron_id = meta:get_string("digtron_id") + + local player_name = clicker:get_player_name() + + if digtron_id == "" then + if not digtron.recover_digtron_id(pos) then + minetest.log("error", "[Digtron] The digtron:controller node at " .. minetest.pos_to_string(pos) + .. " had no digtron id associated with it when " .. player_name + .. "right-clicked on it. Converting it into a digtron:controller_unassembled.") + node.name = "digtron:controller_unassembled" + minetest.set_node(pos, node) + return + end + end + + local player_context = get_context(digtron_id, player_name) + player_context.open = true + minetest.show_formspec(player_name, + "digtron:controller_assembled", + get_controller_assembled_formspec(digtron_id, player_name)) + end, +})) + +minetest.register_lbm({ + label = "Validate and repair Digtron controller metadata", + name = "digtron:validate_controller_metadata", + nodenames = {"digtron:controller"}, + run_at_every_load = true, + action = function(pos, node) + local meta = minetest.get_meta(pos) + local digtron_id = meta:get_string("digtron_id") + if digtron_id == "" then + if not digtron.recover_digtron_id(pos) then + minetest.log("error", "[Digtron] The digtron:controller node at " .. minetest.pos_to_string(pos) + .. " had no digtron id associated with it. Converting it into a digtron:controller_unassembled.") + node.name = "digtron:controller_unassembled" + minetest.set_node(pos, node) + end + end + end, +}) \ No newline at end of file diff --git a/depends.txt b/depends.txt deleted file mode 100644 index c482b27..0000000 --- a/depends.txt +++ /dev/null @@ -1,8 +0,0 @@ -default -pipeworks? -doc? -hopper? -awards? -catacomb? -intllib? -technic? \ No newline at end of file diff --git a/description.txt b/description.txt deleted file mode 100644 index 1f0fdd0..0000000 --- a/description.txt +++ /dev/null @@ -1,5 +0,0 @@ -digtron - a modular tunnel boring/building machine for minetest, v0.7.6 - 2017-01-22 - -Forum : https://forum.minetest.net/viewtopic.php?t=16295 -Github: https://github.com/FaceDeer/digtron/ -Wiki: http://wiki.minetest.net/Mods/digtron \ No newline at end of file diff --git a/doc.lua b/doc.lua deleted file mode 100644 index 257968a..0000000 --- a/doc.lua +++ /dev/null @@ -1,311 +0,0 @@ -digtron.doc = {} - -if not minetest.get_modpath("doc") then - return -end - -local coal_fuel = minetest.get_craft_result({method="fuel", width=1, items={"default:coal_lump"}}).time -local dig_stone_count = coal_fuel/digtron.config.dig_cost_cracky -local dig_dirt_count = coal_fuel/digtron.config.dig_cost_crumbly -local dig_wood_count = coal_fuel/digtron.config.dig_cost_choppy -local build_count = coal_fuel/digtron.config.build_cost - -local battery_ratio = (10000/digtron.config.power_ratio) / coal_fuel - --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - -local pipeworks_enabled = minetest.get_modpath("pipeworks") ~= nil -local hoppers_enabled = minetest.get_modpath("hopper") and hopper ~= nil and hopper.add_container ~= nil - -digtron.doc.core_longdesc = S("A crafting component used in the manufacture of all Digtron block types.") -digtron.doc.core_usagehelp = S("Place the Digtron Core in the center of the crafting grid. All Digtron recipes consist of arranging various other materials around the central Digtron Core.") - --------------------------------------------------------------------- - -digtron.doc.builder_longdesc = S("A 'builder' module for a Digtron. By itself it does nothing, but as part of a Digtron it is used to construct user-defined blocks.") -digtron.doc.builder_usagehelp = S("A builder head is the most complex component of this system. It has period and offset properties, and also an inventory slot where you \"program\" it by placing an example of the block type that you want it to build." -.."\n\n".. -"When the \"Save & Show\" button is clicked the properties for period and offset will be saved, and markers will briefly be shown to indicate where the nearest spots corresponding to those values are. The builder will build its output at those locations provided it is moving along the matching axis." -.."\n\n".. -"There is also an \"Extrusion\" setting. This allows your builder to extrude a line of identical blocks from the builder output, in the direction the output side is facing, until it reaches an obstruction or until it reaches the extrusion limit. This can be useful for placing columns below a bridge, or for filling a large volume with a uniform block type without requiring a large number of builder heads." -.."\n\n".. -"The \"output\" side of a builder is the side with a black crosshair on it." -.."\n\n".. -"Builders also have a \"facing\" setting. If you haven't memorized the meaning of the 24 facing values yet, builder heads have a helpful \"Read & Save\" button to fill this value in for you. Simply build a temporary instance of the block in the output location in front of the builder, adjust it to the orientation you want using the screwdriver tool, and then when you click the \"Read & Save\" button the block's facing will be read and saved." -.."\n\n".. -"Note: if more than one builder tries to build into the same space simultaneously, it is not predictable which builder will take priority. One will succeed and the other will fail. You should arrange your builders to avoid this for consistent results.") - - --------------------------------------------------------------------- - -digtron.doc.inventory_longdesc = S("Stores building materials for use by builder heads and materials dug up by digger heads.") -digtron.doc.inventory_usagehelp = S("Inventory modules have the same capacity as a chest. They're used both for storing the products of the digger heads and as the source of materials used by the builder heads. A digging machine whose builder heads are laying down cobble can automatically self-replenish in this way, but note that an inventory module is still required as buffer space even if the digger heads produced everything needed by the builder heads in a given cycle." -.."\n\n".. -"Inventory modules are not required for a digging-only machine. If there's not enough storage space to hold the materials produced by the digging heads the excess material will be ejected out the back of the control block. They're handy for accumulating ores and other building materials, though." -.."\n\n".. -"Digging machines can have multiple inventory modules added to expand their capacity.") - -if hoppers_enabled then - digtron.doc.inventory_usagehelp = digtron.doc.inventory_usagehelp - .."\n\n".. - S("Digtron inventory modules are compatible with hoppers, adjacent hoppers will add to or take from their inventories. Hoppers are not part of the Digtron and will not move with it, however. They may be useful for creating a \"docking station\" for a Digtron.") -end - -if pipeworks_enabled then - digtron.doc.inventory_usagehelp = digtron.doc.inventory_usagehelp - .."\n\n".. - S("Inventory modules are compatible with Pipeworks blocks. When a Digtron moves one of the inventory modules adjacent to a pipe it will automatically hook up to it, and disconnect again when it moves on.") -end - -local standard_fuel_doc = S("When a control unit is triggered, it will tally up how much fuel is required for the next cycle and then burn items from the fuel hopper until a sufficient amount of heat has been generated to power the operation. Any leftover heat will be retained by the control unit for use in the next cycle; this is the \"heat remaining in controller furnace\". This means you don't have to worry too much about what kinds of fuel you put in the fuel store, none will be wasted (unless you dig away a control unit with some heat remaining in it, that heat does get wasted)." -.."\n\n".. -"By using one lump of coal as fuel a digtron can:\n".. -"\tBuild @1 blocks\n".. -"\tDig @2 stone blocks\n".. -"\tDig @3 wood blocks\n".. -"\tDig @4 dirt or sand blocks", math.floor(build_count), math.floor(dig_stone_count), math.floor(dig_wood_count), math.floor(dig_dirt_count)) - - -digtron.doc.fuelstore_longdesc = S("Stores fuel to run a Digtron") -digtron.doc.fuelstore_usagehelp = S("Digtrons have an appetite. Build operations and dig operations require a certain amount of fuel, and that fuel comes from fuel store modules. Note that movement does not require fuel, only digging and building.") -.."\n\n".. standard_fuel_doc - -if hoppers_enabled then - digtron.doc.fuelstore_usagehelp = digtron.doc.fuelstore_usagehelp - .."\n\n".. - S("Digtron fuel store modules are compatible with hoppers, adjacent hoppers will add to or take from their inventories. Hoppers are not part of the Digtron and will not move with it, however. They may be useful for creating a \"docking station\" for a Digtron.") -end - -if pipeworks_enabled then - digtron.doc.fuelstore_usagehelp = digtron.doc.fuelstore_usagehelp - .."\n\n".. - S("Fuel modules are compatible with Pipeworks blocks. When a Digtron moves one of the inventory modules adjacent to a pipe it will automatically hook up to it, and disconnect again when it moves on.") -end - - --- Battery holders -digtron.doc.battery_holder_longdesc = S("Holds RE batteries to run a Digtron") -digtron.doc.battery_holder_usagehelp = S("Digtrons have an appetite, and it can be satisfied by electricity as well. Build operations and dig operations require a certain amount of power, and that power comes from the batteries place in the holder. Note that movement does not consume charge, only digging and building." -.."\n\n".. -"When a control unit is triggered, it will tally up how much power is required for the next cycle and then discharge the batteries in the battery holder until a sufficient amount of heat has been generated to power the operation. Any leftover heat will be retained by the control unit for use in the next cycle; this is the \"heat remaining in controller furnace\". Thus no power is wasted (unless you dig away a control unit with some heat remaining in it, that heat does get wasted), and the discharged batteries can be taken away to be recharged." -.."\n\n".. -"One fully charged battery can:\n".. -"\tBuild @1 blocks\n".. -"\tDig @2 stone blocks\n".. -"\tDig @3 wood blocks\n".. -"\tDig @4 dirt or sand blocks", math.floor(build_count*battery_ratio), math.floor(dig_stone_count*battery_ratio), math.floor(dig_wood_count*battery_ratio), math.floor(dig_dirt_count*battery_ratio)) - -if pipeworks_enabled then - digtron.doc.battery_holder_usagehelp = digtron.doc.battery_holder_usagehelp - .."\n\n".. - S("Fuel modules are compatible with Pipeworks blocks. When a Digtron moves one of the inventory modules adjacent to a pipe it will automatically hook up to it, and disconnect again when it moves on.") -end - - -digtron.doc.combined_storage_longdesc = S("Stores fuel for a Digtron and also has an inventory for building materials") -digtron.doc.combined_storage_usagehelp = S("For smaller jobs the two dedicated modules may simply be too much of a good thing, wasting precious Digtron space to give unneeded capacity. The combined storage module is the best of both worlds, splitting its internal space between building material inventory and fuel storage. It has 3/4 building material capacity and 1/4 fuel storage capacity.") .. "\n\n" .. standard_fuel_doc - -if hoppers_enabled then - digtron.doc.combined_storage_usagehelp = digtron.doc.combined_storage_usagehelp - .."\n\n".. - S("Digtron inventory modules are compatible with hoppers, adjacent hoppers will add to or take from their inventories. A hopper on top of a combined inventory module will insert items into its general inventory, a side hopper will insert items into its fuel inventory, and a hopper on the bottom of a combined inventory module will take items from its general inventory. Hoppers are not part of the Digtron and will not move with it, however. They may be useful for creating a \"docking station\" for a Digtron.") -end - -if pipeworks_enabled then - digtron.doc.combined_storage_usagehelp = digtron.doc.combined_storage_usagehelp - .."\n\n".. - S("Combination modules are compatible with Pipeworks blocks. When a Digtron moves one of the inventory modules adjacent to a pipe it will automatically hook up to it, and disconnect again when it moves on. Items are extracted from the \"main\" inventory, and items coming into the combination module from any direction except the underside are inserted into \"main\". However, a pipe entering the combination module from the underside will attempt to insert items into the \"fuel\" inventory instead.") -end - ---------------------------------------------------------------------- - -local locked_suffix = "\n\n" .. S("This is the \"locked\" version of the Digtron crate. It can only be used by the player who placed it.") - -digtron.doc.empty_crate_longdesc = S("An empty crate that a Digtron can be stored in") -digtron.doc.empty_crate_usagehelp = S("Digtrons can be pushed around and rotated, and that may be enough for getting them perfectly positioned for the start of a run. But once your digger is a kilometer down under a shaft filled with stairs, how to get it back to the surface to run another pass?" -.."\n\n".. -"Place an empty Digtron crate next to a Digtron and right-click it to pack the Digtron (and all its inventory and settings) into the crate. You can then collect the crate, bring it somewhere else, build the crate, and then unpack the Digtron from it again. The Digtron will appear in the same relative location and orientation to the crate as when it was packed away inside it.") - -digtron.doc.loaded_crate_longdesc = S("A crate containing a Digtron array") -digtron.doc.loaded_crate_usagehelp = S("This crate contains a Digtron assembly that was stored in it earlier. Place it somewhere and right-click on it to access the label text that was applied to it. There's also a button that causes it to display the shape the deployed Digtron would take if you unpacked the crate, marking unbuildable blocks with a warning marker. And finally there's a button to actually deploy the Digtron, replacing this loaded crate with an empty that can be reused later.") - -digtron.doc.empty_locked_crate_longdesc = digtron.doc.empty_crate_longdesc -digtron.doc.empty_locked_crate_usagehelp = digtron.doc.empty_crate_usagehelp .. locked_suffix - -digtron.doc.loaded_locked_crate_longdesc = digtron.doc.loaded_crate_longdesc -digtron.doc.loaded_locked_crate_usagehelp = digtron.doc.loaded_crate_usagehelp .. locked_suffix - ----------------------------------------------------------------------- - -digtron.doc.controller_longdesc = S("A basic controller to make a Digtron array move and operate.") -digtron.doc.controller_usagehelp = S("Right-click on this module to make the digging machine go one step. The digging machine will go in the direction that the control module is oriented." -.."\n\n".. -"A control module can only trigger once per second. Gives you time to enjoy the scenery and smell the flowers (or their mulched remains, at any rate)." -.."\n\n".. -"If you're standing within the digging machine's volume, or in a block adjacent to it, you will be pulled along with the machine when it moves.") - -digtron.doc.auto_controller_longdesc = S("A more sophisticated controller that includes the ability to set the number of cycles it will run for, as well as diagonal movement.") -digtron.doc.auto_controller_usagehelp = S("An Auto-control module can be set to run for an arbitrary number of cycles. Once it's running, right-click on it again to interrupt its rampage. If anything interrupts it - the player's click, an undiggable obstruction, running out of fuel - it will remember the number of remaining cycles so that you can fix the problem and set it running again to complete the original plan." -.."\n\n".. -"The digging machine will go in the direction that the control module is oriented." -.."\n\n".. -"Auto-controllers can also be set to move diagonally by setting the \"Slope\" parameter to a non-zero value. The controller will then shunt the Digtron in the direction of the arrows painted on its sides every X steps it moves. The Digtron will trigger dig heads when it shunts to the side, but will not trigger builder modules or intermittent dig heads. The \"Offset\" setting determines at what point the lateral motion will take place." -.."\n\n".. -"The \"Stop block\" inventory slot in an auto-controller allows you to program an auto-controller to treat certain block types as impenetrable obstructions. This can allow you to fence a Digtron in with something so you don't have to carefully count exactly how many steps it should take, for example." -.."\n\n".. -"Note that the Digtron detects an undiggable block by the item that would be produced when digging it. Setting cobble as the stop block will make both cobble and regular stone undiggable, but setting a block of regular stone (produced from cobble in a furnace) as the stop block will *not* stop a Digtron from digging regular stone (since digging regular stone produces cobble, not stone).") - -digtron.doc.pusher_longdesc = S("A simplified controller that merely moves a Digtron around without triggering its builder or digger modules") -digtron.doc.pusher_usagehelp = S("Aka the \"can you rebuild it six inches to the left\" module. This is a much simplified control module that does not trigger the digger or builder heads when right-clicked, it only moves the digging machine. It's up to you to ensure there's space for it to move into." -.."\n\n".. -"Since movement alone does not require fuel, a pusher module has no internal furnace. Pushers also don't require traction, since their primary purpose is repositioning Digtrons let's say they have a built-in crane or something.") - -digtron.doc.axle_longdesc = S("A device that allows one to rotate their Digtron into new orientations") -digtron.doc.axle_usagehelp = S("This magical module can rotate a Digtron array in place around itself. Right-clicking on it will rotate the Digtron 90 degrees in the direction the orange arrows on its sides indicate (widdershins around the Y axis by default, use the screwdriver to change this) assuming there's space for the Digtron in its new orientation. Builders and diggers will not trigger on rotation.") - ---------------------------------------------------------------------- - -digtron.doc.digger_longdesc = S("A standard Digtron digger head") -digtron.doc.digger_usagehelp = S("Facing of a digger head is significant; it will excavate material from the block on the spinning grinder wheel face of the digger head. Generally speaking, you'll want these to face forward - though having them aimed to the sides can also be useful.") - -digtron.doc.dual_digger_longdesc = S("Two standard Digtron digger heads merged at 90 degrees to each other") -digtron.doc.dual_digger_usagehelp = S("This digger head is mainly of use when you want to build a Digtron capable of digging diagonal paths. A normal one-direction dig head would be unable to clear blocks in both of the directions it would be called upon to move, resulting in a stuck Digtron." -.."\n\n".. -"One can also make use of dual dig heads to simplify the size and layout of a Digtron, though this is generally not of practical use.") - -digtron.doc.dual_soft_digger_longdesc = S("Two standard soft-material Digtron digger heads merged at 90 degrees to each other") -digtron.doc.dual_soft_digger_usagehelp = S("This digger head is mainly of use when you want to build a Digtron capable of digging diagonal paths. A normal one-direction dig head would be unable to clear blocks in both of the directions it would be called upon to move, resulting in a stuck Digtron." -.."\n\n".. -"Like a normal single-direction soft digger head, this digger only excavates material belonging to groups softer than stone." -.."\n\n".. -"One can make use of dual dig heads to simplify the size and layout of a Digtron.") - -digtron.doc.intermittent_digger_longdesc = S("A standard Digtron digger head that only triggers periodically") -digtron.doc.intermittent_digger_usagehelp = S("This is a standard digger head capable of digging any material, but it will only trigger periodically as the Digtron moves. This can be useful for punching regularly-spaced holes in a tunnel wall, for example.") - -digtron.doc.intermittent_soft_digger_longdesc = S("A standard soft-material Digtron digger head that only triggers periodically") -digtron.doc.intermittent_soft_digger_usagehelp = S("This is a standard soft-material digger head capable of digging any material, but it will only trigger periodically as the Digtron moves. This can be useful for punching regularly-spaced holes in a tunnel wall, for example.") - -digtron.doc.soft_digger_longdesc = S("A Digtron digger head that only excavates soft materials") -digtron.doc.soft_digger_usagehelp = S("This specialized digger head is designed to excavate only softer material such as sand or gravel. In technical terms, this digger digs blocks belonging to the \"crumbly\", \"choppy\", \"snappy\", \"oddly_diggable_by_hand\" and \"fleshy\" groups." -.."\n\n".. -"The intended purpose of this digger is to be aimed at the ceiling or walls of a tunnel being dug, making spaces to allow shoring blocks to be inserted into unstable roofs but leaving the wall alone if it's composed of a more stable material." -.."\n\n".. -"It can also serve as part of a lawnmower or tree-harvester.") - ---------------------------------------------------------------------- - -digtron.doc.power_connector_longdesc = S("High-voltage power connector allowing a Digtron to be powered from a Technic power network.") -digtron.doc.power_connector_usagehelp = S("A power connector node automatically hooks into adjacent high-voltage (HV) power cables, but it must be configured to set how much power it will draw from the attached network. Right-click on the power connector to bring up a form that shows the current estimated maximum power usage of the Digtron the power connector is part of and a field where a power value can be entered. The estimated maximum power usage is the amount of power this Digtron will use in the worst case situation, with all of its digger heads digging the toughest material and all of its builder heads building a block simultaneously." -.."\n\n".. -"You can set the power connector's usage lower than this, and if the Digtron is unable to get sufficient power from the network it will use on-board batteries or burnable fuel to make up the shortfall.") - ---------------------------------------------------------------------- - -digtron.doc.inventory_ejector_longdesc = S("An outlet that can be used to eject accumulated detritus from a Digtron's inventory.") -digtron.doc.inventory_ejector_usagehelp = S("When this block is punched it will search the entire inventory of the Digtron and will eject a stack of items taken from it, provided the items are not set for use by any of the Digtron's builders. It will not eject if the destination block is occupied.") - -if pipeworks_enabled then - digtron.doc.inventory_ejector_usagehelp = digtron.doc.inventory_ejector_usagehelp - .."\n\n".. - S("Item ejectors are compatible with pipeworks and will automatically connect to a pipeworks tube if one is adjacent in the output location.") -end - ---------------------------------------------------------------------- - -digtron.doc.duplicator_longdesc = S("A device for duplicating an adjacent Digtron using parts from its inventory.") -digtron.doc.duplicator_usagehelp = S("Place the duplicator block adjacent to a Digtron, and then fill the duplicator's inventory with enough parts to recreate the adjacent Digtron. Then place an empty Digtron crate at the duplicator's output (the side with the black \"+\") and click the \"Duplicate\" button in the duplicator's right-click GUI. If enough parts are available the Digtron will be duplicated and packed into the crate, along with all of its programming but with empty inventories.") - ---------------------------------------------------------------------- - -digtron.doc.structure_longdesc = S("Structural component for a Digtron array") -digtron.doc.structure_usagehelp = S("These blocks allow otherwise-disconnected sections of digtron blocks to be linked together. They are not usually necessary for simple diggers but more elaborate builder arrays might have builder blocks that can't be placed directly adjacent to other digtron blocks and these blocks can serve to keep them connected to the controller." -.."\n\n".. -"They may also be used for providing additional traction if your digtron array is very tall compared to the terrain surface that it's touching." -.."\n\n".. -"You can also use them decoratively, or to build a platform to stand on as you ride your mighty mechanical leviathan through the landscape.") - -digtron.doc.light_longdesc = S("Digtron light source") -digtron.doc.light_usagehelp = S("A light source that moves along with the digging machine. Convenient if you're digging a tunnel that you don't intend to outfit with torches or other permanent light fixtures. Not quite as bright as a torch since the protective lens tends to get grimy while burrowing through the earth.") - -digtron.doc.panel_longdesc = S("Digtron panel") -digtron.doc.panel_usagehelp = S("A structural panel that can be made part of a Digtron to provide shelter for an operator, keep sand out of the Digtron's innards, or just to look cool.") - -digtron.doc.edge_panel_longdesc = S("Digtron edge panel") -digtron.doc.edge_panel_usagehelp = S("A pair of structural panels that can be made part of a Digtron to provide shelter for an operator, keep sand out of the Digtron's innards, or just to look cool.") - -digtron.doc.corner_panel_longdesc = S("Digtron corner panel") -digtron.doc.corner_panel_usagehelp = S("A trio of structural panels that can be made part of a Digtron to provide shelter for an operator, keep sand out of the Digtron's innards, or just to look cool.") - -doc.add_category("digtron", -{ - name = S("Digtron"), - description = S("The Digtron system is a set of blocks used to construct tunnel-boring and construction machines."), - sorting = "custom", - sorting_data = {"summary", "concepts", "noises", "tips"}, - build_formspec = doc.entry_builders.text_and_gallery, -}) - -doc.add_entry("digtron", "summary", { - name = S("Summary"), - data = { text = S("Digtron blocks can be used to construct highly customizable and modular tunnel-boring machines, bridge-builders, road-pavers, wall-o-matics, and other such construction/destruction contraptions." -.."\n\n".. -"The basic blocks that can be assembled into a functioning digging machine are:" -.."\n\n".. -"* Diggers, which excavate material in front of them when the machine is triggered\n".. -"* Builders, which build a user-configured block in front of them\n".. -"* Inventory modules, which hold material produced by the digger and provide material to the builders\n".. -"* Control block, used to trigger the machine and move it in a particular direction." -.."\n\n".. -"A digging machine's components must be connected to the control block via a path leading through the faces of the blocks - diagonal connections across edges and corners don't count.") -}}) - -doc.add_entry("digtron", "concepts", { - name = S("Concepts"), - data = { text = -S("Several general concepts are important when building more sophisticated diggers." -.."\n\n".. -"Facing - a number between 0-23 that determines which direction a block is facing and what orientation it has. Not all blocks make use of facing (basic blocks such as cobble or sand have no facing, for example) so it's not always necessary to set this when configuring a builder head. The facing of already-placed blocks can be altered through the use of the screwdriver tool." -.."\n\n".. -"Period - Builder and digger heads can be made periodic by changing the period value to something other than 1. This determines how frequently they trigger. A period of 1 triggers on every block, a period of 2 triggers once every second block, a period of 3 triggers once every third block, etc. These are useful when setting up a machine to place regularly-spaced features as it goes. For example, you could have a builder head that places a torch every 8 steps, or a digger block that punches a landing in the side of a vertical stairwell at every level." -.."\n\n".. -"Offset - The location at which a periodic module triggers is globally uniform. This is handy if you want to line up the blocks you're building (for example, placing pillars and a crosspiece every 4 blocks in a tunnel, or punching alcoves in a wall to place glass windows). If you wish to change how the pattern lines up, modify the \"offset\" setting." -.."\n\n".. -"Shift-right-clicking - since most of the blocks of the digging machine have control screens associated with right-clicking, building additional blocks on top of them or rotating them with the screwdriver requires the shift key to be held down when right-clicking on them." -.."\n\n".. -"Traction - Digtrons cannot fly. By default, they need to be touching one block of solid ground for every three blocks of Digtron in order to move. Digtrons can fall, though - traction is never needed when a Digtron is moving downward. \"Pusher\" controllers can ignore the need for traction when moving in any direction.") -}}) - -doc.add_entry("digtron", "noises", { - name = S("Audio cues"), - data = { text = -S("When a digging machine is unable to complete a cycle it will make one of several noises to indicate what the problem is. It will also set its mouseover text to explain what went wrong." -.."\n\n".. -"Squealing traction wheels indicates a mobility problem. If the squealing is accompanied by a buzzer, the digging machine has encountered an obstruction it can't dig through. This could be a protected region (the digging machine has only the priviledges of the player triggering it), a chest containing items, or perhaps the digger was incorrectly designed and can't dig the correctly sized and shaped cavity for it to move forward into. There are many possibilities." -.."\n\n".. -"Squealing traction wheels with no accompanying buzzer indicates that the digging machine doesn't have enough solid adjacent blocks to push off of. Tunnel boring machines cannot fly or swim, not even through lava, and they don't dig fast enough to \"catch sick air\" when they emerge from a cliffside. If you wish to cross a chasm you'll need to ensure that there are builder heads placing a solid surface as you go. If you've built a very tall digtron with a small surface footprint you may need to improve its traction by adding structural modules that touch the ground." -.."\n\n".. -"A buzzer by itself indicates that the Digtron has run out of fuel. There may be traces remaining in the hopper, but they're not enough to execute the next dig/build cycle." -.."\n\n".. -"A ringing bell indicates that there are insufficient materials in inventory to supply all the builder heads for this cycle." -.."\n\n".. -"A short high-pitched honk means that one or more of the builder heads don't have an item set. A common oversight, especially with large and elaborate digging machines, that might be hard to notice and annoying to fix if not noticed right away." -.."\n\n".. -"Splashing water sounds means your Digtron is digging adjacent to (or through) water-containing blocks. Digtrons are waterproof, but this might be a useful indication that you should take care when installing doors in the tunnel walls you've placed here." -.."\n\n".. -"A triple \"voop voop voop!\" alarm indicates that there is lava adjacent to your Digtron. Digtrons can't penetrate lava by default, and this alarm indicates that a non-lava-proof Digtron operator may wish to exercise caution when opening the door to clear the obstruction.") -}}) - -doc.add_entry("digtron", "tips", { - name = S("Tips and Tricks"), - data = { text = -S("To more easily visualize the operation of a Digtron, imagine that its cycle of operation follows these steps in order:" -.."\n\n".. -"* Dig\n* Move\n* Build\n* Allow dust to settle (ie, sand and gravel fall)" -.."\n\n".. -"If you're building a repeating pattern of blocks, your periodicity should be one larger than your largest offset. For example, if you've laid out builders to create a set of spiral stairs and the offsets are from 0 to 11, you'll want to use periodicity 12." -.."\n\n".. -"A good way to program a set of builders is to build a complete example of the structure you want them to create, then place builders against the structure and have them \"read\" all of its facings. This also lets you more easily visualize the tricks that might be needed to allow the digtron to pass through the structure as it's being built.") -}}) diff --git a/entities.lua b/entities.lua index 1a95cf6..b4a938d 100644 --- a/entities.lua +++ b/entities.lua @@ -1,3 +1,13 @@ +-- The default minetest.add_entity crashes with an exception if you try adding an entity in an unloaded area +-- this wrapper catches that exception and just ignores it. +safe_add_entity = function(pos, name) + local success, ret = pcall(minetest.add_entity, pos, name) + if success then return ret else return nil end +end + +------------------------------------------------------------------------------------------------- +-- For displaying where things get built under which periodicities + minetest.register_entity("digtron:marker", { initial_properties = { visual = "cube", @@ -8,8 +18,8 @@ minetest.register_entity("digtron:marker", { }, on_activate = function(self, staticdata) - minetest.after(5.0, - function(self) + minetest.after(5.0, + function(self) self.object:remove() end, self) @@ -24,31 +34,61 @@ minetest.register_entity("digtron:marker", { end, }) -minetest.register_entity("digtron:marker_vertical", { - initial_properties = { - visual = "cube", - visual_size = {x=1.05, y=1.05}, - textures = {"digtron_marker.png","digtron_marker.png","digtron_marker_side.png^[transformR90","digtron_marker_side.png^[transformR90","digtron_marker_side.png^[transformR90","digtron_marker_side.png^[transformR90"}, - collisionbox = {-0.525, -0.525, -0.525, 0.525, 0.525, 0.525}, - physical = false, - }, +local vertical = {x=1.5708, y=0, z=0} +-- TODO: update to new method of finding buildpos? +-- TODO: add item indicator entity as well +digtron.show_offset_markers = function(pos, offset, period) + local buildpos = digtron.find_new_pos(pos, minetest.get_node(pos).param2) + local x_pos = math.floor((buildpos.x+offset)/period)*period - offset + safe_add_entity({x=x_pos, y=buildpos.y, z=buildpos.z}, "digtron:marker") + if x_pos >= buildpos.x then + safe_add_entity({x=x_pos - period, y=buildpos.y, z=buildpos.z}, "digtron:marker") + end + if x_pos <= buildpos.x then + safe_add_entity({x=x_pos + period, y=buildpos.y, z=buildpos.z}, "digtron:marker") + end + + local y_pos = math.floor((buildpos.y+offset)/period)*period - offset + local entity = safe_add_entity({x=buildpos.x, y=y_pos, z=buildpos.z}, "digtron:marker") + if entity ~= nil then entity:set_rotation(vertical) end + if y_pos >= buildpos.y then + local entity = safe_add_entity({x=buildpos.x, y=y_pos - period, z=buildpos.z}, "digtron:marker") + if entity ~= nil then entity:set_rotation(vertical) end + end + if y_pos <= buildpos.y then + local entity = safe_add_entity({x=buildpos.x, y=y_pos + period, z=buildpos.z}, "digtron:marker") + if entity ~= nil then entity:set_rotation(vertical) end + end + + local z_pos = math.floor((buildpos.z+offset)/period)*period - offset + local entity = safe_add_entity({x=buildpos.x, y=buildpos.y, z=z_pos}, "digtron:marker") + if entity ~= nil then entity:setyaw(1.5708) end + if z_pos >= buildpos.z then + local entity = safe_add_entity({x=buildpos.x, y=buildpos.y, z=z_pos - period}, "digtron:marker") + if entity ~= nil then entity:setyaw(1.5708) end + end + if z_pos <= buildpos.z then + local entity = safe_add_entity({x=buildpos.x, y=buildpos.y, z=z_pos + period}, "digtron:marker") + if entity ~= nil then entity:setyaw(1.5708) end + end +end + +----------------------------------------------------------------------------------------------- +-- For displaying whether nodes are part of a digtron or are obstructed + +digtron.show_buildable_nodes = function(succeeded, failed) + if succeeded then + for _, pos in ipairs(succeeded) do + safe_add_entity(pos, "digtron:marker_crate_good") + end + end + if failed then + for _, pos in ipairs(failed) do + safe_add_entity(pos, "digtron:marker_crate_bad") + end + end +end - on_activate = function(self, staticdata) - minetest.after(5.0, - function(self) - self.object:remove() - end, - self) - end, - - on_rightclick=function(self, clicker) - self.object:remove() - end, - - on_punch = function(self, hitter) - self.object:remove() - end, -}) minetest.register_entity("digtron:marker_crate_good", { initial_properties = { @@ -57,11 +97,12 @@ minetest.register_entity("digtron:marker_crate_good", { textures = {"digtron_crate.png", "digtron_crate.png", "digtron_crate.png", "digtron_crate.png", "digtron_crate.png", "digtron_crate.png"}, collisionbox = {-0.525, -0.525, -0.525, 0.525, 0.525, 0.525}, physical = false, + glow = minetest.LIGHT_MAX, }, on_activate = function(self, staticdata) - minetest.after(digtron.config.marker_crate_good_duration, - function(self) + minetest.after(digtron.config.marker_crate_good_duration, + function(self) self.object:remove() end, self) @@ -83,11 +124,12 @@ minetest.register_entity("digtron:marker_crate_bad", { textures = {"digtron_no_entry.png", "digtron_no_entry.png", "digtron_no_entry.png", "digtron_no_entry.png", "digtron_no_entry.png", "digtron_no_entry.png"}, collisionbox = {-0.525, -0.525, -0.525, 0.525, 0.525, 0.525}, physical = false, + glow = minetest.LIGHT_MAX, }, on_activate = function(self, staticdata) - minetest.after(digtron.config.marker_crate_bad_duration, - function(self) + minetest.after(digtron.config.marker_crate_bad_duration, + function(self) self.object:remove() end, self) @@ -102,13 +144,63 @@ minetest.register_entity("digtron:marker_crate_bad", { end, }) +----------------------------------------------------------------------------------------------------------------- +-- Builder items + +digtron.remove_builder_item = function(pos) + local objects = minetest.env:get_objects_inside_radius(pos, 0.5) + if objects ~= nil then + for _, obj in ipairs(objects) do + if obj and obj:get_luaentity() and obj:get_luaentity().name == "digtron:builder_item" then + obj:remove() + end + end + end +end + +-- Used by unassembled builders +digtron.update_builder_item = function(pos) + local node = minetest.get_node(pos) + if minetest.get_item_group(node.name, "digtron") ~= 4 then + return + end + local target_pos = vector.add(pos, minetest.facedir_to_dir(node.param2)) + digtron.remove_builder_item(target_pos) + local meta = minetest.get_meta(pos) + local item = meta:get_string("item") + if item ~= "" then + digtron.create_builder_item = item + safe_add_entity(target_pos,"digtron:builder_item") + end +end + +-- Updates entire Digtron +digtron.update_builder_items = function(digtron_id) + local layout = digtron.get_layout(digtron_id) + local root_pos = digtron.get_pos(digtron_id) + + for layout_node_id, data in pairs(layout) do + local node = data.node + if minetest.get_item_group(node.name, "digtron") == 4 then + local item = data.meta.fields.item + local pos = vector.add(data.pos, root_pos) + local target_pos = vector.add(pos, minetest.facedir_to_dir(node.param2)) + digtron.remove_builder_item(target_pos) + if item ~= "" then + digtron.create_builder_item = item + safe_add_entity(target_pos,"digtron:builder_item") + end + end + end +end + minetest.register_entity("digtron:builder_item", { initial_properties = { hp_max = 1, is_visible = true, visual = "wielditem", - visual_size = {x=0.25, y=0.25}, + visual_size = {x=0.3333, y=0.3333}, collisionbox = {0,0,0,0,0,0}, physical = false, textures = {""}, @@ -118,13 +210,25 @@ minetest.register_entity("digtron:builder_item", { on_activate = function(self, staticdata) local props = self.object:get_properties() if staticdata ~= nil and staticdata ~= "" then - local pos = self.object:getpos() - local node = minetest.get_node(pos) - if minetest.get_node_group(node.name, "digtron") ~= 4 then - -- We were reactivated without a builder node on our location, self-destruct + local pos = self.object:get_pos() + local adjacent_builder = false + for _, dir in ipairs(digtron.cardinal_dirs) do + local target_pos = vector.add(pos, dir) + local node = minetest.get_node(target_pos) + if minetest.get_item_group(node.name, "digtron") == 4 then + -- Not checking whether the adjacent builder is aimed right, + -- has the right builder_item, etc. This is just a failsafe + -- to clean up entities that somehow got left behind when a + -- Digtron moved, not that important really + adjacent_builder = true + break + end + end + if not adjacent_builder then self.object:remove() return end + props.textures = {staticdata} self.object:set_properties(props) elseif digtron.create_builder_item ~= nil then diff --git a/functions.lua b/functions.lua new file mode 100644 index 0000000..a0cd131 --- /dev/null +++ b/functions.lua @@ -0,0 +1,1470 @@ +local mod_meta = digtron.mod_meta + +local cache = {} + +local S = digtron.S + +local zero_pos = {x=0, y=0, z=0} + +minetest.register_chatcommand("digtron_dump", { + --params = " ", -- Short parameter description + description = "dump digtron data to debug", -- Full description + privs = {server=true}, -- Require the "privs" privilege to run + func = function(name, param) + minetest.debug(dump(mod_meta:to_table())) + end, +}) + +minetest.register_chatcommand("digtron_wipe", { + --params = " ", -- Short parameter description + description = "dump digtron data to debug", -- Full description + privs = {server=true}, -- Require the "privs" privilege to run + func = function(name, param) + -- Wipes mod_meta + for field, value in pairs(mod_meta:to_table().fields) do + mod_meta:set_string(field, "") + end + end, +}) + +local damage_hp = digtron.config.damage_hp +-- see predict_dig for how punch_data gets calculated +local damage_creatures = function(punch_data, items_dropped) + local target_pos = punch_data[2] + local objects = minetest.get_objects_inside_radius(target_pos, 1.0) + if objects ~= nil then + local source_pos = punch_data[1] + for _, obj in ipairs(objects) do + local dir = vector.normalize(vector.subtract(obj:get_pos(), source_pos)) + local armour_multiplier = 1 + local fleshy_armour = obj:get_armor_groups().fleshy + if fleshy_armour then + armour_multiplier = fleshy_armour/100 + end + if obj:is_player() then + if obj.add_player_velocity then -- added pretty recently, see https://github.com/minetest/minetest/commit/291e7730cf24ba5081f10b5ddbf2494951333827 + obj:add_player_velocity(dir) + else + obj:set_pos(vector.add(obj:get_pos(), vector.multiply(dir,1))) + end + obj:set_hp(math.max(obj:get_hp() - damage_hp*armour_multiplier, 0)) + else + local lua_entity = obj:get_luaentity() + if lua_entity ~= nil then + -- suck up items in Digtron's path + if lua_entity.name == "__builtin:item" then + table.insert(items_dropped, ItemStack(lua_entity.itemstring)) + lua_entity.itemstring = "" + obj:remove() + else + if lua_entity.add_velocity then + lua_entity:add_velocity(dir) + end + obj:set_hp(math.max(obj:get_hp() - damage_hp*armour_multiplier, 0)) + end + end + end + end + end + -- If we killed any mobs they might have dropped some stuff, vacuum that up now too. + objects = minetest.get_objects_inside_radius(target_pos, 1.0) + if objects ~= nil then + for _, obj in ipairs(objects) do + if not obj:is_player() then + local lua_entity = obj:get_luaentity() + if lua_entity ~= nil and lua_entity.name == "__builtin:item" then + table.insert(items_dropped, ItemStack(lua_entity.itemstring)) + lua_entity.itemstring = "" + obj:remove() + end + end + end + end +end + +----------------------------------------------------------------------- +-- Inventory + +local modpath = minetest.get_modpath(minetest.get_current_modname()) +local inventory_functions = dofile(modpath.."/inventories.lua") + +local retrieve_inventory = inventory_functions.retrieve_inventory +local persist_inventory = inventory_functions.persist_inventory +local get_predictive_inventory = inventory_functions.get_predictive_inventory +local commit_predictive_inventory = inventory_functions.commit_predictive_inventory +local clear_predictive_inventory = inventory_functions.clear_predictive_inventory + +---------------------------------------------------------------------------- +-- Common utility functions + +local protection_check = function(pos, player_name) + if minetest.is_protected(pos, player_name) and + not minetest.check_player_privs(player_name, "protection_bypass") then + return true + end + return false +end + +local function deep_copy(table_in) + local table_out = {} + for index, value in pairs(table_in) do + if type(value) == "table" then + table_out[index] = deep_copy(value) + else + table_out[index] = value + end + end + return table_out +end + +-------------------------------------------------------------------------------------- + +local create_new_id = function() + local digtron_id = "digtron" .. tostring(math.random(1, 2^21)) + -- It's super unlikely that we'll get a collision, but what the heck - maybe something will go + -- wrong with the random number source + while mod_meta:get_string(digtron_id..":layout") ~= "" do + digtron_id = "digtron" .. tostring(math.random(1, 2^21)) + end + return digtron_id +end + +-- Deletes a Digtron record. Note: just throws everything away, this is not digtron.disassemble. +local dispose_callbacks = {} +local dispose_id = function(digtron_id) + -- name doesn't bother caching + mod_meta:set_string(digtron_id..":name", "") + + -- inventory handles itself specially too + mod_meta:set_string(digtron_id..":inv", "") + minetest.remove_detached_inventory(digtron_id) + + -- clears the cache tables + for i, func in ipairs(dispose_callbacks) do + func(digtron_id) + end +end + +-------------------------------------------------------------------------------------------- +-- Name + +-- Not bothering with a dynamic table store for names, they're just strings with no need for serialization or deserialization +local get_name = function(digtron_id) + local digtron_name = mod_meta:get_string(digtron_id..":name") + if digtron_name == "" then + return S("Unnamed Digtron") + else + return digtron_name + end +end + +local set_name = function(digtron_id, digtron_name) + -- Don't allow a name to be set for a non-existent Digtron + if mod_meta:get(digtron_id..":layout") then + mod_meta:set_string(digtron_id..":name", digtron_name) + end +end + +------------------------------------------------------------------------------------------------------- +-- Tables stored to metadata and cached locally + +local get_table_functions = function(identifier) + cache[identifier] = {} + + local persist_func = function(digtron_id, tbl) + mod_meta:set_string(digtron_id..":"..identifier, minetest.serialize(tbl)) + cache[identifier][digtron_id] = tbl + end + + local retrieve_func = function(digtron_id) + local current = cache[identifier][digtron_id] + if current then + return current + end + local tbl_string = mod_meta:get_string(digtron_id..":"..identifier) + if tbl_string ~= "" then + current = minetest.deserialize(tbl_string) + if current then + cache[identifier][digtron_id] = current + end + return current + end + return nil + end + + local dispose_func = function(digtron_id) + mod_meta:set_string(digtron_id..":"..identifier, "") + cache[identifier][digtron_id] = nil + end + + -- add a callback for dispose_id + table.insert(dispose_callbacks, dispose_func) + + return persist_func, retrieve_func, dispose_func +end + +local persist_layout, retrieve_layout = get_table_functions("layout") +local persist_pos, retrieve_pos, dispose_pos = get_table_functions("pos") +local persist_sequence, retrieve_sequence = get_table_functions("sequence") + +------------------------------------------------------------------------------------------------------- +-- Layout creation helpers + +local duplicate = function(digtron_id) + local layout = retrieve_layout(digtron_id) + if layout == nil then + minetest.log("error", "[Digtron] digtron.duplicate called with non-existent id " .. digtron_id) + return + end + local new_layout = deep_copy(layout) -- make a copy because persist_layout caches its parameter as-is + local new_id = create_new_id() + local new_name = S("Copy of @1", get_name(digtron_id)) + persist_layout(new_id, new_layout) + set_name(new_id, new_name) + + local old_inv = retrieve_inventory(digtron_id) + local new_inv = retrieve_inventory(new_id) + for inv_name, item_list in pairs(old_inv:get_lists()) do + -- Don't copy inventory contents, just copy sizes + new_inv:set_size(inv_name, #item_list) + end + persist_inventory(new_id) + + local new_controller = ItemStack("digtron:controller") + local meta = new_controller:get_meta() + meta:set_string("digtron_id", new_id) + meta:set_int("digtron_layout_node_id", 1) -- the root node is always in index 1, see "assemble" + meta:set_string("description", new_name) + return new_controller +end + +-- recursive function searches out all connected unassigned digtron nodes +local get_all_digtron_nodes +get_all_digtron_nodes = function(pos, digtron_nodes, digtron_adjacent, player_name) + for _, dir in ipairs(digtron.cardinal_dirs) do + local test_pos = vector.add(pos, dir) + local test_hash = minetest.hash_node_position(test_pos) + if not (digtron_nodes[test_hash] or digtron_adjacent[test_hash]) then -- don't test twice + local test_node = minetest.get_node(test_pos) + local group_value = minetest.get_item_group(test_node.name, "digtron") + if group_value > 0 then + local meta = minetest.get_meta(test_pos) + if meta:contains("digtron_id") then + -- Node is part of an existing digtron, don't incorporate it + digtron_adjacent[test_hash] = true + --elseif TODO test for protected node status using player_name + else + --test_node.group_value = group_value -- for later ease of reference + digtron_nodes[test_hash] = test_node + get_all_digtron_nodes(test_pos, digtron_nodes, digtron_adjacent, player_name) -- recurse + end + else + -- don't record details, just keeping track of Digtron's borders + digtron_adjacent[test_hash] = true + end + end + end +end + +------------------------------------------------------------------------------------------------- +-- Cache-only data, not persisted + +cache_bounding_box = {} +local update_bounding_box = function(bounding_box, pos) + bounding_box.minp.x = math.min(bounding_box.minp.x, pos.x) + bounding_box.minp.y = math.min(bounding_box.minp.y, pos.y) + bounding_box.minp.z = math.min(bounding_box.minp.z, pos.z) + bounding_box.maxp.x = math.max(bounding_box.maxp.x, pos.x) + bounding_box.maxp.y = math.max(bounding_box.maxp.y, pos.y) + bounding_box.maxp.z = math.max(bounding_box.maxp.z, pos.z) +end +local retrieve_bounding_box = function(digtron_id) + local val = cache_bounding_box[digtron_id] + if val then return val end + + local layout = retrieve_layout(digtron_id) + if layout == nil then return nil end + + local bbox = {minp = {x=0, y=0, z=0}, maxp = {x=0, y=0, z=0}} + for layout_node_id, data in pairs(layout) do + update_bounding_box(bbox, data.pos) + end + cache_bounding_box[digtron_id] = bbox + return bbox +end + +cache_all_adjacent_pos = {} +cache_all_digger_targets = {} +cache_all_builder_targets = {} +local refresh_adjacent = function(digtron_id) + local layout = retrieve_layout(digtron_id) + if layout == nil then return nil end + + local adjacent = {} -- all adjacent nodes. TODO: if implementing traction wheels, won't be needed + local adjacent_to_diggers = {} + local adjacent_to_builders = {} + + local all_digtron_node_hashes = {} -- track the locations of the digtron nodes themselves, these will be omitted from adjacency + for layout_node_id, data in pairs(layout) do + local hash = minetest.hash_node_position(data.pos) + data.hash = hash + all_digtron_node_hashes[hash] = true + end + + for layout_node_id, data in pairs(layout) do + local hash = data.hash + + for _, dir_hash in ipairs(digtron.cardinal_dirs_hash) do + local potential_adjacent = hash + dir_hash + if not all_digtron_node_hashes[potential_adjacent] then + adjacent[potential_adjacent] = true + end + end + + local digtron_group = minetest.get_item_group(data.node.name, "digtron") + + -- Diggers + if digtron_group >= 10 and digtron_group <= 13 then + -- All diggers target the node directly in front of them + local targets = {} + local dir_hash = digtron.facedir_to_dir_hash(data.node.param2) + local potential_target = hash + dir_hash -- pointed at this hash + if not all_digtron_node_hashes[potential_target] then + table.insert(targets, minetest.get_position_from_hash(potential_target)) + end + + -- If it's a dual digger, add a second dir + if digtron_group == 11 or digtron_group == 13 then + dir_hash = digtron.facedir_to_down_hash(data.node.param2) + potential_target = hash + dir_hash -- pointed at this hash + if not all_digtron_node_hashes[potential_target] then + table.insert(targets, minetest.get_position_from_hash(potential_target)) + end + end + + local soft = nil + -- if it's a soft digger note that fact. + if digtron_group == 12 or digtron_group == 13 then + soft = true + end + + local fields = data.meta.fields + if #targets > 0 then + table.insert(adjacent_to_diggers, { + pos = data.pos, + period = tonumber(fields.period) or 1, + offset = tonumber(fields.offset) or 0, + targets = targets, + soft = soft, + }) + end + end + + -- Builders + if digtron_group == 4 then + local dir_hash = digtron.facedir_to_dir_hash(data.node.param2) + local potential_target = hash + dir_hash + if not all_digtron_node_hashes[potential_target] then + local fields = data.meta.fields + -- TODO: trace extrusion and if it intersects Digtron layout cap it there. + -- This is getting pretty edge case, though, don't worry about it until you're + -- completely bored + table.insert(adjacent_to_builders, { + pos = data.pos, + period = tonumber(fields.period) or 1, + offset = tonumber(fields.offset) or 0, + item = fields.item, + facing = tonumber(fields.facing) or 0, -- facing of built node + extrusion = tonumber(fields.extrusion) or 1, + dir = minetest.facedir_to_dir(data.node.param2), + }) + end + end + end + + cache_all_adjacent_pos[digtron_id] = adjacent + cache_all_digger_targets[digtron_id] = adjacent_to_diggers + cache_all_builder_targets[digtron_id] = adjacent_to_builders +end +local retrieve_all_adjacent_pos = function(digtron_id) + local val = cache_all_adjacent_pos[digtron_id] + if val then return val end + refresh_adjacent(digtron_id) + return cache_all_adjacent_pos[digtron_id] +end +local retrieve_all_digger_targets = function(digtron_id) + local val = cache_all_digger_targets[digtron_id] + if val then return val end + refresh_adjacent(digtron_id) + return cache_all_digger_targets[digtron_id] +end +local retrieve_all_builder_targets = function(digtron_id) + local val = cache_all_builder_targets[digtron_id] + if val then return val end + refresh_adjacent(digtron_id) + return cache_all_builder_targets[digtron_id] +end + +-- call this whenever a stored layout is modified (eg, by rotating it) +-- automatically called on dispose +local invalidate_layout_cache = function(digtron_id) + cache_bounding_box[digtron_id] = nil + cache_all_adjacent_pos[digtron_id] = nil + cache_all_digger_targets[digtron_id] = nil + cache_all_builder_targets[digtron_id] = nil +end +table.insert(dispose_callbacks, invalidate_layout_cache) + +-------------------------------------------------------------------------------------------------------- +-- assemble and disassemble + +-- Returns the id of the new Digtron record, or nil on failure +local assemble = function(root_pos, player_name) + local root_node = minetest.get_node(root_pos) + + local root_meta = minetest.get_meta(root_pos) + if root_meta:contains("digtron_id") then + -- Already assembled. TODO: validate that the digtron_id actually exists as well + minetest.log("error", "[Digtron] digtron.assemble called with pos " .. minetest.pos_to_string(root_pos) + .. " but the controller at this location was already part of a assembled Digtron.") + return nil + end + local digtron_name = root_meta:get_string("infotext") + + -- This should be called on an unassembled node. + if root_node.name ~= "digtron:controller_unassembled" then + -- Called on an incorrect node + minetest.log("error", "[Digtron] digtron.assemble called with pos " .. minetest.pos_to_string(root_pos) + .. " but the node at this location was " .. root_node.name) + return nil + end + + local root_hash = minetest.hash_node_position(root_pos) + local digtron_nodes = {[root_hash] = root_node} -- Nodes that are part of Digtron. + -- Initialize with the controller, it won't be added by get_all_adjacent_digtron_nodes + local digtron_adjacent = {} -- Nodes that are adjacent to Digtron but not a part of it. + -- There's a slight inefficiency in throwing away digtron_adjacent when retrieve_all_adjacent_pos could + -- use this info, but it's small and IMO not worth the complexity. + get_all_digtron_nodes(root_pos, digtron_nodes, digtron_adjacent, player_name) + + local digtron_id = create_new_id(root_pos) + local digtron_inv = retrieve_inventory(digtron_id) + + local layout = {} + + for hash, node in pairs(digtron_nodes) do + local pos = minetest.get_position_from_hash(hash) + local relative_pos = vector.subtract(pos, root_pos) + + local current_meta + if hash == root_hash then + current_meta = root_meta -- we're processing the controller, we already have a reference to its meta + else + current_meta = minetest.get_meta(minetest.get_position_from_hash(hash)) + end + + local current_meta_table = current_meta:to_table() + + if current_meta_table.fields.digtron_id then + -- Trying to incorporate part of an existing digtron, should be impossible. + minetest.log("error", "[Digtron] digtron.assemble tried to incorporate a Digtron node of type " + .. node.name .. " at " .. minetest.pos_to_string(minetest.get_position_from_hash(hash)) + .. " that was already assigned to digtron id " .. current_meta_table.fields.digtron_id) + dispose_id(digtron_id) + return nil + end + -- Process inventories specially + for listname, items in pairs(current_meta_table.inventory) do + local count = #items + -- increase the corresponding detached inventory size + digtron_inv:set_size(listname, digtron_inv:get_size(listname) + count) + for _, stack in ipairs(items) do + digtron_inv:add_item(listname, stack) + end + -- erase actual items from stored layout metadata, the detached inventory is authoritative + -- store the inventory size so the inventory can be easily recreated + current_meta_table.inventory[listname] = #items + end + + -- If the node being incorporated into the assembled digtron has a "_digtron_assembled_node" property + -- defined, then pretend it's that node rather than the actual node. + local node_def = minetest.registered_nodes[node.name] + if node_def and node_def._digtron_assembled_node then + node.name = node_def._digtron_assembled_node + minetest.swap_node(minetest.get_position_from_hash(hash), node) + end + + node.param1 = nil -- we don't care about param1, wipe it to save space + table.insert(layout, {meta = current_meta_table, node = node, pos = relative_pos}) + local meta = minetest.get_meta(pos) + end + + -- Ensure the root node is in position 1 + for layout_node_id, data in ipairs(layout) do + if vector.equals(data.pos, zero_pos) then + layout[layout_node_id] = layout[1] + layout[1] = data + break + end + end + + persist_inventory(digtron_id) + persist_layout(digtron_id, layout) + set_name(digtron_id, digtron_name) + invalidate_layout_cache(digtron_id) + persist_pos(digtron_id, root_pos) + persist_sequence(digtron_id, digtron.default_sequence()) + + -- Wipe out the inventories of all in-world nodes, it's stored in the mod_meta now. + -- Wait until now to do it in case the above loop fails partway through and we need to abort. + for layout_node_id, data in ipairs(layout) do + local node_pos = vector.add(root_pos, data.pos) + local node_meta + if vector.equals(root_pos, node_pos) then + node_meta = root_meta -- we're processing the controller, we already have a reference to its meta + else + node_meta = minetest.get_meta(node_pos) + end + local inv = node_meta:get_inventory() + + for listname, items in pairs(inv:get_lists()) do + for i = 1, #items do + inv:set_stack(listname, i, ItemStack("")) + end + end + + -- Set metadata on the nodes to indicate that they've been incorporated into an assembled Digtron + node_meta:set_string("digtron_id", digtron_id) + node_meta:set_int("digtron_layout_node_id", layout_node_id) + node_meta:mark_as_private("digtron_id") + node_meta:mark_as_private("digtron_layout_node_id") + end + + minetest.log("action", "Digtron " .. digtron_id .. " assembled at " .. minetest.pos_to_string(root_pos) + .. " by " .. player_name) + minetest.sound_play("digtron_machine_assemble", {gain = 0.5, pos=root_pos}) + + return digtron_id +end + +local function log_prefix(function_name, digtron_id, target_name, target_pos) + return "[Digtron] " .. function_name .. " tried interacting with one of ".. digtron_id .. "'s " + .. target_name .. "s at " .. minetest.pos_to_string(target_pos) +end + +-- Returns pos, node, and meta for the digtron node provided the in-world node matches the layout +-- returns nil otherwise +local get_valid_data = function(digtron_id, layout_node_id, root_pos, data, function_name) + local node_pos = data.pos + if not node_pos then + minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with " .. digtron_id + .. " node id " .. layout_node_id .. " but there was no pos in its data: " .. dump(data)) + return + end + node_pos = vector.add(root_pos, node_pos) + local data_node_name = data.node.name + local node = minetest.get_node(node_pos) + local node_meta = minetest.get_meta(node_pos) + local target_digtron_id = node_meta:get_string("digtron_id") + local target_node_id = node_meta:get_int("digtron_layout_node_id") + + if target_node_id ~= layout_node_id then + -- A recoverable error, but may indicate something's wrong elsewhere. + minetest.log("warning", log_prefix(function_name, digtron_id, data_node_name, node_pos) + .. " but the node at that location was marked as layout id " .. target_node_id + .. " instead of " .. layout_node_id) + node_meta:set_int("digtron_layout_node_id", layout_node_id) + node_meta:mark_as_private("digtron_layout_node_id") + end + if data_node_name ~= node.name then + minetest.log("error", log_prefix(function_name, digtron_id, data_node_name, node_pos) + .. " but the node at that location was of type " .. node.name) + return + elseif target_digtron_id ~= digtron_id then + if target_digtron_id ~= "" then + minetest.log("error", log_prefix(function_name, digtron_id, data_node_name, node_pos) + .. " but the node at that location had a non-matching digtron_id value of \"" + .. target_digtron_id .. "\"") + return + else + -- Allow digtron to recover from bad map metadata writes, the bane of Digtron 1.0's existence + minetest.log("warning", log_prefix(function_name, digtron_id, data_node_name, node_pos) + .. " but the node at that location had no digtron_id in its metadata. " + .. "Since the node type matched the layout, however, it was included anyway. It's possible " + .. "its metadata was not written correctly by a previous Digtron activity.") + node_meta:set_string("digtron_id", digtron_id) + node_meta:mark_as_private("digtron_id") + return node_pos, node, node_meta + end + end + return node_pos, node, node_meta +end + +-- Turns the Digtron back into pieces +local disassemble = function(digtron_id, player_name) + local root_pos = retrieve_pos(digtron_id) + if not root_pos then + minetest.log("error", "[Digtron] digtron.disassemble was unable to find a position for " .. digtron_id + .. ", disassembly was impossible. Has the digtron been removed from world?") + return + end + + local layout = retrieve_layout(digtron_id) + local inv = retrieve_inventory(digtron_id) + + if not (layout and inv) then + minetest.log("error", "[Digtron] digtron.disassemble was unable to find either layout or inventory record for " .. digtron_id + .. ", disassembly was impossible. Clearing any other remaining data for this id.") + dispose_id(digtron_id) + return + end + + -- Write metadata and inventory to in-world node at this location + for layout_node_id, data in pairs(layout) do + local node_pos, node, node_meta = get_valid_data(digtron_id, layout_node_id, root_pos, data, "disassemble") + + if node_pos then + local node_inv = node_meta:get_inventory() + for listname, size in pairs(data.meta.inventory) do + node_inv:set_size(listname, size) + local digtron_inv_list = inv:get_list(listname) + if digtron_inv_list then + for i, itemstack in ipairs(digtron_inv_list) do + -- add everything, putting leftovers back in the main inventory + inv:set_stack(listname, i, node_inv:add_item(listname, itemstack)) + end + else + minetest.log("warning", "[Digtron] inventory list " .. listname .. " existed in " .. node.name + .. " that was part of " .. digtron_id .. " but was not present in the detached inventory for this digtron." + .. " This should not have happened, please report an issue to Digtron programmers," + .. " but it shouldn't impact digtron disassembly.") + end + end + + local node_def = minetest.registered_nodes[node.name] + if node_def and node_def._digtron_disassembled_node then + minetest.swap_node(node_pos, {name=node_def._digtron_disassembled_node, param2=node.param2}) + end + + -- Ensure node metadata fields are all set, too + for field, value in pairs(data.meta.fields) do + node_meta:set_string(field, value) + end + + -- Clear digtron_id, this node is no longer part of an active digtron + node_meta:set_string("digtron_id", "") + end + end + + -- replace the controller node with the disassembled version + local root_node = minetest.get_node(root_pos) + if root_node.name == "digtron:controller" then + root_node.name = "digtron:controller_disassembled" + minetest.set_node(root_pos, root_node) + end + local root_meta = minetest.get_meta(root_pos) + root_meta:set_string("infotext", get_name(digtron_id)) + + minetest.log("action", "Digtron " .. digtron_id .. " disassembled at " .. minetest.pos_to_string(root_pos) + .. " by " .. player_name) + minetest.sound_play("digtron_machine_disassemble", {gain = 0.5, pos=root_pos}) + + dispose_id(digtron_id) + + return root_pos +end + +------------------------------------------------------------------------------------------ +-- Moving Digtrons around + +-- Removes the in-world nodes of a digtron +-- Does not destroy its layout info +-- returns a table of vectors of all the nodes that were removed, or nil on failure +local remove_from_world = function(digtron_id, player_name) + local layout = retrieve_layout(digtron_id) + local root_pos = retrieve_pos(digtron_id) + + if not layout then + minetest.log("error", "[Digtron] digtron.remove_from_world Unable to find layout record for " .. digtron_id + .. ", wiping any remaining metadata for this id to prevent corruption. Sorry!") + if root_pos then + local meta = minetest.get_meta(root_pos) + meta:set_string("digtron_id", "") + end + dispose_id(digtron_id) + return nil + end + + if not root_pos then + minetest.log("error", "[Digtron] digtron.remove_from_world Unable to find position for " .. digtron_id + .. ", it may have already been removed from the world.") + return nil + end + + local nodes_to_destroy = {} + for layout_node_id, data in pairs(layout) do + local node_pos = get_valid_data(digtron_id, layout_node_id, root_pos, data, "remove_from_world") + if node_pos then + table.insert(nodes_to_destroy, node_pos) + end + end + + minetest.bulk_set_node(nodes_to_destroy, {name="air"}) + dispose_pos(digtron_id) + return nodes_to_destroy +end + +-- Tests if a Digtron can be built at the designated location +local is_buildable_to = function(digtron_id, layout, root_pos, player_name, ignore_nodes, return_immediately_on_failure) + -- If this digtron is already in-world, we're likely testing as part of a movement attempt. + -- Record its existing node locations, they will be treated as buildable_to + local old_root_pos = retrieve_pos(digtron_id) + local old_layout = retrieve_layout(digtron_id) + if layout == nil then + layout = old_layout + end + + local ignore_hashes = {} + if old_root_pos then + for layout_node_id, data in pairs(old_layout) do + local old_hash = minetest.hash_node_position(vector.add(data.pos, old_root_pos)) + ignore_hashes[old_hash] = true + end + end + if ignore_nodes then + for _, ignore_pos in ipairs(ignore_nodes) do + ignore_hashes[minetest.hash_node_position(ignore_pos)] = true + end + end + + local succeeded = {} + local failed = {} + local permitted = true + + for hash, data in pairs(layout) do + -- Don't use get_valid_data, the Digtron isn't in-world yet + local node_pos = vector.add(minetest.get_position_from_hash(hash), root_pos) + local node_hash = minetest.hash_node_position(node_pos) + local node = minetest.get_node(node_pos) + local node_def = minetest.registered_nodes[node.name] + if not ( + (node_def and node_def.buildable_to) + or ignore_hashes[node_hash]) or + protection_check(node_pos, player_name) + then + if return_immediately_on_failure then + return false -- no need to test further, don't return node positions + else + permitted = false + table.insert(failed, node_pos) + end + elseif not return_immediately_on_failure then + table.insert(succeeded, node_pos) + end + end + + return permitted, succeeded, failed +end + +-- Places the Digtron into the world. +local build_to_world = function(digtron_id, layout, root_pos, player_name) + if layout == nil then + layout = retrieve_layout(digtron_id) + end + local built_positions = {} + for layout_node_id, data in pairs(layout) do + -- Don't use get_valid_data, the Digtron isn't in-world yet + local node_pos = vector.add(data.pos, root_pos) + minetest.debug("setting root at " .. minetest.pos_to_string(root_pos)) + minetest.debug("setting node at " .. minetest.pos_to_string(data.pos)) + minetest.set_node(node_pos, data.node) + local meta = minetest.get_meta(node_pos) + for field, value in pairs(data.meta.fields) do + meta:set_string(field, value) + end + meta:set_string("digtron_id", digtron_id) + meta:set_int("digtron_layout_node_id", layout_node_id) + meta:mark_as_private("digtron_id") + meta:mark_as_private("digtron_layout_node_id") + table.insert(built_positions, node_pos) + end + persist_pos(digtron_id, root_pos) + + return built_positions +end + +local move = function(digtron_id, dest_pos, player_name) + local layout = retrieve_layout(digtron_id) + local permitted, succeeded, failed = is_buildable_to(digtron_id, layout, dest_pos, player_name) + if permitted then + local removed = remove_from_world(digtron_id, player_name) + if removed then + build_to_world(digtron_id, layout, dest_pos, player_name) + minetest.sound_play("digtron_engine", {gain = 0.5, pos=dest_pos}) + for _, removed_pos in ipairs(removed) do + minetest.check_for_falling(removed_pos) + end + end + return true + else + digtron.show_buildable_nodes({}, failed) + minetest.sound_play("digtron_squeal", {gain = 0.5, pos=dest_pos}) + return false + end +end + +------------------------------------------------------------------------ +-- Rotation + +local rotate_layout = function(digtron_id, axis) + local layout = retrieve_layout(digtron_id) + local axis_hash = minetest.hash_node_position(axis) + local rotated_layout = {} + for layout_node_id, data in pairs(layout) do + local duplicate_data = deep_copy(data) + -- Facings + local node_name = duplicate_data.node.name + local node_def = minetest.registered_nodes[node_name] + if node_def.paramtype2 == "wallmounted" then + duplicate_data.node.param2 = digtron.rotate_wallmounted(axis_hash, duplicate_data.node.param2) + elseif node_def.paramtype2 == "facedir" then + duplicate_data.node.param2 = digtron.rotate_facedir(axis_hash, duplicate_data.node.param2) + end + + -- Rotate builder item facings + if minetest.get_item_group(node_name, "digtron") == 4 then + local build_item = duplicate_data.meta.fields.item + local build_item_def = minetest.registered_items[build_item] + if build_item_def.paramtype2 == "wallmounted" then + duplicate_data.meta.fields.facing = digtron.rotate_wallmounted(axis_hash, tonumber(duplicate_data.meta.fields.facing)) + elseif build_item_def.paramtype2 == "facedir" then + duplicate_data.meta.fields.facing = digtron.rotate_facedir(axis_hash, tonumber(duplicate_data.meta.fields.facing)) + end + end + + -- Position + local pos = data.pos + duplicate_data.pos = digtron.rotate_pos(axis_hash, pos) + rotated_layout[layout_node_id] = duplicate_data + end + + return rotated_layout +end + +local rotate = function(digtron_id, axis, player_name) + local rotated_layout = rotate_layout(digtron_id, axis) + local root_pos = retrieve_pos(digtron_id) + local permitted, succeeded, failed = is_buildable_to(digtron_id, rotated_layout, root_pos, player_name) + if permitted then + local removed = remove_from_world(digtron_id, player_name) + if removed then + build_to_world(digtron_id, rotated_layout, root_pos, player_name) + minetest.sound_play("digtron_hydraulic", {gain = 0.5, pos=dest_pos}) + persist_layout(digtron_id, rotated_layout) + -- Don't need to do fancy callback checking for digtron nodes since I made all those + -- nodes and I know they don't have anything that needs to be done for them. + -- Just check for falling nodes. + for _, removed_pos in ipairs(removed) do + minetest.check_for_falling(removed_pos) + end + invalidate_layout_cache(digtron_id) + end + return true + else + digtron.show_buildable_nodes({}, failed) + minetest.sound_play("digtron_squeal", {gain = 0.5, pos=root_pos}) + return false + end +end + +------------------------------------------------------------------------------------ +-- Digging + +local is_soft_material = function(target_name) + if minetest.get_item_group(target_name, "crumbly") ~= 0 or + minetest.get_item_group(target_name, "choppy") ~= 0 or + minetest.get_item_group(target_name, "snappy") ~= 0 or + minetest.get_item_group(target_name, "oddly_breakable_by_hand") ~= 0 or + minetest.get_item_group(target_name, "fleshy") ~= 0 then + return true + end + return false +end + +local get_material_cost = function(target_name) + local material_cost = 0 + local in_known_group = false + if minetest.get_item_group(target_name, "cracky") ~= 0 then + in_known_group = true + material_cost = math.max(material_cost, digtron.config.dig_cost_cracky) + end + if minetest.get_item_group(target_name, "crumbly") ~= 0 then + in_known_group = true + material_cost = math.max(material_cost, digtron.config.dig_cost_crumbly) + end + if minetest.get_item_group(target_name, "choppy") ~= 0 then + in_known_group = true + material_cost = math.max(material_cost, digtron.config.dig_cost_choppy) + end + if not in_known_group then + material_cost = digtron.config.dig_cost_default + end + return material_cost +end + +local predict_dig = function(digtron_id, player_name, controlling_coordinate) + local predictive_inv = get_predictive_inventory(digtron_id) + local root_pos = retrieve_pos(digtron_id) + if not (root_pos and predictive_inv) then + minetest.log("error", "[Digtron] predict_dig failed to retrieve either " + .."a predictive inventory or a root position for " .. digtron_id) + return + end + + local leftovers = {} + local dug_positions = {} + local cost = 0 + local dug_hashes = {} -- to ensure the same node isn't dug twice + local punches_thrown + if damage_hp ~= 0 then + punches_thrown = {} + end + + for _, digger_data in pairs(retrieve_all_digger_targets(digtron_id)) do + for _, target in ipairs(digger_data.targets) do + local target_pos = vector.add(root_pos, target) + local target_hash = minetest.hash_node_position(target_pos) + if not dug_hashes[target_hash] then + local target_node = minetest.get_node(target_pos) + local target_name = target_node.name + local targetdef = minetest.registered_nodes[target_name] + if + (target_pos[controlling_coordinate] + digger_data.offset) % digger_data.period == 0 and -- test periodicity and offset + minetest.get_item_group(target_name, "digtron") == 0 and + minetest.get_item_group(target_name, "digtron_protected") == 0 and + minetest.get_item_group(target_name, "immortal") == 0 and + ( + targetdef == nil or -- can dig undefined nodes, why not + targetdef.can_dig == nil or + targetdef.can_dig(target_pos, minetest.get_player_by_name(player_name)) + ) and + not protection_check(target_pos, player_name) + and (not digger_data.soft or is_soft_material(target_name)) + then + if punches_thrown then + table.insert(punches_thrown, {vector.add(root_pos, digger_data.pos), target_pos}) + end + if target_name ~= "air" then -- TODO: generalise this somehow for liquids and other undiggables + if digtron.config.uses_resources then + cost = cost + get_material_cost(target_name) + end + local drops = minetest.get_node_drops(target_name, "") + for _, drop in ipairs(drops) do + local leftover = predictive_inv:add_item("main", ItemStack(drop)) + if leftover:get_count() > 0 then + table.insert(leftovers, leftover) + end + end + table.insert(dug_positions, target_pos) + dug_hashes[target_hash] = true + end + end + end + end + end + + return leftovers, dug_positions, cost, punches_thrown +end + +-- Removes nodes and records node info so execute_dug_callbacks can be called later +local get_and_remove_nodes = function(nodes_to_dig) + local ret = {} + for _, pos in ipairs(nodes_to_dig) do + local record = {} + record.pos = pos + record.node = minetest.get_node(pos) + record.meta = minetest.get_meta(pos) + minetest.remove_node(pos) + table.insert(ret, record) + end + return ret +end + +local log_dug_nodes = function(nodes_to_dig, digtron_id, root_pos, player_name) + local nodes_dug_count = #nodes_to_dig + if nodes_dug_count > 0 then + local pluralized = "node" + if nodes_dug_count > 1 then + pluralized = "nodes" + end + minetest.log("action", nodes_dug_count .. " " .. pluralized .. " dug by " + .. digtron_id .. " near ".. minetest.pos_to_string(root_pos) + .. " operated by by " .. player_name) + end +end + +-- Execute all the callbacks that would normally be called on a node after it's been dug. +-- This is a separate step from actually removing the nodes because we don't want to execute +-- these until after *everything* has been dug - this can trigger sand falling, we don't +-- want that getting in the way of nodes yet to be built. +local execute_dug_callbacks = function(nodes_dug) + -- Execute various on-dig callbacks for the nodes that Digtron dug + for _, dug_data in ipairs(nodes_dug) do + local old_pos = dug_data.pos + local old_node = dug_data.node + local old_name = old_node.name + + for _, callback in ipairs(minetest.registered_on_dignodes) do + -- Copy pos and node because callback can modify them + local pos_copy = {x=old_pos.x, y=old_pos.y, z=old_pos.z} + local oldnode_copy = {name=old_name, param1=old_node.param1, param2=old_node.param2} + callback(pos_copy, oldnode_copy, digtron.fake_player) + end + + local old_def = minetest.registered_nodes[old_name] + if old_def ~= nil then + local old_after_dig = old_def.after_dig_node + if old_after_dig ~= nil then + old_after_dig(old_pos, old_node, dug_data.meta, digtron.fake_player) + end + end + end +end + +------------------------------------------------------------------------------------------------------ +-- Building + +-- need to provide root_pos because Digtron moves before building +local predict_build = function(digtron_id, root_pos, player_name, ignore_nodes, controlling_coordinate) + local predictive_inv = get_predictive_inventory(digtron_id) + if not predictive_inv then + minetest.log("error", "[Digtron] predict_build failed to retrieve " + .."a predictive inventory for " .. digtron_id) + return + end + + local ignore_hashes = {} + if ignore_nodes then + for _, ignore_pos in ipairs(ignore_nodes) do + ignore_hashes[minetest.hash_node_position(ignore_pos)] = true + end + end + + local missing_items = {} + local built_nodes = {} + local cost = 0 + + for _, builder_data in pairs(retrieve_all_builder_targets(digtron_id)) do + local dir = builder_data.dir + local periodicity_permitted = nil + for i = 1, builder_data.extrusion do + local target_pos = vector.add(vector.add(builder_data.pos, vector.multiply(dir, i)), root_pos) + local test_hash = minetest.hash_node_position(target_pos) + if periodicity_permitted == nil then + -- test periodicity and offset once + periodicity_permitted = (target_pos[controlling_coordinate] + builder_data.offset) % builder_data.period == 0 + if not periodicity_permitted then + break -- period/offset doesn't line up with the target + end + end + local target_node = minetest.get_node(target_pos) + local target_name = target_node.name + local targetdef = minetest.registered_nodes[target_name] + if + ignore_hashes[test_hash] or + (targetdef ~= nil + and targetdef.buildable_to + and not protection_check(target_pos, player_name) + ) + then + local item = builder_data.item + local facing = builder_data.facing + + local removed_item = predictive_inv:remove_item("main", ItemStack(item)) + if removed_item:get_count() < 1 then + missing_items[item] = (missing_items[item] or 0) + 1 + end + + if digtron.config.uses_resources then + cost = cost + digtron.config.build_cost + end + + table.insert(built_nodes, { + pos = target_pos, + node = {name=item, param2=facing }, + old_node = target_node, + }) + else + break -- extrusion reached an obstacle + end + end + end + return missing_items, built_nodes, cost +end + +-- Place all items listed in built_nodes in-world, returning any itemstacks that item_place_node returned. +-- Also returns the number of successes for logging purposes +local build_nodes = function(built_nodes) + local leftovers = {} + local success_count = 0 + for _, build_info in ipairs(built_nodes) do + local item_stack = ItemStack(build_info.node.name) + local buildpos = build_info.pos + local build_facing = build_info.node.param2 + local returned_stack, success = digtron.item_place_node(item_stack, digtron.fake_player, buildpos, build_facing) + if success then + success_count = success_count + 1 + end + if returned_stack:get_count() > 0 then + table.insert(leftovers, returned_stack) + end + end + return leftovers, success_count +end + +local log_built_nodes = function(success_count, digtron_id, root_pos, player_name) + if success_count > 0 then + local pluralized = "node" + if success_count > 1 then + pluralized = "nodes" + end + minetest.log("action", success_count .. " " .. pluralized .. " built by " + .. digtron_id .. " near ".. minetest.pos_to_string(root_pos) + .. " operated by by " .. player_name) + end +end + +-- Execute all the callbacks that would normally be called on a node after it's been built. +-- This is a separate step from actually placing the nodes because we don't want to execute +-- these until after *everything* has been built - this can trigger sand falling, we don't +-- want that getting in the way of nodes yet to be built. +local execute_built_callbacks = function(built_nodes) + for _, build_info in ipairs(built_nodes) do + local new_pos = build_info.pos + local new_node = build_info.node + local old_node = build_info.old_node + for _, callback in ipairs(minetest.registered_on_placenodes) do + -- Copy pos and node because callback can modify them + local pos_copy = {x=new_pos.x, y=new_pos.y, z=new_pos.z} + local oldnode_copy = {name=old_node.name, param1=old_node.param1, param2=old_node.param2} + local newnode_copy = {name=new_node.name, param1=new_node.param1, param2=new_node.param2} + callback(pos_copy, newnode_copy, digtron.fake_player, oldnode_copy) + end + + local new_def = minetest.registered_nodes[new_node.name] + if new_def ~= nil and new_def.after_place_node ~= nil then + new_def.after_place_node(new_pos, digtron.fake_player) + end + end +end + +------------------------------------------------------------------------------------------------------- +-- Execute cycle + +-- Used to determine which coordinate is being checked for periodicity. eg, if the digtron is moving in the z direction, then periodicity is checked for every n nodes in the z axis. +local get_controlling_coordinate = function(facedir) + -- used for determining builder period and offset + local dir = digtron.facedir_to_dir_map[facedir] + if dir == 1 or dir == 3 then + return "z" + elseif dir == 2 or dir == 4 then + return "x" + else + return "y" + end +end + +-- Attempts to insert the item list into the digtron inventory, and whatever doesn't fit +-- gets placed as an item at pos +local insert_or_eject = function(digtron_id, item_list, pos) + local predictive_inv = get_predictive_inventory(digtron_id) + if not predictive_inv then + minetest.log("error", "[Digtron] predict_build failed to retrieve " + .."a predictive inventory for " .. digtron_id) + return + end + for _, item in ipairs(item_list) do + local final_leftover = predictive_inv:add_item("main", item) + minetest.item_drop(final_leftover, digtron.fake_player, pos) + end +end + +-- TODO: the dig_down parameter is a bit hacky, see if I can come up with a better way to arrange this code +local execute_dig_move_build_cycle = function(digtron_id, player_name, dig_down) + local old_root_pos = retrieve_pos(digtron_id) + local root_node = minetest.get_node(old_root_pos) + local root_facedir = root_node.param2 + local controlling_coordinate = get_controlling_coordinate(root_facedir) + + local dig_leftovers, nodes_to_dig, dig_cost, punches_thrown = predict_dig(digtron_id, player_name, controlling_coordinate) + local new_root_pos + + if dig_down then + new_root_pos = vector.add(old_root_pos, digtron.facedir_to_down(root_facedir)) + else + new_root_pos = vector.add(old_root_pos, digtron.facedir_to_dir(root_facedir)) + end + + local layout = retrieve_layout(digtron_id) + local buildable_to, succeeded, failed = digtron.is_buildable_to(digtron_id, layout, new_root_pos, player_name, nodes_to_dig) + local missing_items, built_nodes, build_cost + + if dig_down then + missing_items = {} + built_nodes = {} + build_cost = 0 + else + missing_items, built_nodes, build_cost = predict_build(digtron_id, new_root_pos, player_name, nodes_to_dig, controlling_coordinate) + end + + if not buildable_to then + clear_predictive_inventory(digtron_id) + digtron.show_buildable_nodes({}, failed) + minetest.sound_play("digtron_squeal", {gain = 0.5, pos=old_root_pos}) + minetest.chat_send_player(player_name, S("@1 at @2 has encountered an obstacle.", + get_name(digtron_id), minetest.pos_to_string(old_root_pos))) + return false + elseif next(missing_items) ~= nil then + clear_predictive_inventory(digtron_id) + local items = {} + for item, count in ipairs(missing_items) do + local item_def = minetest.registered_items[item] + if item_def == nil then -- Shouldn't be possible, but don't crash if it does happen somehow + table.insert(items, count .. " " .. item) + else + table.insert(items, count .. " " .. item_def.description) + end + end + minetest.chat_send_player(player_name, S("@1 at @2 requires @3 to execute its next build cycle.", + get_name(digtron_id), minetest.pos_to_string(old_root_pos), table.concat(items, ", "))) + minetest.sound_play("digtron_dingding", {gain = 0.5, pos=old_root_pos}) + return false + else + digtron.fake_player:update(old_root_pos, player_name) + + -- Removing old nodes + local removed = digtron.remove_from_world(digtron_id, player_name) + if removed then + local nodes_dug = get_and_remove_nodes(nodes_to_dig, player_name) + log_dug_nodes(nodes_to_dig, digtron_id, old_root_pos, player_name) + + local items_dropped = {} + if punches_thrown then + for _, punch_data in ipairs(punches_thrown) do + damage_creatures(punch_data, items_dropped) + end + end + + -- Building new Digtron + build_to_world(digtron_id, layout, new_root_pos, player_name) + minetest.sound_play("digtron_construction", {gain = 0.5, pos=new_root_pos}) + + local build_leftovers, success_count = build_nodes(built_nodes, player_name) + log_built_nodes(success_count, digtron_id, old_root_pos, player_name) + + -- Don't need to do fancy callback checking for digtron nodes since I made all those + -- nodes and I know they don't have anything that needs to be done for them. + -- Just check for falling nodes. + for _, removed_pos in ipairs(removed) do + minetest.check_for_falling(removed_pos) + end + + -- Must be called after digtron.build_to_world because it triggers falling nodes + execute_dug_callbacks(nodes_dug) + execute_built_callbacks(built_nodes) + + -- try putting dig_leftovers and build_leftovers into the inventory one last time before ejecting it + insert_or_eject(digtron_id, dig_leftovers, old_root_pos) + insert_or_eject(digtron_id, build_leftovers, old_root_pos) + insert_or_eject(digtron_id, items_dropped, old_root_pos) + + commit_predictive_inventory(digtron_id) + end + return true + end +end + +--------------------------------------------------------------------------------- +-- Node callbacks + +-- If the digtron node has an assigned ID and a layout for that ID exists and +-- a matching node exists in the layout then don't let it be dug. +local can_dig = function(pos, digger) + if digger then + local player_name = digger:get_player_name() + if protection_check(pos, player_name) then + return false + end + end + + local meta = minetest.get_meta(pos) + local digtron_id = meta:get_string("digtron_id") + if digtron_id == "" then + return true + end + + local node = minetest.get_node(pos) + + local root_pos = retrieve_pos(digtron_id) + local layout = retrieve_layout(digtron_id) + if root_pos == nil or layout == nil then + -- Somehow, this belongs to a digtron id that's missing information that should exist in persistence. + local missing = "" + if root_pos == nil then missing = missing .. "root_pos " end + if layout == nil then missing = missing .. "layout " end + + minetest.log("error", "[Digtron] can_dig was called on a " .. node.name .. " at location " + .. minetest.pos_to_string(pos) .. " that claimed to belong to " .. digtron_id + .. ". However, layout and/or location data are missing: " .. missing) + -- TODO May be better to do this to prevent node duplication. But we're already in bug land here so tread gently. + --minetest.remove_node(pos) + --return false + return true + end + + local layout_hash = minetest.hash_node_position(vector.subtract(pos, root_pos)) + local layout_data = layout[layout_hash] + if layout_data == nil or layout_data.node == nil then + minetest.log("error", "[Digtron] can_dig was called on a " .. node.name .. " at location " + .. minetest.pos_to_string(pos) .. " that claimed to belong to " .. digtron_id + .. ". However, the layout for that digtron_id didn't contain any corresponding node at its location.") + return true + end + if layout_data.node.name ~= node.name or layout_data.node.param2 ~= node.param2 then + minetest.log("error", "[Digtron] can_dig was called on a " .. node.name .. " with param2 " + .. node.param2 .." at location " .. minetest.pos_to_string(pos) .. " that belonged to " .. digtron_id + .. ". However, the layout for that digtron_id contained a " .. layout_data.node.name + .. "with param2 ".. layout_data.node.param2 .. " at its location.") + return true + end + + -- We're part of a valid Digtron. No touchy. + return false +end + +-- put this on all Digtron nodes. If other inventory types are added (eg, batteries) +-- update this. +local on_blast = function(pos, intensity) + if intensity < 1.0 then return end -- The Almighty Digtron ignores weak-ass explosions + + local meta = minetest.get_meta(pos) + local digtron_id = meta:get_string("digtron_id") + if digtron_id ~= "" then + if not digtron.disassemble(digtron_id, "an explosion") then + minetest.log("error", "[Digtron] a digtron node at " .. minetest.pos_to_string(pos) + .. " was hit by an explosion and had digtron_id " .. digtron_id + .. " but didn't have a root position recorded, so it could not be disassembled.") + return + end + end + + local drops = {} + default.get_inventory_drops(pos, "main", drops) + default.get_inventory_drops(pos, "fuel", drops) + local node = minetest.get_node(pos) + table.insert(drops, ItemStack(node.name)) + minetest.remove_node(pos) + return drops +end + +-- Use this inside other on_rightclicks for configuring Digtron nodes, this +-- overrides if you're right-clicking with another Digtron node and assumes +-- that you're trying to build it. +local on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + local item_def = itemstack:get_definition() + if item_def.type == "node" and minetest.get_item_group(itemstack:get_name(), "digtron") > 0 then + local returnstack, success = minetest.item_place_node(itemstack, clicker, pointed_thing) + if success and item_def.sounds and item_def.sounds.place and item_def.sounds.place.name then + minetest.sound_play(item_def.sounds.place, {pos = pos}) + end + return returnstack, success + end +end + +------------------------------------------------------------------------------------ +-- Creative trash + +-- Catch when someone throws a Digtron controller with an ID into the trash, dispose +-- of the persisted layout. +if minetest.get_modpath("creative") then + local trash = minetest.detached_inventories["creative_trash"] + if trash then + local old_on_put = trash.on_put + if old_on_put then + local digtron_on_put = function(inv, listname, index, stack, player) + local stack = inv:get_stack(listname, index) + local stack_meta = stack:get_meta() + local digtron_id = stack_meta:get_string("digtron_id") + if stack:get_name() == "digtron:controller" and digtron_id ~= "" then + minetest.log("action", player:get_player_name() .. " disposed of " .. digtron_id + .. " in the creative inventory's trash receptacle.") + dispose_id(digtron_id) + end + return old_on_put(inv, listname, index, stack, player) + end + trash.on_put = digtron_on_put + end + end +end + +-------------------------------------------------------------------------------------- +-- Fallback method for recovering missing metadata +-- If this gets called frequently then something's wrong. + +local recover_digtron_id = function(root_pos) + for field, value in pairs(mod_meta:to_table().fields) do + local fields = field:split(":") + if #fields == 2 and fields[2] == "pos" and vector.equals(root_pos, minetest.deserialize(value)) then + local digtron_id = fields[1] + minetest.log("warning", "[Digtron] had to use recover_digtron_id to restore " + ..digtron_id .. " to the controller at " .. minetest.pos_to_string(root_pos) + ..". If this happens frequently please file an issue with Digtron's developers. " + .."recover_digtron_id will now attempt to restore the digtron_id metadata key to all " + .."nodes in this Digtron's layout.") + local layout = retrieve_layout(digtron_id) + local root_pos = retrieve_pos(digtron_id) + for layout_node_id, data in pairs(layout) do + -- get_valid_data will attempt to repair node metadata that's missing digtron_id + local node_pos, node, node_meta = get_valid_data(digtron_id, layout_node_id, root_pos, data, "recover_digtron_id") + end + return true + end + end + return false +end + +--------------------------------------------------------------------------------------------------------------------------- +-- External API + +-- node definition methods +digtron.can_dig = can_dig +digtron.on_blast = on_blast +digtron.on_rightclick = on_rightclick + +digtron.get_name = get_name +digtron.set_name = set_name +digtron.get_pos = retrieve_pos +digtron.get_bounding_box = retrieve_bounding_box + +-- used by formspecs +digtron.get_inventory = retrieve_inventory +digtron.set_sequence = persist_sequence +digtron.get_sequence = retrieve_sequence + +digtron.get_layout = retrieve_layout +digtron.set_layout = function(digtron_id, layout) + invalidate_layout_cache(digtron_id) + persist_layout(digtron_id, layout) +end +digtron.duplicate = duplicate + +digtron.assemble = assemble +digtron.disassemble = disassemble +digtron.remove_from_world = remove_from_world +digtron.is_buildable_to = is_buildable_to +digtron.build_to_world = build_to_world +digtron.move = move +digtron.rotate = rotate +digtron.execute_dig_move_build_cycle = execute_dig_move_build_cycle + +digtron.recover_digtron_id = recover_digtron_id \ No newline at end of file diff --git a/geometry.lua b/geometry.lua new file mode 100644 index 0000000..e441eb5 --- /dev/null +++ b/geometry.lua @@ -0,0 +1,164 @@ +local cardinal_dirs = { + {x = 0, y = 0, z = 1}, + {x = 1, y = 0, z = 0}, + {x = 0, y = 0, z = -1}, + {x = -1, y = 0, z = 0}, + {x = 0, y = -1, z = 0}, + {x = 0, y = 1, z = 0}, +} +-- Turn the cardinal directions into a set of integers you can add to a hash to step in that direction. +local cardinal_dirs_hash = {} +local origin_hash = minetest.hash_node_position({x = 0, y = 0, z = 0}) +for i, dir in ipairs(cardinal_dirs) do + cardinal_dirs_hash[i] = minetest.hash_node_position(dir) - origin_hash +end + +-- Mapping from facedir value to index in cardinal_dirs. +local facedir_to_dir_map = { + [0] = 1, 2, 3, 4, + 5, 2, 6, 4, + 6, 2, 5, 4, + 1, 5, 3, 6, + 1, 6, 3, 5, + 1, 4, 3, 2, +} + +local facedir_to_up_map = { + [0] = 6, 6, 6, 6, + 3, 3, 3, 3, + 1, 1, 1, 1, + 4, 4, 4, 4, + 2, 2, 2, 2, + 5, 5, 5, 5, +} + +local facedir_to_down_map = { + [0] = 5, 5, 5, 5, + 1, 1, 1, 1, + 3, 3, 3, 3, + 2, 2, 2, 2, + 4, 4, 4, 4, + 6, 6, 6, 6, +} + +local facedir_to_right_map = { + [0] = 2, 3, 4, 1, + 2, 6, 4, 6, + 2, 5, 4, 6, + 5, 3, 6, 1, + 6, 3, 5, 1, + 4, 3, 2, 1, +} + +local facedir_to_dir = function(facedir) + return cardinal_dirs[facedir_to_dir_map[facedir % 32]] +end +local facedir_to_dir_hash = function(facedir) + return cardinal_dirs_hash[facedir_to_dir_map[facedir % 32]] +end +local facedir_to_up = function(facedir) + return cardinal_dirs[facedir_to_up_map[facedir % 32]] +end +local facedir_to_up_hash = function(facedir) + return cardinal_dirs_hash[facedir_to_up_map[facedir % 32]] +end +local facedir_to_down = function(facedir) + return cardinal_dirs[facedir_to_down_map[facedir % 32]] +end +local facedir_to_down_hash = function(facedir) + return cardinal_dirs_hash[facedir_to_down_map[facedir % 32]] +end +local facedir_to_right = function(facedir) + return cardinal_dirs[facedir_to_right_map[facedir % 32]] +end +local facedir_to_right_hash = function(facedir) + return cardinal_dirs_hash[facedir_to_right_map[facedir % 32]] +end + +-- Rotation + +local negative_x = minetest.hash_node_position({x = -1, y = 0, z = 0}) +local positive_x = minetest.hash_node_position({x = 1, y = 0, z = 0}) +local negative_y = minetest.hash_node_position({x = 0, y = -1, z = 0}) +local positive_y = minetest.hash_node_position({x = 0, y = 1, z = 0}) +local negative_z = minetest.hash_node_position({x = 0, y = 0, z = -1}) +local positive_z = minetest.hash_node_position({x = 0, y = 0, z = 1}) + +local facedir_rot = { + [negative_x] = {[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 + [positive_x] = {[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 + [negative_y] = {[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 + [positive_y] = {[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 + [negative_z] = {[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 + [positive_z] = {[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_rot = { + [negative_x] = {[0] = 4, 5, 2, 3, 1, 0}, -- 270 degrees + [positive_x] = {[0] = 5, 4, 2, 3, 0, 1}, -- 90 degrees + [negative_y] = {[0] = 0, 1, 4, 5, 3, 2}, -- 270 degrees + [positive_y] = {[0] = 0, 1, 5, 4, 2, 3}, -- 90 degrees + [negative_z] = {[0] = 3, 2, 0, 1, 4, 5}, -- 270 degrees + [positive_z] = {[0] = 2, 3, 1, 0, 4, 5}, -- 90 degrees +} + +local rotate_facedir = function(axis_hash, facedir) + return facedir_rot[axis_hash][facedir] +end +local rotate_wallmounted = function(axis_hash, facedir) + return wallmounted_rot[axis_hash][facedir] +end + +--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) +-- operates directly on the pos vector +-- Rotates it around origin +local rotate_pos = function(axis_hash, pos) + if axis_hash == negative_x and not (pos.y == 0 and pos.z == 0) then + local temp_z = pos.z + pos.z = pos.y + pos.y = -temp_z + elseif axis_hash == positive_x and not (pos.y == 0 and pos.z == 0) then + local temp_z = pos.z + pos.z = -pos.y + pos.y = temp_z + elseif axis_hash == negative_y and not (pos.x == 0 and pos.z == 0) then + local temp_x = pos.x + pos.x = -pos.z + pos.z = temp_x + elseif axis_hash == positive_y and not (pos.x == 0 and pos.z == 0) then + local temp_x = pos.x + pos.x = pos.z + pos.z = -temp_x + elseif axis_hash == negative_z and not (pos.x == 0 and pos.y == 0) then + local temp_x = pos.x + pos.x = -pos.y + pos.y = temp_x + elseif axis_hash == positive_z and not (pos.x == 0 and pos.y == 0) then + local temp_x = pos.x + pos.x = pos.y + pos.y = -temp_x + end + return pos +end + +digtron.cardinal_dirs = cardinal_dirs -- used by builder entities as well +digtron.cardinal_dirs_hash = cardinal_dirs_hash +digtron.facedir_to_dir_map = facedir_to_dir_map -- used by get_controlling_coordinate + +digtron.facedir_to_dir = facedir_to_dir +digtron.facedir_to_dir_hash = facedir_to_dir_hash +digtron.facedir_to_up = facedir_to_up +digtron.facedir_to_up_hash = facedir_to_up_hash +digtron.facedir_to_down = facedir_to_down +digtron.facedir_to_down_hash = facedir_to_down_hash +digtron.facedir_to_right = facedir_to_right +digtron.facedir_to_right_hash = facedir_to_right_hash + +digtron.rotate_pos = rotate_pos +digtron.rotate_wallmounted = rotate_wallmounted +digtron.rotate_facedir = rotate_facedir \ No newline at end of file diff --git a/i18n.py b/i18n.py new file mode 100644 index 0000000..d33bbb0 --- /dev/null +++ b/i18n.py @@ -0,0 +1,418 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Script to generate the template file and update the translation files. +# Copy the script into the mod or modpack root folder and run it there. +# +# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer +# LGPLv2.1+ + +from __future__ import print_function +import os, fnmatch, re, shutil, errno +from sys import argv as _argv + +# Running params +params = {"recursive": False, + "help": False, + "mods": False, + "verbose": False, + "folders": [] +} +# Available CLI options +options = {"recursive": ['--recursive', '-r'], + "help": ['--help', '-h'], + "mods": ['--installed-mods'], + "verbose": ['--verbose', '-v'] +} + +# Strings longer than this will have extra space added between +# them in the translation files to make it easier to distinguish their +# beginnings and endings at a glance +doublespace_threshold = 60 + +def set_params_folders(tab: list): + '''Initialize params["folders"] from CLI arguments.''' + # Discarding argument 0 (tool name) + for param in tab[1:]: + stop_param = False + for option in options: + if param in options[option]: + stop_param = True + break + if not stop_param: + params["folders"].append(os.path.abspath(param)) + +def set_params(tab: list): + '''Initialize params from CLI arguments.''' + for option in options: + for option_name in options[option]: + if option_name in tab: + params[option] = True + break + +def print_help(name): + '''Prints some help message.''' + print(f'''SYNOPSIS + {name} [OPTIONS] [PATHS...] +DESCRIPTION + {', '.join(options["help"])} + prints this help message + {', '.join(options["recursive"])} + run on all subfolders of paths given + {', '.join(options["mods"])} + run on locally installed modules + {', '.join(options["verbose"])} + add output information +''') + + +def main(): + '''Main function''' + set_params(_argv) + set_params_folders(_argv) + if params["help"]: + print_help(_argv[0]) + elif params["recursive"] and params["mods"]: + print("Option --installed-mods is incompatible with --recursive") + else: + # Add recursivity message + print("Running ", end='') + if params["recursive"]: + print("recursively ", end='') + # Running + if params["mods"]: + print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}") + run_all_subfolders("~/.minetest/mods") + elif len(params["folders"]) >= 2: + print("on folder list:", params["folders"]) + for f in params["folders"]: + if params["recursive"]: + run_all_subfolders(f) + else: + update_folder(f) + elif len(params["folders"]) == 1: + print("on folder", params["folders"][0]) + if params["recursive"]: + run_all_subfolders(params["folders"][0]) + else: + update_folder(params["folders"][0]) + else: + print("on folder", os.path.abspath("./")) + if params["recursive"]: + run_all_subfolders(os.path.abspath("./")) + else: + update_folder(os.path.abspath("./")) + +#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ') +#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote +pattern_lua = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL) +pattern_lua_bracketed = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL) + +# Handles "concatenation" .. " of strings" +pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL) + +pattern_tr = re.compile(r'(.+?[^@])=(.*)') +pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)') +pattern_tr_filename = re.compile(r'\.tr$') +pattern_po_language_code = re.compile(r'(.*)\.po$') + +#attempt to read the mod's name from the mod.conf file. Returns None on failure +def get_modname(folder): + try: + with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf: + for line in mod_conf: + match = pattern_name.match(line) + if match: + return match.group(1) + except FileNotFoundError: + pass + return None + +#If there are already .tr files in /locale, returns a list of their names +def get_existing_tr_files(folder): + out = [] + for root, dirs, files in os.walk(os.path.join(folder, 'locale/')): + for name in files: + if pattern_tr_filename.search(name): + out.append(name) + return out + +# A series of search and replaces that massage a .po file's contents into +# a .tr file's equivalent +def process_po_file(text): + # The first three items are for unused matches + text = re.sub(r'#~ msgid "', "", text) + text = re.sub(r'"\n#~ msgstr ""\n"', "=", text) + text = re.sub(r'"\n#~ msgstr "', "=", text) + # comment lines + text = re.sub(r'#.*\n', "", text) + # converting msg pairs into "=" pairs + text = re.sub(r'msgid "', "", text) + text = re.sub(r'"\nmsgstr ""\n"', "=", text) + text = re.sub(r'"\nmsgstr "', "=", text) + # various line breaks and escape codes + text = re.sub(r'"\n"', "", text) + text = re.sub(r'"\n', "\n", text) + text = re.sub(r'\\"', '"', text) + text = re.sub(r'\\n', '@n', text) + # remove header text + text = re.sub(r'=Project-Id-Version:.*\n', "", text) + # remove double-spaced lines + text = re.sub(r'\n\n', '\n', text) + return text + +# Go through existing .po files and, if a .tr file for that language +# *doesn't* exist, convert it and create it. +# The .tr file that results will subsequently be reprocessed so +# any "no longer used" strings will be preserved. +# Note that "fuzzy" tags will be lost in this process. +def process_po_files(folder, modname): + for root, dirs, files in os.walk(os.path.join(folder, 'locale/')): + for name in files: + code_match = pattern_po_language_code.match(name) + if code_match == None: + continue + language_code = code_match.group(1) + tr_name = modname + "." + language_code + ".tr" + tr_file = os.path.join(root, tr_name) + if os.path.exists(tr_file): + if params["verbose"]: + print(f"{tr_name} already exists, ignoring {name}") + continue + fname = os.path.join(root, name) + with open(fname, "r", encoding='utf-8') as po_file: + if params["verbose"]: + print(f"Importing translations from {name}") + text = process_po_file(po_file.read()) + with open(tr_file, "wt", encoding='utf-8') as tr_out: + tr_out.write(text) + +# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612 +# Creates a directory if it doesn't exist, silently does +# nothing if it already exists +def mkdir_p(path): + try: + os.makedirs(path) + except OSError as exc: # Python >2.5 + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: raise + +# Converts the template dictionary to a text to be written as a file +# dKeyStrings is a dictionary of localized string to source file sets +# dOld is a dictionary of existing translations and comments from +# the previous version of this text +def strings_to_text(dkeyStrings, dOld, mod_name): + lOut = [f"# textdomain: {mod_name}\n"] + + dGroupedBySource = {} + + for key in dkeyStrings: + sourceList = list(dkeyStrings[key]) + sourceList.sort() + sourceString = "\n".join(sourceList) + listForSource = dGroupedBySource.get(sourceString, []) + listForSource.append(key) + dGroupedBySource[sourceString] = listForSource + + lSourceKeys = list(dGroupedBySource.keys()) + lSourceKeys.sort() + for source in lSourceKeys: + localizedStrings = dGroupedBySource[source] + localizedStrings.sort() + lOut.append("") + lOut.append(source) + lOut.append("") + for localizedString in localizedStrings: + val = dOld.get(localizedString, {}) + translation = val.get("translation", "") + comment = val.get("comment") + if len(localizedString) > doublespace_threshold and not lOut[-1] == "": + lOut.append("") + if comment != None: + lOut.append(comment) + lOut.append(f"{localizedString}={translation}") + if len(localizedString) > doublespace_threshold: + lOut.append("") + + + unusedExist = False + for key in dOld: + if key not in dkeyStrings: + val = dOld[key] + translation = val.get("translation") + comment = val.get("comment") + # only keep an unused translation if there was translated + # text or a comment associated with it + if translation != None and (translation != "" or comment): + if not unusedExist: + unusedExist = True + lOut.append("\n\n##### not used anymore #####\n") + if len(key) > doublespace_threshold and not lOut[-1] == "": + lOut.append("") + if comment != None: + lOut.append(comment) + lOut.append(f"{key}={translation}") + if len(key) > doublespace_threshold: + lOut.append("") + return "\n".join(lOut) + '\n' + +# Writes a template.txt file +# dkeyStrings is the dictionary returned by generate_template +def write_template(templ_file, dkeyStrings, mod_name): + # read existing template file to preserve comments + existing_template = import_tr_file(templ_file) + + text = strings_to_text(dkeyStrings, existing_template[0], mod_name) + mkdir_p(os.path.dirname(templ_file)) + with open(templ_file, "wt", encoding='utf-8') as template_file: + template_file.write(text) + + +# Gets all translatable strings from a lua file +def read_lua_file_strings(lua_file): + lOut = [] + with open(lua_file, encoding='utf-8') as text_file: + text = text_file.read() + #TODO remove comments here + + text = re.sub(pattern_concat, "", text) + + strings = [] + for s in pattern_lua.findall(text): + strings.append(s[1]) + for s in pattern_lua_bracketed.findall(text): + strings.append(s) + + for s in strings: + s = re.sub(r'"\.\.\s+"', "", s) + s = re.sub("@[^@=0-9]", "@@", s) + s = s.replace('\\"', '"') + s = s.replace("\\'", "'") + s = s.replace("\n", "@n") + s = s.replace("\\n", "@n") + s = s.replace("=", "@=") + lOut.append(s) + return lOut + +# Gets strings from an existing translation file +# returns both a dictionary of translations +# and the full original source text so that the new text +# can be compared to it for changes. +def import_tr_file(tr_file): + dOut = {} + text = None + if os.path.exists(tr_file): + with open(tr_file, "r", encoding='utf-8') as existing_file : + # save the full text to allow for comparison + # of the old version with the new output + text = existing_file.read() + existing_file.seek(0) + # a running record of the current comment block + # we're inside, to allow preceeding multi-line comments + # to be retained for a translation line + latest_comment_block = None + for line in existing_file.readlines(): + line = line.rstrip('\n') + if line[:3] == "###": + # Reset comment block if we hit a header + latest_comment_block = None + continue + if line[:1] == "#": + # Save the comment we're inside + if not latest_comment_block: + latest_comment_block = line + else: + latest_comment_block = latest_comment_block + "\n" + line + continue + match = pattern_tr.match(line) + if match: + # this line is a translated line + outval = {} + outval["translation"] = match.group(2) + if latest_comment_block: + # if there was a comment, record that. + outval["comment"] = latest_comment_block + latest_comment_block = None + dOut[match.group(1)] = outval + return (dOut, text) + +# Walks all lua files in the mod folder, collects translatable strings, +# and writes it to a template.txt file +# Returns a dictionary of localized strings to source file sets +# that can be used with the strings_to_text function. +def generate_template(folder, mod_name): + dOut = {} + for root, dirs, files in os.walk(folder): + for name in files: + if fnmatch.fnmatch(name, "*.lua"): + fname = os.path.join(root, name) + found = read_lua_file_strings(fname) + if params["verbose"]: + print(f"{fname}: {str(len(found))} translatable strings") + + for s in found: + sources = dOut.get(s, set()) + sources.add(f"### {os.path.basename(fname)} ###") + dOut[s] = sources + + if len(dOut) == 0: + return None + templ_file = os.path.join(folder, "locale/template.txt") + write_template(templ_file, dOut, mod_name) + return dOut + +# Updates an existing .tr file, copying the old one to a ".old" file +# if any changes have happened +# dNew is the data used to generate the template, it has all the +# currently-existing localized strings +def update_tr_file(dNew, mod_name, tr_file): + if params["verbose"]: + print(f"updating {tr_file}") + + tr_import = import_tr_file(tr_file) + dOld = tr_import[0] + textOld = tr_import[1] + + textNew = strings_to_text(dNew, dOld, mod_name) + + if textOld and textOld != textNew: + print(f"{tr_file} has changed.") + shutil.copyfile(tr_file, f"{tr_file}.old") + + with open(tr_file, "w", encoding='utf-8') as new_tr_file: + new_tr_file.write(textNew) + +# Updates translation files for the mod in the given folder +def update_mod(folder): + modname = get_modname(folder) + if modname is not None: + process_po_files(folder, modname) + print(f"Updating translations for {modname}") + data = generate_template(folder, modname) + if data == None: + print(f"No translatable strings found in {modname}") + else: + for tr_file in get_existing_tr_files(folder): + update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file)) + else: + print("Unable to find modname in folder " + folder) + +# Determines if the folder being pointed to is a mod or a mod pack +# and then runs update_mod accordingly +def update_folder(folder): + is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf")) + if is_modpack: + subfolders = [f.path for f in os.scandir(folder) if f.is_dir()] + for subfolder in subfolders: + update_mod(subfolder + "/") + else: + update_mod(folder) + print("Done.") + +def run_all_subfolders(folder): + for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]: + update_folder(modfolder + "/") + + +main() diff --git a/init.lua b/init.lua index 82de725..2d7f8a3 100644 --- a/init.lua +++ b/init.lua @@ -1,8 +1,8 @@ digtron = {} +digtron.doc = {} -- TODO: move to doc file -digtron.auto_controller_colorize = "#88000030" -digtron.pusher_controller_colorize = "#00880030" -digtron.soft_digger_colorize = "#88880030" +local modname = minetest.get_current_modname() +digtron.S = minetest.get_translator(modname) -- A global dictionary is used here so that other substitutions can be added easily by other mods, if necessary digtron.builder_read_item_substitutions = { @@ -23,94 +23,50 @@ digtron.builder_read_item_substitutions = { -- Sometimes we want builder heads to call an item's "on_place" method, other times we -- don't want them to. There's no way to tell which situation is best programmatically -- so we have to rely on whitelists to be on the safe side. - --first exact matches are tested, and the value given in this global table is returned digtron.builder_on_place_items = { ["default:torch"] = true, } - -- Then a string prefix is checked, returning this value. Useful for enabling on_placed on a mod-wide basis. digtron.builder_on_place_prefixes = { ["farming:"] = true, ["farming_plus:"] = true, ["crops:"] = true, } - -- Finally, items belonging to group "digtron_on_place" will have their on_place methods called. -local digtron_modpath = minetest.get_modpath( "digtron" ) -dofile( digtron_modpath .. "/class_fakeplayer.lua") -digtron.fake_player = DigtronFakePlayer.create({x=0,y=0,z=0}, "fake_player") -- since we only need one fake player at a time and it doesn't retain useful state, create a global one and just update it as needed. +digtron.mod_meta = minetest.get_mod_storage() -dofile( digtron_modpath .. "/config.lua" ) -dofile( digtron_modpath .. "/util.lua" ) -dofile( digtron_modpath .. "/doc.lua" ) -dofile( digtron_modpath .. "/awards.lua" ) -dofile( digtron_modpath .. "/class_pointset.lua" ) -dofile( digtron_modpath .. "/class_layout.lua" ) -dofile( digtron_modpath .. "/entities.lua" ) -dofile( digtron_modpath .. "/nodes/node_misc.lua" ) -- contains structure and light nodes -dofile( digtron_modpath .. "/nodes/node_storage.lua" ) -- contains inventory and fuel storage nodes -dofile( digtron_modpath .. "/nodes/node_diggers.lua" ) -- contains all diggers -dofile( digtron_modpath .. "/nodes/node_builders.lua" ) -- contains all builders (there's just one currently) -dofile( digtron_modpath .. "/nodes/node_controllers.lua" ) -- controllers -dofile( digtron_modpath .. "/nodes/node_axle.lua" ) -- Rotation controller -dofile( digtron_modpath .. "/nodes/node_crate.lua" ) -- Digtron portability support -dofile( digtron_modpath .. "/nodes/node_item_ejector.lua" ) -- ejects non-building, non-fuel items from inventories -dofile( digtron_modpath .. "/nodes/node_duplicator.lua" ) -- constructs copies of existing Digtrons +local modpath = minetest.get_modpath(modname) +dofile(modpath.."/config.lua") ---Technic -dofile( digtron_modpath .. "/nodes/node_battery_holder.lua" ) -- holds rechargeable batteries from the technic mod -dofile( digtron_modpath .. "/nodes/node_power_connector.lua") - -dofile( digtron_modpath .. "/nodes/recipes.lua" ) +dofile(modpath.."/class_fakeplayer.lua") +digtron.fake_player = DigtronFakePlayer.create({x=0,y=0,z=0}, "fake_player") -- since we only need one fake player at a time and it doesn't retain useful state, create a global one and just update it as needed. +dofile(modpath.."/util_item_place_node.lua") -dofile( digtron_modpath .. "/upgrades.lua" ) -- various LBMs for upgrading older versions of Digtron. +dofile(modpath.."/geometry.lua") +dofile(modpath.."/entities.lua") +dofile(modpath.."/functions.lua") +dofile(modpath.."/controller.lua") +dofile(modpath.."/nodes/node_misc.lua") +dofile(modpath.."/nodes/node_storage.lua") +dofile(modpath.."/nodes/node_digger.lua") +dofile(modpath.."/nodes/node_builder.lua") +dofile(modpath.."/nodes/node_duplicator.lua") +dofile(modpath.."/nodes/recipes.lua") -- digtron group numbers: -- 1 - generic digtron node, nothing special is done with these. They're just dragged along. -- 2 - inventory-holding digtron, has a "main" inventory that the digtron can add to and take from. --- 3 - digger head, has an "execute_dig" method in its definition --- 4 - builder head, has a "test_build" and "execute_build" method in its definition +-- 4 - builder head -- 5 - fuel-holding digtron, has a "fuel" invetory that the control node can draw fuel items from. Separate from general inventory, nothing gets put here automatically. -- 6 - holds both fuel and main inventories -- 7 - holds batteries (RE Battery from technic) to provide clean renewable power -- 8 - connects to adjacent HV technic cable --- 9 - connects to pipeworks, auto-ejects mined items - --- This code was added for use with FaceDeer's fork of the [catacomb] mod. Paramat's version doesn't support customized protected nodes, which causes --- it to "eat" Digtrons sometimes. -if minetest.get_modpath("catacomb") and catacomb ~= nil and catacomb.chamber_protected_nodes ~= nil and catacomb.passage_protected_nodes ~= nil then - local digtron_nodes = { - minetest.get_content_id("digtron:inventory"), - minetest.get_content_id("digtron:fuelstore"), - minetest.get_content_id("digtron:battery_holder"), - minetest.get_content_id("digtron:combined_storage"), - minetest.get_content_id("digtron:axle"), - minetest.get_content_id("digtron:builder"), - minetest.get_content_id("digtron:controller"), - minetest.get_content_id("digtron:auto_controller"), - minetest.get_content_id("digtron:pusher"), - minetest.get_content_id("digtron:loaded_crate"), - minetest.get_content_id("digtron:digger"), - minetest.get_content_id("digtron:intermittent_digger"), - minetest.get_content_id("digtron:soft_digger"), - minetest.get_content_id("digtron:intermittent_soft_digger"), - minetest.get_content_id("digtron:dual_digger"), - minetest.get_content_id("digtron:dual_soft_digger"), - minetest.get_content_id("digtron:structure"), - minetest.get_content_id("digtron:light"), - minetest.get_content_id("digtron:panel"), - minetest.get_content_id("digtron:edge_panel"), - minetest.get_content_id("digtron:corner_panel"), - minetest.get_content_id("digtron:battery_holder"), - minetest.get_content_id("digtron:inventory_ejector"), - minetest.get_content_id("digtron:power_connector"), - } - for _, node_id in pairs(digtron_nodes) do - catacomb.chamber_protected_nodes[node_id] = true - catacomb.passage_protected_nodes[node_id] = true - end -end \ No newline at end of file +-- 9 - connects to pipeworks +-- 10 digger +-- 11 dual-headed digger +-- 12 soft digger +-- 13 dual-headed soft digger \ No newline at end of file diff --git a/intllib.lua b/intllib.lua deleted file mode 100644 index 6669d72..0000000 --- a/intllib.lua +++ /dev/null @@ -1,45 +0,0 @@ - --- Fallback functions for when `intllib` is not installed. --- Code released under Unlicense . - --- Get the latest version of this file at: --- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua - -local function format(str, ...) - local args = { ... } - local function repl(escape, open, num, close) - if escape == "" then - local replacement = tostring(args[tonumber(num)]) - if open == "" then - replacement = replacement..close - end - return replacement - else - return "@"..open..num..close - end - end - return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl)) -end - -local gettext, ngettext -if minetest.get_modpath("intllib") then - if intllib.make_gettext_pair then - -- New method using gettext. - gettext, ngettext = intllib.make_gettext_pair() - else - -- Old method using text files. - gettext = intllib.Getter() - end -end - --- Fill in missing functions. - -gettext = gettext or function(msgid, ...) - return format(msgid, ...) -end - -ngettext = ngettext or function(msgid, msgid_plural, n, ...) - return format(n==1 and msgid or msgid_plural, ...) -end - -return gettext, ngettext diff --git a/inventories.lua b/inventories.lua new file mode 100644 index 0000000..7704620 --- /dev/null +++ b/inventories.lua @@ -0,0 +1,168 @@ +local mod_meta = digtron.mod_meta + +-- indexed by digtron_id, set to true whenever the detached inventory's contents change +local dirty_inventories = {} + +local detached_inventory_callbacks = { + -- Called when a player wants to move items inside the inventory. + -- Return value: number of items allowed to move. + allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) + --allow anything in "main" + if to_list == "main" then + return count + end + + --only allow fuel items in "fuel" + if to_list == "fuel" then + local stack = inv:get_stack(from_list, from_index) + if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then + return count + end + end + return 0 + end, + + -- Called when a player wants to put something into the inventory. + -- Return value: number of items allowed to put. + -- Return value -1: Allow and don't modify item count in inventory. + allow_put = function(inv, listname, index, stack, player) + -- Only allow fuel items to be placed in fuel + if listname == "fuel" then + if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then + return stack:get_count() + else + return 0 + end + end + return stack:get_count() -- otherwise, allow all drops + end, + + -- Called when a player wants to take something out of the inventory. + -- Return value: number of items allowed to take. + -- Return value -1: Allow and don't modify item count in inventory. + allow_take = function(inv, listname, index, stack, player) + return stack:get_count() + end, + + -- Called after the actual action has happened, according to what was + -- allowed. + -- No return value. + on_move = function(inv, from_list, from_index, to_list, to_index, count, player) + dirty_inventories[inv:get_location().name] = true + end, + on_put = function(inv, listname, index, stack, player) + dirty_inventories[inv:get_location().name] = true + end, + on_take = function(inv, listname, index, stack, player) + dirty_inventories[inv:get_location().name] = true + end, +} + +-- If the detached inventory doesn't exist, reads saved metadata version of the inventory and creates it +-- Doesn't do anything if the detached inventory already exists, the detached inventory is authoritative +local retrieve_inventory = function(digtron_id) + local inv = minetest.get_inventory({type="detached", name=digtron_id}) + if inv == nil then + inv = minetest.create_detached_inventory(digtron_id, detached_inventory_callbacks) + local inv_string = mod_meta:get_string(digtron_id..":inv") + if inv_string ~= "" then + local inventory_table = minetest.deserialize(inv_string) + for listname, invlist in pairs(inventory_table) do + inv:set_size(listname, #invlist) + inv:set_list(listname, invlist) + end + end + end + return inv +end + +-- Stores contents of detached inventory as a metadata string +local persist_inventory = function(digtron_id) + local inv = minetest.get_inventory({type="detached", name=digtron_id}) + if inv == nil then + minetest.log("error", "[Digtron] persist_inventory attempted to record a nonexistent inventory " + .. digtron_id) + return + end + local lists = inv:get_lists() + + local persist = {} + for listname, invlist in pairs(lists) do + local inventory = {} + for i, stack in ipairs(invlist) do + table.insert(inventory, stack:to_string()) -- convert into strings for serialization + end + persist[listname] = inventory + end + + mod_meta:set_string(digtron_id..":inv", minetest.serialize(persist)) +end + +minetest.register_globalstep(function(dtime) + for digtron_id, _ in pairs(dirty_inventories) do + persist_inventory(digtron_id) + dirty_inventories[digtron_id] = nil + end +end) + +-- There should only be one of these at a time, but it doesn't cost much to be safe. +local predictive_inventory = {} +-- Copies digtron's inventory into a temporary location so that a dig cycle can be run +-- using it without affecting the actual inventory until everything's been confirmed to work +local get_predictive_inventory = function(digtron_id) + local existing = predictive_inventory[digtron_id] + if existing then return existing end + + -- Using digtron_id as the player_name parameter to prevent this from being sent on the network to anyone real. + local predictive_inv = minetest.create_detached_inventory("digtron_predictive_"..digtron_id, detached_inventory_callbacks, digtron_id) + predictive_inventory[digtron_id] = predictive_inv + local source_inv = retrieve_inventory(digtron_id) + + -- Populate predictive inventory with the digtron's contents + for listname, invlist in pairs(source_inv:get_lists()) do + predictive_inv:set_size(listname, #invlist) + predictive_inv:set_list(listname, invlist) + end + + return predictive_inv +end +-- Wipes predictive inventory without committing it (eg, on failure of predicted operation) +local clear_predictive_inventory = function(digtron_id) + local predictive_inv = predictive_inventory[digtron_id] + if not predictive_inv then + minetest.log("error", "[Digtron] clear_predictive_inventory called for " .. digtron_id + .. " but predictive inventory did not exist") + return + end + + minetest.remove_detached_inventory("digtron_predictive_"..digtron_id) + predictive_inventory[digtron_id] = nil + + if next(predictive_inventory) ~= nil then + minetest.log("warning", "[Digtron] multiple predictive inventories were in existence, this shouldn't be happening. File an issue with Digtron programmers.") + end +end +-- Copies the predictive inventory's contents into the actual digtron's inventory and wipes the predictive inventory +local commit_predictive_inventory = function(digtron_id) + local predictive_inv = predictive_inventory[digtron_id] + if not predictive_inv then + minetest.log("error", "[Digtron] commit_predictive_inventory called for " .. digtron_id + .. " but predictive inventory did not exist") + return + end + + local source_inv = retrieve_inventory(digtron_id) + for listname, invlist in pairs(predictive_inv:get_lists()) do + source_inv:set_list(listname, invlist) + end + dirty_inventories[digtron_id] = true + clear_predictive_inventory(digtron_id) +end + +return { + retrieve_inventory = retrieve_inventory, + persist_inventory = persist_inventory, + get_predictive_inventory = get_predictive_inventory, + commit_predictive_inventory = commit_predictive_inventory, + clear_predictive_inventory = clear_predictive_inventory, +} \ No newline at end of file diff --git a/locale/template.pot b/locale/template.pot deleted file mode 100644 index cd9ff0a..0000000 --- a/locale/template.pot +++ /dev/null @@ -1,1494 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-01-09 00:37-0700\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#: awards.lua:122 -msgid "Deep Blue Digtron" -msgstr "" - -#: awards.lua:123 -msgid "Encounter water while operating a Digtron." -msgstr "" - -#: awards.lua:129 -msgid "Digtrons of Fire" -msgstr "" - -#: awards.lua:130 -msgid "Encounter lava while operating a Digtron." -msgstr "" - -#: awards.lua:136 -msgid "Bigtron" -msgstr "" - -#: awards.lua:137 -msgid "Operate a Digtron with 10 or more component blocks." -msgstr "" - -#: awards.lua:143 -msgid "Really Bigtron" -msgstr "" - -#: awards.lua:144 -msgid "Operate a Digtron with 100 or more component blocks." -msgstr "" - -#: awards.lua:150 -msgid "Buildtron" -msgstr "" - -#: awards.lua:151 -msgid "Operate a Digtron with 25 or more builder modules." -msgstr "" - -#: awards.lua:157 -msgid "Digging Leviathan" -msgstr "" - -#: awards.lua:158 -msgid "Operate a Digtron with 25 or more digger heads." -msgstr "" - -#: awards.lua:164 -msgid "Digtron In The Sky" -msgstr "" - -#: awards.lua:165 -msgid "Operate a Digtron above 1000m elevation" -msgstr "" - -#: awards.lua:171 -msgid "Digtron High" -msgstr "" - -#: awards.lua:172 -msgid "Operate a Digtron above 100m elevation" -msgstr "" - -#: awards.lua:178 -msgid "Scratching the Surface" -msgstr "" - -#: awards.lua:179 -msgid "Operate a Digtron 100m underground" -msgstr "" - -#: awards.lua:185 -msgid "Digging Deeper" -msgstr "" - -#: awards.lua:186 -msgid "Operate a Digtron 1,000m underground" -msgstr "" - -#: awards.lua:192 -msgid "More Than a Mile" -msgstr "" - -#: awards.lua:193 -msgid "Operate a Digtron 2,000m underground" -msgstr "" - -#: awards.lua:199 -msgid "Digging Below Plausibility" -msgstr "" - -#: awards.lua:200 -msgid "" -"The deepest mine in the world is only 3.9 km deep, you operated a Digtron " -"below 4km" -msgstr "" - -#: awards.lua:206 -msgid "Double Depth" -msgstr "" - -#: awards.lua:207 -msgid "Operate a Digtron 8,000m underground" -msgstr "" - -#: awards.lua:213 -msgid "Halfway to the Core" -msgstr "" - -#: awards.lua:214 -msgid "Operate a Digtron 16,000m underground" -msgstr "" - -#: awards.lua:220 -msgid "Nowhere To Go But Up" -msgstr "" - -#: awards.lua:221 -msgid "Operate a Digtron 30,000m underground" -msgstr "" - -#: awards.lua:227 -msgid "Mese Master" -msgstr "" - -#: awards.lua:228 -msgid "Mine 100 Mese crystals with a Digtron" -msgstr "" - -#: awards.lua:239 -msgid "Diamond Vs. Diamond" -msgstr "" - -#: awards.lua:240 -msgid "Mine 100 diamonds with a Digtron" -msgstr "" - -#: awards.lua:251 -msgid "Strip Mining" -msgstr "" - -#: awards.lua:252 -msgid "Excavate 1000 units of dirt with a Digtron" -msgstr "" - -#: awards.lua:263 -msgid "Digtron Miner" -msgstr "" - -#: awards.lua:264 -msgid "Excavate 1000 blocks using a Digtron" -msgstr "" - -#: awards.lua:274 -msgid "Digtron Expert Miner" -msgstr "" - -#: awards.lua:275 -msgid "Excavate 10,000 blocks using a Digtron" -msgstr "" - -#: awards.lua:285 -msgid "Digtron Master Miner" -msgstr "" - -#: awards.lua:286 -msgid "Excavate 100,000 blocks using a Digtron" -msgstr "" - -#: awards.lua:296 -msgid "DIGTRON MEGAMINER" -msgstr "" - -#: awards.lua:297 -msgid "Excavate over a million blocks using a Digtron!" -msgstr "" - -#: awards.lua:307 -msgid "Clear Cutting" -msgstr "" - -#: awards.lua:308 -msgid "Chop down 1000 units of tree with a Digtron" -msgstr "" - -#: awards.lua:319 -msgid "Digtron Deforestation" -msgstr "" - -#: awards.lua:320 -msgid "Chop down 10,000 units of tree with a Digtron" -msgstr "" - -#: awards.lua:331 -msgid "Lawnmower" -msgstr "" - -#: awards.lua:332 -msgid "Harvest 1000 units of grass with a Digtron" -msgstr "" - -#: awards.lua:343 -msgid "Iron Digtron" -msgstr "" - -#: awards.lua:344 -msgid "Excavate 1000 units of iron ore with a Digtron" -msgstr "" - -#: awards.lua:355 -msgid "Copper Digtron" -msgstr "" - -#: awards.lua:356 -msgid "Excavate 1000 units of copper ore with a Digtron" -msgstr "" - -#: awards.lua:367 -msgid "Coal Digtron" -msgstr "" - -#: awards.lua:368 -msgid "Excavate 1,000 units if coal with a Digtron" -msgstr "" - -#: awards.lua:379 -msgid "Bagger 288" -msgstr "" - -#: awards.lua:380 -msgid "Excavate 10,000 units of coal with a Digtron" -msgstr "" - -#: awards.lua:391 -msgid "Digtron 49er" -msgstr "" - -#: awards.lua:392 -msgid "Excavate 100 units of gold with a Digtron" -msgstr "" - -#: awards.lua:403 -msgid "Constructive Digging" -msgstr "" - -#: awards.lua:404 -msgid "Build 1,000 blocks with a Digtron" -msgstr "" - -#: awards.lua:414 -msgid "Highly Constructive Digging" -msgstr "" - -#: awards.lua:415 -msgid "Build 10,000 blocks with a Digtron" -msgstr "" - -#: awards.lua:425 -msgid "Digtron Packrat" -msgstr "" - -#: awards.lua:426 -msgid "Stored 10 or more Digtron blocks in one crate." -msgstr "" - -#: awards.lua:432 -msgid "Digtron Hoarder" -msgstr "" - -#: awards.lua:433 -msgid "Stored 100 or more Digtron blocks in one crate." -msgstr "" - -#: doc.lua:22 -msgid "" -"A crafting component used in the manufacture of all Digtron block types." -msgstr "" - -#: doc.lua:23 -msgid "" -"Place the Digtron Core in the center of the crafting grid. All Digtron " -"recipes consist of arranging various other materials around the central " -"Digtron Core." -msgstr "" - -#: doc.lua:27 -msgid "" -"A 'builder' module for a Digtron. By itself it does nothing, but as part of " -"a Digtron it is used to construct user-defined blocks." -msgstr "" - -#: doc.lua:28 -msgid "" -"A builder head is the most complex component of this system. It has period " -"and offset properties, and also an inventory slot where you \"program\" it " -"by placing an example of the block type that you want it to build.\n" -"\n" -"When the \"Save & Show\" button is clicked the properties for period and " -"offset will be saved, and markers will briefly be shown to indicate where " -"the nearest spots corresponding to those values are. The builder will build " -"its output at those locations provided it is moving along the matching " -"axis.\n" -"\n" -"There is also an \"Extrusion\" setting. This allows your builder to extrude " -"a line of identical blocks from the builder output, in the direction the " -"output side is facing, until it reaches an obstruction or until it reaches " -"the extrusion limit. This can be useful for placing columns below a bridge, " -"or for filling a large volume with a uniform block type without requiring a " -"large number of builder heads.\n" -"\n" -"The \"output\" side of a builder is the side with a black crosshair on it.\n" -"\n" -"Builders also have a \"facing\" setting. If you haven't memorized the " -"meaning of the 24 facing values yet, builder heads have a helpful \"Read & " -"Save\" button to fill this value in for you. Simply build a temporary " -"instance of the block in the output location in front of the builder, adjust " -"it to the orientation you want using the screwdriver tool, and then when you " -"click the \"Read & Save\" button the block's facing will be read and saved.\n" -"\n" -"Note: if more than one builder tries to build into the same space " -"simultaneously, it is not predictable which builder will take priority. One " -"will succeed and the other will fail. You should arrange your builders to " -"avoid this for consistent results." -msgstr "" - -#: doc.lua:43 -msgid "" -"Stores building materials for use by builder heads and materials dug up by " -"digger heads." -msgstr "" - -#: doc.lua:44 -msgid "" -"Inventory modules have the same capacity as a chest. They're used both for " -"storing the products of the digger heads and as the source of materials used " -"by the builder heads. A digging machine whose builder heads are laying down " -"cobble can automatically self-replenish in this way, but note that an " -"inventory module is still required as buffer space even if the digger heads " -"produced everything needed by the builder heads in a given cycle.\n" -"\n" -"Inventory modules are not required for a digging-only machine. If there's " -"not enough storage space to hold the materials produced by the digging heads " -"the excess material will be ejected out the back of the control block. " -"They're handy for accumulating ores and other building materials, though.\n" -"\n" -"Digging machines can have multiple inventory modules added to expand their " -"capacity." -msgstr "" - -#: doc.lua:53 -msgid "" -"Digtron inventory modules are compatible with hoppers, adjacent hoppers will " -"add to or take from their inventories. Hoppers are not part of the Digtron " -"and will not move with it, however. They may be useful for creating a " -"\"docking station\" for a Digtron." -msgstr "" - -#: doc.lua:59 -msgid "" -"Inventory modules are compatible with Pipeworks blocks. When a Digtron moves " -"one of the inventory modules adjacent to a pipe it will automatically hook " -"up to it, and disconnect again when it moves on." -msgstr "" - -#: doc.lua:62 -msgid "" -"When a control unit is triggered, it will tally up how much fuel is required " -"for the next cycle and then burn items from the fuel hopper until a " -"sufficient amount of heat has been generated to power the operation. Any " -"leftover heat will be retained by the control unit for use in the next " -"cycle; this is the \"heat remaining in controller furnace\". This means you " -"don't have to worry too much about what kinds of fuel you put in the fuel " -"store, none will be wasted (unless you dig away a control unit with some " -"heat remaining in it, that heat does get wasted).\n" -"\n" -"By using one lump of coal as fuel a digtron can:\n" -"\tBuild @1 blocks\n" -"\tDig @2 stone blocks\n" -"\tDig @3 wood blocks\n" -"\tDig @4 dirt or sand blocks" -msgstr "" - -#: doc.lua:71 -msgid "Stores fuel to run a Digtron" -msgstr "" - -#: doc.lua:72 -msgid "" -"Digtrons have an appetite. Build operations and dig operations require a " -"certain amount of fuel, and that fuel comes from fuel store modules. Note " -"that movement does not require fuel, only digging and building." -msgstr "" - -#: doc.lua:78 -msgid "" -"Digtron fuel store modules are compatible with hoppers, adjacent hoppers " -"will add to or take from their inventories. Hoppers are not part of the " -"Digtron and will not move with it, however. They may be useful for creating " -"a \"docking station\" for a Digtron." -msgstr "" - -#: doc.lua:84 -#: doc.lua:103 -msgid "" -"Fuel modules are compatible with Pipeworks blocks. When a Digtron moves one " -"of the inventory modules adjacent to a pipe it will automatically hook up to " -"it, and disconnect again when it moves on." -msgstr "" - -#: doc.lua:89 -msgid "Holds RE batteries to run a Digtron" -msgstr "" - -#: doc.lua:90 -msgid "" -"Digtrons have an appetite, and it can be satisfied by electricity as well. " -"Build operations and dig operations require a certain amount of power, and " -"that power comes from the batteries place in the holder. Note that movement " -"does not consume charge, only digging and building.\n" -"\n" -"When a control unit is triggered, it will tally up how much power is " -"required for the next cycle and then discharge the batteries in the battery " -"holder until a sufficient amount of heat has been generated to power the " -"operation. Any leftover heat will be retained by the control unit for use in " -"the next cycle; this is the \"heat remaining in controller furnace\". Thus " -"no power is wasted (unless you dig away a control unit with some heat " -"remaining in it, that heat does get wasted), and the discharged batteries " -"can be taken away to be recharged.\n" -"\n" -"One fully charged battery can:\n" -"\tBuild @1 blocks\n" -"\tDig @2 stone blocks\n" -"\tDig @3 wood blocks\n" -"\tDig @4 dirt or sand blocks" -msgstr "" - -#: doc.lua:107 -msgid "" -"Stores fuel for a Digtron and also has an inventory for building materials" -msgstr "" - -#: doc.lua:108 -msgid "" -"For smaller jobs the two dedicated modules may simply be too much of a good " -"thing, wasting precious Digtron space to give unneeded capacity. The " -"combined storage module is the best of both worlds, splitting its internal " -"space between building material inventory and fuel storage. It has 3/4 " -"building material capacity and 1/4 fuel storage capacity." -msgstr "" - -#: doc.lua:113 -msgid "" -"Digtron inventory modules are compatible with hoppers, adjacent hoppers will " -"add to or take from their inventories. A hopper on top of a combined " -"inventory module will insert items into its general inventory, a side hopper " -"will insert items into its fuel inventory, and a hopper on the bottom of a " -"combined inventory module will take items from its general inventory. " -"Hoppers are not part of the Digtron and will not move with it, however. They " -"may be useful for creating a \"docking station\" for a Digtron." -msgstr "" - -#: doc.lua:119 -msgid "" -"Combination modules are compatible with Pipeworks blocks. When a Digtron " -"moves one of the inventory modules adjacent to a pipe it will automatically " -"hook up to it, and disconnect again when it moves on. Items are extracted " -"from the \"main\" inventory, and items coming into the combination module " -"from any direction except the underside are inserted into \"main\". However, " -"a pipe entering the combination module from the underside will attempt to " -"insert items into the \"fuel\" inventory instead." -msgstr "" - -#: doc.lua:124 -msgid "" -"This is the \"locked\" version of the Digtron crate. It can only be used by " -"the player who placed it." -msgstr "" - -#: doc.lua:126 -msgid "An empty crate that a Digtron can be stored in" -msgstr "" - -#: doc.lua:127 -msgid "" -"Digtrons can be pushed around and rotated, and that may be enough for " -"getting them perfectly positioned for the start of a run. But once your " -"digger is a kilometer down under a shaft filled with stairs, how to get it " -"back to the surface to run another pass?\n" -"\n" -"Place an empty Digtron crate next to a Digtron and right-click it to pack " -"the Digtron (and all its inventory and settings) into the crate. You can " -"then collect the crate, bring it somewhere else, build the crate, and then " -"unpack the Digtron from it again. The Digtron will appear in the same " -"relative location and orientation to the crate as when it was packed away " -"inside it." -msgstr "" - -#: doc.lua:131 -msgid "A crate containing a Digtron array" -msgstr "" - -#: doc.lua:132 -msgid "" -"This crate contains a Digtron assembly that was stored in it earlier. Place " -"it somewhere and right-click on it to access the label text that was applied " -"to it. There's also a button that causes it to display the shape the " -"deployed Digtron would take if you unpacked the crate, marking unbuildable " -"blocks with a warning marker. And finally there's a button to actually " -"deploy the Digtron, replacing this loaded crate with an empty that can be " -"reused later." -msgstr "" - -#: doc.lua:142 -msgid "A basic controller to make a Digtron array move and operate." -msgstr "" - -#: doc.lua:143 -msgid "" -"Right-click on this module to make the digging machine go one step. The " -"digging machine will go in the direction that the control module is " -"oriented.\n" -"\n" -"A control module can only trigger once per second. Gives you time to enjoy " -"the scenery and smell the flowers (or their mulched remains, at any rate).\n" -"\n" -"If you're standing within the digging machine's volume, or in a block " -"adjacent to it, you will be pulled along with the machine when it moves." -msgstr "" - -#: doc.lua:149 -msgid "" -"A more sophisticated controller that includes the ability to set the number " -"of cycles it will run for, as well as diagonal movement." -msgstr "" - -#: doc.lua:150 -msgid "" -"An Auto-control module can be set to run for an arbitrary number of cycles. " -"Once it's running, right-click on it again to interrupt its rampage. If " -"anything interrupts it - the player's click, an undiggable obstruction, " -"running out of fuel - it will remember the number of remaining cycles so " -"that you can fix the problem and set it running again to complete the " -"original plan.\n" -"\n" -"The digging machine will go in the direction that the control module is " -"oriented.\n" -"\n" -"Auto-controllers can also be set to move diagonally by setting the \"Slope\" " -"parameter to a non-zero value. The controller will then shunt the Digtron in " -"the direction of the arrows painted on its sides every X steps it moves. The " -"Digtron will trigger dig heads when it shunts to the side, but will not " -"trigger builder modules or intermittent dig heads. The \"Offset\" setting " -"determines at what point the lateral motion will take place.\n" -"\n" -"The \"Stop block\" inventory slot in an auto-controller allows you to " -"program an auto-controller to treat certain block types as impenetrable " -"obstructions. This can allow you to fence a Digtron in with something so you " -"don't have to carefully count exactly how many steps it should take, for " -"example.\n" -"\n" -"Note that the Digtron detects an undiggable block by the item that would be " -"produced when digging it. Setting cobble as the stop block will make both " -"cobble and regular stone undiggable, but setting a block of regular stone " -"(produced from cobble in a furnace) as the stop block will *not* stop a " -"Digtron from digging regular stone (since digging regular stone produces " -"cobble, not stone)." -msgstr "" - -#: doc.lua:160 -msgid "" -"A simplified controller that merely moves a Digtron around without " -"triggering its builder or digger modules" -msgstr "" - -#: doc.lua:161 -msgid "" -"Aka the \"can you rebuild it six inches to the left\" module. This is a much " -"simplified control module that does not trigger the digger or builder heads " -"when right-clicked, it only moves the digging machine. It's up to you to " -"ensure there's space for it to move into.\n" -"\n" -"Since movement alone does not require fuel, a pusher module has no internal " -"furnace. Pushers also don't require traction, since their primary purpose is " -"repositioning Digtrons let's say they have a built-in crane or something." -msgstr "" - -#: doc.lua:165 -msgid "A device that allows one to rotate their Digtron into new orientations" -msgstr "" - -#: doc.lua:166 -msgid "" -"This magical module can rotate a Digtron array in place around itself. Right-" -"clicking on it will rotate the Digtron 90 degrees in the direction the " -"orange arrows on its sides indicate (widdershins around the Y axis by " -"default, use the screwdriver to change this) assuming there's space for the " -"Digtron in its new orientation. Builders and diggers will not trigger on " -"rotation." -msgstr "" - -#: doc.lua:170 -msgid "A standard Digtron digger head" -msgstr "" - -#: doc.lua:171 -msgid "" -"Facing of a digger head is significant; it will excavate material from the " -"block on the spinning grinder wheel face of the digger head. Generally " -"speaking, you'll want these to face forward - though having them aimed to " -"the sides can also be useful." -msgstr "" - -#: doc.lua:173 -msgid "Two standard Digtron digger heads merged at 90 degrees to each other" -msgstr "" - -#: doc.lua:174 -msgid "" -"This digger head is mainly of use when you want to build a Digtron capable " -"of digging diagonal paths. A normal one-direction dig head would be unable " -"to clear blocks in both of the directions it would be called upon to move, " -"resulting in a stuck Digtron.\n" -"\n" -"One can also make use of dual dig heads to simplify the size and layout of a " -"Digtron, though this is generally not of practical use." -msgstr "" - -#: doc.lua:178 -msgid "" -"Two standard soft-material Digtron digger heads merged at 90 degrees to each " -"other" -msgstr "" - -#: doc.lua:179 -msgid "" -"This digger head is mainly of use when you want to build a Digtron capable " -"of digging diagonal paths. A normal one-direction dig head would be unable " -"to clear blocks in both of the directions it would be called upon to move, " -"resulting in a stuck Digtron.\n" -"\n" -"Like a normal single-direction soft digger head, this digger only excavates " -"material belonging to groups softer than stone.\n" -"\n" -"One can make use of dual dig heads to simplify the size and layout of a " -"Digtron." -msgstr "" - -#: doc.lua:185 -msgid "A standard Digtron digger head that only triggers periodically" -msgstr "" - -#: doc.lua:186 -msgid "" -"This is a standard digger head capable of digging any material, but it will " -"only trigger periodically as the Digtron moves. This can be useful for " -"punching regularly-spaced holes in a tunnel wall, for example." -msgstr "" - -#: doc.lua:188 -msgid "" -"A standard soft-material Digtron digger head that only triggers periodically" -msgstr "" - -#: doc.lua:189 -msgid "" -"This is a standard soft-material digger head capable of digging any " -"material, but it will only trigger periodically as the Digtron moves. This " -"can be useful for punching regularly-spaced holes in a tunnel wall, for " -"example." -msgstr "" - -#: doc.lua:191 -msgid "A Digtron digger head that only excavates soft materials" -msgstr "" - -#: doc.lua:192 -msgid "" -"This specialized digger head is designed to excavate only softer material " -"such as sand or gravel. In technical terms, this digger digs blocks " -"belonging to the \"crumbly\", \"choppy\", \"snappy\", " -"\"oddly_diggable_by_hand\" and \"fleshy\" groups.\n" -"\n" -"The intended purpose of this digger is to be aimed at the ceiling or walls " -"of a tunnel being dug, making spaces to allow shoring blocks to be inserted " -"into unstable roofs but leaving the wall alone if it's composed of a more " -"stable material.\n" -"\n" -"It can also serve as part of a lawnmower or tree-harvester." -msgstr "" - -#: doc.lua:200 -msgid "" -"High-voltage power connector allowing a Digtron to be powered from a Technic " -"power network." -msgstr "" - -#: doc.lua:201 -msgid "" -"A power connector node automatically hooks into adjacent high-voltage (HV) " -"power cables, but it must be configured to set how much power it will draw " -"from the attached network. Right-click on the power connector to bring up a " -"form that shows the current estimated maximum power usage of the Digtron the " -"power connector is part of and a field where a power value can be entered. " -"The estimated maximum power usage is the amount of power this Digtron will " -"use in the worst case situation, with all of its digger heads digging the " -"toughest material and all of its builder heads building a block " -"simultaneously.\n" -"\n" -"You can set the power connector's usage lower than this, and if the Digtron " -"is unable to get sufficient power from the network it will use on-board " -"batteries or burnable fuel to make up the shortfall." -msgstr "" - -#: doc.lua:207 -msgid "" -"An outlet that can be used to eject accumulated detritus from a Digtron's " -"inventory." -msgstr "" - -#: doc.lua:208 -msgid "" -"When this block is punched it will search the entire inventory of the " -"Digtron and will eject a stack of items taken from it, provided the items " -"are not set for use by any of the Digtron's builders. It will not eject if " -"the destination block is occupied." -msgstr "" - -#: doc.lua:213 -msgid "" -"Item ejectors are compatible with pipeworks and will automatically connect " -"to a pipeworks tube if one is adjacent in the output location." -msgstr "" - -#: doc.lua:218 -msgid "" -"A device for duplicating an adjacent Digtron using parts from its inventory." -msgstr "" - -#: doc.lua:219 -msgid "" -"Place the duplicator block adjacent to a Digtron, and then fill the " -"duplicator's inventory with enough parts to recreate the adjacent Digtron. " -"Then place an empty Digtron crate at the duplicator's output (the side with " -"the black \"+\") and click the \"Duplicate\" button in the duplicator's " -"right-click GUI. If enough parts are available the Digtron will be " -"duplicated and packed into the crate, along with all of its programming but " -"with empty inventories." -msgstr "" - -#: doc.lua:223 -msgid "Structural component for a Digtron array" -msgstr "" - -#: doc.lua:224 -msgid "" -"These blocks allow otherwise-disconnected sections of digtron blocks to be " -"linked together. They are not usually necessary for simple diggers but more " -"elaborate builder arrays might have builder blocks that can't be placed " -"directly adjacent to other digtron blocks and these blocks can serve to keep " -"them connected to the controller.\n" -"\n" -"They may also be used for providing additional traction if your digtron " -"array is very tall compared to the terrain surface that it's touching.\n" -"\n" -"You can also use them decoratively, or to build a platform to stand on as " -"you ride your mighty mechanical leviathan through the landscape." -msgstr "" - -#: doc.lua:230 -msgid "Digtron light source" -msgstr "" - -#: doc.lua:231 -msgid "" -"A light source that moves along with the digging machine. Convenient if " -"you're digging a tunnel that you don't intend to outfit with torches or " -"other permanent light fixtures. Not quite as bright as a torch since the " -"protective lens tends to get grimy while burrowing through the earth." -msgstr "" - -#: doc.lua:233 -msgid "Digtron panel" -msgstr "" - -#: doc.lua:234 -msgid "" -"A structural panel that can be made part of a Digtron to provide shelter for " -"an operator, keep sand out of the Digtron's innards, or just to look cool." -msgstr "" - -#: doc.lua:236 -msgid "Digtron edge panel" -msgstr "" - -#: doc.lua:237 -msgid "" -"A pair of structural panels that can be made part of a Digtron to provide " -"shelter for an operator, keep sand out of the Digtron's innards, or just to " -"look cool." -msgstr "" - -#: doc.lua:239 -msgid "Digtron corner panel" -msgstr "" - -#: doc.lua:240 -msgid "" -"A trio of structural panels that can be made part of a Digtron to provide " -"shelter for an operator, keep sand out of the Digtron's innards, or just to " -"look cool." -msgstr "" - -#: doc.lua:244 -msgid "Digtron" -msgstr "" - -#: doc.lua:245 -msgid "" -"The Digtron system is a set of blocks used to construct tunnel-boring and " -"construction machines." -msgstr "" - -#: doc.lua:252 -msgid "Summary" -msgstr "" - -#: doc.lua:253 -msgid "" -"Digtron blocks can be used to construct highly customizable and modular " -"tunnel-boring machines, bridge-builders, road-pavers, wall-o-matics, and " -"other such construction/destruction contraptions.\n" -"\n" -"The basic blocks that can be assembled into a functioning digging machine " -"are:\n" -"\n" -"* Diggers, which excavate material in front of them when the machine is " -"triggered\n" -"* Builders, which build a user-configured block in front of them\n" -"* Inventory modules, which hold material produced by the digger and provide " -"material to the builders\n" -"* Control block, used to trigger the machine and move it in a particular " -"direction.\n" -"\n" -"A digging machine's components must be connected to the control block via a " -"path leading through the faces of the blocks - diagonal connections across " -"edges and corners don't count." -msgstr "" - -#: doc.lua:266 -msgid "Concepts" -msgstr "" - -#: doc.lua:268 -msgid "" -"Several general concepts are important when building more sophisticated " -"diggers.\n" -"\n" -"Facing - a number between 0-23 that determines which direction a block is " -"facing and what orientation it has. Not all blocks make use of facing (basic " -"blocks such as cobble or sand have no facing, for example) so it's not " -"always necessary to set this when configuring a builder head. The facing of " -"already-placed blocks can be altered through the use of the screwdriver " -"tool.\n" -"\n" -"Period - Builder and digger heads can be made periodic by changing the " -"period value to something other than 1. This determines how frequently they " -"trigger. A period of 1 triggers on every block, a period of 2 triggers once " -"every second block, a period of 3 triggers once every third block, etc. " -"These are useful when setting up a machine to place regularly-spaced " -"features as it goes. For example, you could have a builder head that places " -"a torch every 8 steps, or a digger block that punches a landing in the side " -"of a vertical stairwell at every level.\n" -"\n" -"Offset - The location at which a periodic module triggers is globally " -"uniform. This is handy if you want to line up the blocks you're building " -"(for example, placing pillars and a crosspiece every 4 blocks in a tunnel, " -"or punching alcoves in a wall to place glass windows). If you wish to change " -"how the pattern lines up, modify the \"offset\" setting.\n" -"\n" -"Shift-right-clicking - since most of the blocks of the digging machine have " -"control screens associated with right-clicking, building additional blocks " -"on top of them or rotating them with the screwdriver requires the shift key " -"to be held down when right-clicking on them.\n" -"\n" -"Traction - Digtrons cannot fly. By default, they need to be touching one " -"block of solid ground for every three blocks of Digtron in order to move. " -"Digtrons can fall, though - traction is never needed when a Digtron is " -"moving downward. \"Pusher\" controllers can ignore the need for traction " -"when moving in any direction." -msgstr "" - -#: doc.lua:282 -msgid "Audio cues" -msgstr "" - -#: doc.lua:284 -msgid "" -"When a digging machine is unable to complete a cycle it will make one of " -"several noises to indicate what the problem is. It will also set its " -"mouseover text to explain what went wrong.\n" -"\n" -"Squealing traction wheels indicates a mobility problem. If the squealing is " -"accompanied by a buzzer, the digging machine has encountered an obstruction " -"it can't dig through. This could be a protected region (the digging machine " -"has only the priviledges of the player triggering it), a chest containing " -"items, or perhaps the digger was incorrectly designed and can't dig the " -"correctly sized and shaped cavity for it to move forward into. There are " -"many possibilities.\n" -"\n" -"Squealing traction wheels with no accompanying buzzer indicates that the " -"digging machine doesn't have enough solid adjacent blocks to push off of. " -"Tunnel boring machines cannot fly or swim, not even through lava, and they " -"don't dig fast enough to \"catch sick air\" when they emerge from a " -"cliffside. If you wish to cross a chasm you'll need to ensure that there are " -"builder heads placing a solid surface as you go. If you've built a very tall " -"digtron with a small surface footprint you may need to improve its traction " -"by adding structural modules that touch the ground.\n" -"\n" -"A buzzer by itself indicates that the Digtron has run out of fuel. There may " -"be traces remaining in the hopper, but they're not enough to execute the " -"next dig/build cycle.\n" -"\n" -"A ringing bell indicates that there are insufficient materials in inventory " -"to supply all the builder heads for this cycle.\n" -"\n" -"A short high-pitched honk means that one or more of the builder heads don't " -"have an item set. A common oversight, especially with large and elaborate " -"digging machines, that might be hard to notice and annoying to fix if not " -"noticed right away.\n" -"\n" -"Splashing water sounds means your Digtron is digging adjacent to (or " -"through) water-containing blocks. Digtrons are waterproof, but this might be " -"a useful indication that you should take care when installing doors in the " -"tunnel walls you've placed here.\n" -"\n" -"A triple \"voop voop voop!\" alarm indicates that there is lava adjacent to " -"your Digtron. Digtrons can't penetrate lava by default, and this alarm " -"indicates that a non-lava-proof Digtron operator may wish to exercise " -"caution when opening the door to clear the obstruction." -msgstr "" - -#: doc.lua:302 -msgid "Tips and Tricks" -msgstr "" - -#: doc.lua:304 -msgid "" -"To more easily visualize the operation of a Digtron, imagine that its cycle " -"of operation follows these steps in order:\n" -"\n" -"* Dig\n" -"* Move\n" -"* Build\n" -"* Allow dust to settle (ie, sand and gravel fall)\n" -"\n" -"If you're building a repeating pattern of blocks, your periodicity should be " -"one larger than your largest offset. For example, if you've laid out " -"builders to create a set of spiral stairs and the offsets are from 0 to 11, " -"you'll want to use periodicity 12.\n" -"\n" -"A good way to program a set of builders is to build a complete example of " -"the structure you want them to create, then place builders against the " -"structure and have them \"read\" all of its facings. This also lets you more " -"easily visualize the tricks that might be needed to allow the digtron to " -"pass through the structure as it's being built." -msgstr "" - -#: util_execute_cycle.lua:51 -msgid "Digtron is adjacent to unloaded nodes." -msgstr "" - -#: util_execute_cycle.lua:65 -msgid "Digtron has @1 blocks but only enough traction to move @2 blocks.\n" -msgstr "" - -#: util_execute_cycle.lua:112 -#: util_execute_cycle.lua:373 -#: util_execute_cycle.lua:453 -#: util_execute_cycle.lua:576 -#: nodes\node_controllers.lua:50 -#: nodes\node_controllers.lua:209 -msgid "Heat remaining in controller furnace: @1" -msgstr "" - -#: util_execute_cycle.lua:177 -#: util_execute_cycle.lua:423 -#: util_execute_cycle.lua:518 -#: nodes\node_axle.lua:59 -msgid "Digtron is obstructed." -msgstr "" - -#: util_execute_cycle.lua:253 -msgid "Digtron needs more fuel." -msgstr "" - -#: util_execute_cycle.lua:263 -msgid "" -"Digtron connected to at least one builder with no output material assigned." -msgstr "" - -#: util_execute_cycle.lua:267 -msgid "Digtron has insufficient building materials. Needed: @1" -msgstr "" - -#: util_execute_cycle.lua:352 -msgid "" -"Digtron unexpectedly failed to execute one or more build operations, likely " -"due to an inventory error." -msgstr "" - -#: nodes\node_axle.lua:6 -msgid "Digtron Rotation Axle" -msgstr "" - -#: nodes\node_battery_holder.lua:13 -msgid "Batteries" -msgstr "" - -#: nodes\node_battery_holder.lua:33 -msgid "Digtron Battery Holder" -msgstr "" - -#: nodes\node_builders.lua:20 -msgid "Block to build" -msgstr "" - -#: nodes\node_builders.lua:21 -msgid "Extrusion" -msgstr "" - -#: nodes\node_builders.lua:22 -msgid "" -"Builder will extrude this many blocks in the direction it is facing.\n" -"Can be set from 1 to @1.\n" -"Note that Digtron won't build into unloaded map regions." -msgstr "" - -#: nodes\node_builders.lua:23 -#: nodes\node_diggers.lua:36 -msgid "Periodicity" -msgstr "" - -#: nodes\node_builders.lua:24 -msgid "" -"Builder will build once every n steps.\n" -"These steps are globally aligned, so all builders with the\n" -"same period and offset will build on the same location." -msgstr "" - -#: nodes\node_builders.lua:25 -#: nodes\node_controllers.lua:93 -#: nodes\node_diggers.lua:38 -msgid "Offset" -msgstr "" - -#: nodes\node_builders.lua:26 -msgid "" -"Offsets the start of periodicity counting by this amount.\n" -"For example, a builder with period 2 and offset 0 builds\n" -"every even-numbered block and one with period 2 and\n" -"offset 1 builds every odd-numbered block." -msgstr "" - -#: nodes\node_builders.lua:27 -#: nodes\node_diggers.lua:40 -msgid "" -"Save &\n" -"Show" -msgstr "" - -#: nodes\node_builders.lua:28 -#: nodes\node_diggers.lua:41 -msgid "Saves settings" -msgstr "" - -#: nodes\node_builders.lua:29 -msgid "Facing" -msgstr "" - -#: nodes\node_builders.lua:30 -msgid "" -"Value from 0-23. Not all block types make use of this.\n" -"Use the 'Read & Save' button to copy the facing of the block\n" -"currently in the builder output location." -msgstr "" - -#: nodes\node_builders.lua:31 -msgid "" -"Read &\n" -"Save" -msgstr "" - -#: nodes\node_builders.lua:32 -msgid "" -"Reads the facing of the block currently in the build location,\n" -"then saves all settings." -msgstr "" - -#: nodes\node_builders.lua:41 -#: nodes\node_controllers.lua:108 -#: nodes\node_crate.lua:168 -#: nodes\node_diggers.lua:45 -#: nodes\node_duplicator.lua:22 -#: nodes\node_item_ejector.lua:17 -msgid "Help" -msgstr "" - -#: nodes\node_builders.lua:42 -#: nodes\node_controllers.lua:109 -#: nodes\node_crate.lua:169 -#: nodes\node_diggers.lua:46 -#: nodes\node_duplicator.lua:23 -#: nodes\node_item_ejector.lua:18 -msgid "Show documentation about this block" -msgstr "" - -#: nodes\node_builders.lua:137 -msgid "Digtron Builder Module" -msgstr "" - -#: nodes\node_controllers.lua:22 -msgid "Digtron Control Module" -msgstr "" - -#: nodes\node_controllers.lua:85 -msgid "Cycles" -msgstr "" - -#: nodes\node_controllers.lua:86 -msgid "" -"When triggered, this controller will try to run for the given number of " -"cycles.\n" -"The cycle count will decrement as it runs, so if it gets halted by a " -"problem\n" -"you can fix the problem and restart." -msgstr "" - -#: nodes\node_controllers.lua:87 -msgid "Set" -msgstr "" - -#: nodes\node_controllers.lua:88 -msgid "Saves the cycle setting without starting the controller running" -msgstr "" - -#: nodes\node_controllers.lua:89 -msgid "" -"Set &\n" -"Execute" -msgstr "" - -#: nodes\node_controllers.lua:90 -msgid "Begins executing the given number of cycles" -msgstr "" - -#: nodes\node_controllers.lua:91 -msgid "Slope" -msgstr "" - -#: nodes\node_controllers.lua:92 -msgid "" -"For diagonal digging. After moving forward this number of nodes the auto " -"controller\n" -"will add an additional cycle moving the digtron laterally in the\n" -"direction of the arrows on the side of this controller.\n" -"Set to 0 for no lateral digging." -msgstr "" - -#: nodes\node_controllers.lua:94 -msgid "" -"Sets the offset of the lateral motion defined in the Slope field.\n" -"Note: this offset is relative to the controller's location.\n" -"The controller will move laterally when it reaches the indicated point." -msgstr "" - -#: nodes\node_controllers.lua:95 -msgid "Delay" -msgstr "" - -#: nodes\node_controllers.lua:96 -msgid "Number of seconds to wait between each cycle" -msgstr "" - -#: nodes\node_controllers.lua:98 -msgid "Stop block" -msgstr "" - -#: nodes\node_controllers.lua:129 -#: nodes\node_controllers.lua:151 -#: nodes\node_controllers.lua:167 -msgid "Cycles remaining: @1" -msgstr "" - -#: nodes\node_controllers.lua:129 -#: nodes\node_controllers.lua:151 -msgid "Halted!" -msgstr "" - -#: nodes\node_controllers.lua:179 -msgid "Digtron Automatic Control Module" -msgstr "" - -#: nodes\node_controllers.lua:295 -msgid "Interrupted!" -msgstr "" - -#: nodes\node_controllers.lua:306 -msgid "Digtron Pusher Module" -msgstr "" - -#: nodes\node_crate.lua:30 -#: nodes\node_crate.lua:141 -msgid "Digtron Crate" -msgstr "" - -#: nodes\node_crate.lua:30 -#: nodes\node_crate.lua:31 -#: nodes\node_crate.lua:141 -#: nodes\node_crate.lua:199 -#: nodes\node_crate.lua:348 -msgid "Owned by @1" -msgstr "" - -#: nodes\node_crate.lua:37 -msgid "Digtron can't be packaged, it contains protected blocks" -msgstr "" - -#: nodes\node_crate.lua:45 -msgid "No Digtron components adjacent to package" -msgstr "" - -#: nodes\node_crate.lua:86 -#: nodes\node_duplicator.lua:182 -msgid "Crated @1-block Digtron" -msgstr "" - -#: nodes\node_crate.lua:92 -msgid "Digtron Crate (Empty)" -msgstr "" - -#: nodes\node_crate.lua:118 -msgid "Digtron Locked Crate (Empty)" -msgstr "" - -#: nodes\node_crate.lua:161 -#: nodes\node_crate.lua:176 -msgid "Digtron Name" -msgstr "" - -#: nodes\node_crate.lua:162 -#: nodes\node_crate.lua:177 -msgid "" -"Save\n" -"Title" -msgstr "" - -#: nodes\node_crate.lua:163 -#: nodes\node_crate.lua:178 -msgid "Saves the title of this Digtron" -msgstr "" - -#: nodes\node_crate.lua:164 -#: nodes\node_crate.lua:179 -msgid "" -"Show\n" -"Blocks" -msgstr "" - -#: nodes\node_crate.lua:165 -#: nodes\node_crate.lua:180 -msgid "Shows which blocks the packed Digtron will occupy if unpacked" -msgstr "" - -#: nodes\node_crate.lua:166 -#: nodes\node_crate.lua:181 -msgid "Unpack" -msgstr "" - -#: nodes\node_crate.lua:167 -#: nodes\node_crate.lua:182 -msgid "Attempts to unpack the Digtron on this location" -msgstr "" - -#: nodes\node_crate.lua:217 -msgid "" -"Unable to read layout from crate metadata, regrettably this Digtron may be " -"corrupted." -msgstr "" - -#: nodes\node_crate.lua:248 -msgid "Unable to deploy Digtron due to protected blocks in target area" -msgstr "" - -#: nodes\node_crate.lua:254 -msgid "Unable to deploy Digtron due to obstruction in target area" -msgstr "" - -#: nodes\node_crate.lua:291 -msgid "Digtron Crate (Loaded)" -msgstr "" - -#: nodes\node_crate.lua:322 -msgid "Digtron Locked Crate (Loaded)" -msgstr "" - -#: nodes\node_diggers.lua:37 -msgid "" -"Digger will dig once every n steps.\n" -"These steps are globally aligned, all diggers with\n" -"the same period and offset will dig on the same location." -msgstr "" - -#: nodes\node_diggers.lua:39 -msgid "" -"Offsets the start of periodicity counting by this amount.\n" -"For example, a digger with period 2 and offset 0 digs\n" -"every even-numbered block and one with period 2 and\n" -"offset 1 digs every odd-numbered block." -msgstr "" - -#: nodes\node_diggers.lua:104 -msgid "Digtron Digger Head" -msgstr "" - -#: nodes\node_diggers.lua:157 -msgid "Digtron Intermittent Digger Head" -msgstr "" - -#: nodes\node_diggers.lua:228 -msgid "Digtron Soft Material Digger Head" -msgstr "" - -#: nodes\node_diggers.lua:283 -msgid "Digtron Intermittent Soft Material Digger Head" -msgstr "" - -#: nodes\node_diggers.lua:357 -msgid "Digtron Dual Digger Head" -msgstr "" - -#: nodes\node_diggers.lua:438 -msgid "Digtron Dual Soft Material Digger Head" -msgstr "" - -#: nodes\node_duplicator.lua:10 -msgid "Digtron components" -msgstr "" - -#: nodes\node_duplicator.lua:17 -msgid "Duplicate" -msgstr "" - -#: nodes\node_duplicator.lua:18 -msgid "" -"Puts a copy of the adjacent Digtron into an empty crate\n" -"located at the output side of the duplicator,\n" -"using components from the duplicator's inventory." -msgstr "" - -#: nodes\node_duplicator.lua:27 -msgid "Digtron Duplicator" -msgstr "" - -#: nodes\node_duplicator.lua:99 -msgid "Needs an empty crate in output position to store duplicate" -msgstr "" - -#: nodes\node_duplicator.lua:107 -msgid "Digtron can't be duplicated, it contains protected blocks" -msgstr "" - -#: nodes\node_duplicator.lua:113 -msgid "No Digtron components adjacent to duplicate" -msgstr "" - -#: nodes\node_duplicator.lua:135 -msgid "" -"Duplicator requires:\n" -"@1" -msgstr "" - -#: nodes\node_item_ejector.lua:27 -msgid "Eject into world" -msgstr "" - -#: nodes\node_item_ejector.lua:28 -msgid "When checked, will eject items even if there's no pipe to accept it" -msgstr "" - -#: nodes\node_item_ejector.lua:30 -msgid "Automatic" -msgstr "" - -#: nodes\node_item_ejector.lua:31 -msgid "" -"When checked, will eject items automatically with every Digtron cycle.\n" -"Item ejectors can always be operated manually by punching them." -msgstr "" - -#: nodes\node_item_ejector.lua:112 -msgid "Digtron Inventory Ejector" -msgstr "" - -#: nodes\node_misc.lua:7 -msgid "Digtron Structure" -msgstr "" - -#: nodes\node_misc.lua:40 -msgid "Digtron Light" -msgstr "" - -#: nodes\node_misc.lua:62 -msgid "Digtron Panel" -msgstr "" - -#: nodes\node_misc.lua:85 -msgid "Digtron Edge Panel" -msgstr "" - -#: nodes\node_misc.lua:114 -msgid "Digtron Corner Panel" -msgstr "" - -#: nodes\node_power_connector.lua:15 -msgid "" -"Maximize\n" -"Power" -msgstr "" - -#: nodes\node_power_connector.lua:16 -msgid "" -"Maximum Power\n" -"Required: @1" -msgstr "" - -#: nodes\node_power_connector.lua:17 -msgid "" -"Refresh\n" -"Max" -msgstr "" - -#: nodes\node_power_connector.lua:27 -msgid "Digtron HV Power Connector" -msgstr "" - -#: nodes\node_power_connector.lua:66 -msgid "Digtron Power @1/@2" -msgstr "" - -#: nodes\node_storage.lua:12 -#: nodes\node_storage.lua:192 -msgid "Inventory items" -msgstr "" - -#: nodes\node_storage.lua:27 -msgid "Digtron Inventory Storage" -msgstr "" - -#: nodes\node_storage.lua:93 -#: nodes\node_storage.lua:194 -msgid "Fuel items" -msgstr "" - -#: nodes\node_storage.lua:108 -msgid "Digtron Fuel Storage" -msgstr "" - -#: nodes\node_storage.lua:208 -msgid "Digtron Combined Storage" -msgstr "" - -#: nodes\recipes.lua:6 -msgid "Digtron Core" -msgstr "" diff --git a/locale/template.txt b/locale/template.txt new file mode 100644 index 0000000..08963c5 --- /dev/null +++ b/locale/template.txt @@ -0,0 +1,150 @@ +# textdomain: digtron + + +### controller.lua ### + +@1 left= +Back= +Controls= +Cycles= +Delete= +Dig Move Build= +Dig Move Down= +Digtron Assembly= +Digtron Control Module= +Digtron name= +Disassemble= +Down= +Execute= +Forward= +Fuel= +Insert= +Left= +Main Inventory= +Move= +Move Back= +Move Down= +Move Forward= +Move Left= +Move Right= +Move Up= +New@nCommand= +Page @1/@2= +Pitch Down= +Pitch Up= +Reset= +Right= +Roll Clockwise= +Roll Widdershins= +Roll@nClockwise= +Roll@nWiddershins= +Rotate= +Sequence= +Stop= +Up= +Yaw Left= +Yaw Right= + +### functions.lua ### + +@1 at @2 has encountered an obstacle.= +@1 at @2 requires @3 to execute its next build cycle.= +Copy of @1= +Unnamed Digtron= + +### node_builder.lua ### + +Builder for @1@nperiod @2, offset @3, extrusion @4= + +Builder will build once every n steps.@nThese steps are globally aligned, so all builders with the@nsame period and offset will build on the same location.= + +Builder will extrude this many blocks in the direction it is facing.@nCan be set from 1 to @1.@nNote that Digtron won't build into unloaded map regions.= + +Digtron Builder Module= +Extrusion= +Facing= + +Offsets the start of periodicity counting by this amount.@nFor example, a builder with period 2 and offset 0 builds@nevery even-numbered block and one with period 2 and@noffset 1 builds every odd-numbered block.= + +Read= + +Reads the facing of the block currently in the build location.= + +Saves settings, closes interface, and shows the locations this builder will build to in-world.= + +Value from 0-23. Not all block types make use of this.@nUse the 'Read & Save' button to copy the facing of the block@ncurrently in the builder output location.= + + +### node_builder.lua ### +### node_digger.lua ### + +Offset= +Periodicity= +Save &@nShow= + +### node_builder.lua ### +### node_digger.lua ### +### node_storage.lua ### + +This Digtron is active, interact with it via the controller node.= + + +### node_digger.lua ### + +Digger will dig once every n steps.@nThese steps are globally aligned, all diggers with@nthe same period and offset will dig on the same location.= + +Digger@nperiod @1, offset @2= +Digtron Digger= +Digtron Dual Digger= +Digtron Dual Soft Digger= +Digtron Soft Digger= + +Offsets the start of periodicity counting by this amount.@nFor example, a digger with period 2 and offset 0 digs@nevery even-numbered block and one with period 2 and@noffset 1 digs every odd-numbered block.= + +Saves settings= + +### node_duplicator.lua ### + +Amount of this component currently available.= + +Amount of this component required to copy the template Digtron.= + +Copy= +Digtron Duplicator= +Digtron component.= +Digtron components= + +Digtron components in this inventory will be used to create the duplicate.= + +Duplicate= +Duplication cannot proceed at this time.= +Place the Digtron you want to make a copy of here.= + +Puts a copy of the template Digtron into the output inventory slot.= + +Template= +The duplicate Digtron is output here.= + +### node_misc.lua ### + +Digtron Corner Panel= +Digtron Edge Panel= +Digtron Light= +Digtron Panel= +Digtron Structure= + +### node_pipeworks_interface.lua ### + +Digtron Inventory Interface= + +### node_storage.lua ### + +Digtron Combined Storage= +Digtron Fuel Storage= +Digtron Inventory Storage= +Fuel items= +Inventory items= + +### recipes.lua ### + +Digtron Core= diff --git a/locale/update.bat b/locale/update.bat deleted file mode 100644 index e87d44c..0000000 --- a/locale/update.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION -cd .. -set LIST= -for /r %%X in (*.lua) do set LIST=!LIST! %%X -..\intllib\tools\xgettext.bat %LIST% \ No newline at end of file diff --git a/mod.conf b/mod.conf index 03cac2f..6dd7671 100644 --- a/mod.conf +++ b/mod.conf @@ -4,4 +4,6 @@ author = FaceDeer description = Adds components for building modular tunnel boring machines license = MIT, LGPL 2.1 or later forum = https://forum.minetest.net/viewtopic.php?t=16295 -version = 0.8 \ No newline at end of file +version = 2.0 +depends = default +optional_depends = creative, pipeworks \ No newline at end of file diff --git a/models/digtron_digger.obj b/models/digtron_digger.obj new file mode 100644 index 0000000..061837d --- /dev/null +++ b/models/digtron_digger.obj @@ -0,0 +1,917 @@ +# Blender v2.79 (sub 0) OBJ File: 'digger.blend' +# www.blender.org +g drill_4.003_Cylinder.006 +v 0.500000 -0.500000 0.500000 +v 0.500000 0.500000 0.500000 +v -0.500000 -0.500000 0.500000 +v -0.500000 0.500000 0.500000 +vt 1.000000 -0.000000 +vt 1.000000 1.000000 +vt -0.000000 1.000000 +vt -0.000000 -0.000000 +vn 0.0000 0.0000 1.0000 +s off +f 1/1/1 2/2/1 4/3/1 3/4/1 +g drill_4.002_Cylinder.005 +v 0.500000 -0.500000 0.000000 +v 0.500000 0.500000 0.000000 +v 0.500000 -0.500000 0.500000 +v 0.500000 0.500000 0.500000 +v -0.500000 -0.500000 0.000000 +v -0.500000 0.500000 0.000000 +v -0.500000 -0.500000 0.500000 +v -0.500000 0.500000 0.500000 +v 0.250000 -0.250000 -0.500000 +v 0.250000 0.250000 -0.500000 +v 0.250000 -0.250000 0.000000 +v 0.250000 0.250000 0.000000 +v -0.250000 -0.250000 -0.500000 +v -0.250000 0.250000 -0.500000 +v -0.250000 -0.250000 0.000000 +v -0.250000 0.250000 0.000000 +vt 0.500000 0.000000 +vt 0.500000 1.000000 +vt 0.000000 1.000000 +vt -0.000000 0.000000 +vt 0.000000 1.000000 +vt -0.000000 0.000000 +vt 0.500000 0.000000 +vt 0.500000 1.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vt 0.500000 0.000000 +vt 0.500000 1.000000 +vt 0.000000 1.000000 +vt -0.000000 0.000000 +vt 0.500000 0.000000 +vt 0.500000 1.000000 +vt 1.000000 0.250000 +vt 1.000000 0.750000 +vt 0.500000 0.750000 +vt 0.500000 0.250000 +vt 0.500000 0.750000 +vt 0.500000 0.250000 +vt 1.000000 0.250000 +vt 1.000000 0.750000 +vt 0.500000 0.750000 +vt 0.500000 0.250000 +vt 1.000000 0.250000 +vt 1.000000 0.750000 +vt 0.500000 0.750000 +vt 0.500000 0.250000 +vt 1.000000 0.250000 +vt 1.000000 0.750000 +vn 1.0000 0.0000 0.0000 +vn -1.0000 0.0000 0.0000 +vn -0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +s off +f 5/5/2 6/6/2 8/7/2 7/8/2 +f 11/9/3 12/10/3 10/11/3 9/12/3 +f 7/13/4 11/14/4 9/15/4 5/16/4 +f 12/17/5 8/18/5 6/19/5 10/20/5 +f 13/21/2 14/22/2 16/23/2 15/24/2 +f 19/25/3 20/26/3 18/27/3 17/28/3 +f 15/29/4 19/30/4 17/31/4 13/32/4 +f 20/33/5 16/34/5 14/35/5 18/36/5 +g drill_4.001_Cylinder.004 +v 0.416156 -0.138553 0.562500 +v -0.392238 0.196295 0.562500 +v 0.416156 -0.138553 0.625000 +v -0.392238 0.196295 0.625000 +v 0.392238 -0.196295 0.562500 +v -0.416156 0.138553 0.562500 +v 0.392238 -0.196295 0.625000 +v -0.416156 0.138553 0.625000 +v 0.138553 0.416156 0.562500 +v -0.196295 -0.392238 0.562500 +v 0.138553 0.416156 0.625000 +v -0.196295 -0.392238 0.625000 +v 0.196295 0.392238 0.562500 +v -0.138553 -0.416156 0.562500 +v 0.196295 0.392238 0.625000 +v -0.138553 -0.416156 0.625000 +v 0.218593 0.124369 0.812153 +v -0.242511 -0.066627 0.812153 +v 0.218593 0.124369 0.874653 +v -0.242511 -0.066627 0.874653 +v 0.242511 0.066627 0.812153 +v -0.218593 -0.124369 0.812153 +v 0.242511 0.066627 0.874653 +v -0.218593 -0.124369 0.874653 +v -0.124493 0.218892 0.812153 +v 0.066751 -0.242810 0.812153 +v -0.124493 0.218892 0.874653 +v 0.066751 -0.242810 0.874653 +v -0.066751 0.242810 0.812153 +v 0.124493 -0.218892 0.812153 +v -0.066751 0.242810 0.874653 +v 0.124493 -0.218892 0.874653 +v -0.057742 0.023918 1.000000 +v -0.461940 0.191342 0.500000 +v -0.057742 -0.023918 1.000000 +v -0.461940 -0.191342 0.500000 +v -0.023918 -0.057742 1.000000 +v -0.191342 -0.461940 0.500000 +v 0.023918 -0.057742 1.000000 +v 0.191342 -0.461940 0.500000 +v 0.057742 -0.023918 1.000000 +v 0.461940 -0.191342 0.500000 +v 0.057742 0.023918 1.000000 +v 0.461940 0.191342 0.500000 +v 0.023918 0.057742 1.000000 +v 0.191342 0.461940 0.500000 +v -0.023918 0.057742 1.000000 +v -0.191342 0.461940 0.500000 +v 0.331456 -0.287262 0.562500 +v -0.287262 0.331456 0.562500 +v 0.331456 -0.287262 0.625000 +v -0.287262 0.331456 0.625000 +v 0.287262 -0.331456 0.562500 +v -0.331456 0.287262 0.562500 +v 0.287262 -0.331456 0.625000 +v -0.331456 0.287262 0.625000 +v 0.287262 0.331456 0.562500 +v -0.331456 -0.287262 0.562500 +v 0.287262 0.331456 0.625000 +v -0.331456 -0.287262 0.625000 +v 0.331456 0.287262 0.562500 +v -0.287262 -0.331456 0.562500 +v 0.331456 0.287262 0.625000 +v -0.287262 -0.331456 0.625000 +v 0.249548 0.031250 0.812153 +v -0.249548 0.031250 0.812153 +v 0.249548 0.031250 0.874653 +v -0.249548 0.031250 0.874653 +v 0.249548 -0.031250 0.812153 +v -0.249548 -0.031250 0.812153 +v 0.249548 -0.031250 0.874653 +v -0.249548 -0.031250 0.874653 +v -0.031250 0.249872 0.812153 +v -0.031250 -0.249872 0.812153 +v -0.031250 0.249872 0.874653 +v -0.031250 -0.249872 0.874653 +v 0.031250 0.249872 0.812153 +v 0.031250 -0.249872 0.812153 +v 0.031250 0.249872 0.874653 +v 0.031250 -0.249872 0.874653 +v -0.044194 0.044194 1.000000 +v -0.353553 0.353553 0.500000 +v -0.062500 0.000000 1.000000 +v -0.500000 0.000000 0.500000 +v -0.044194 -0.044194 1.000000 +v -0.353553 -0.353553 0.500000 +v 0.000000 -0.062500 1.000000 +v 0.000000 -0.500000 0.500000 +v 0.044194 -0.044194 1.000000 +v 0.353553 -0.353553 0.500000 +v 0.062500 0.000000 1.000000 +v 0.500000 0.000000 0.500000 +v 0.044194 0.044194 1.000000 +v 0.353553 0.353553 0.500000 +v 0.000000 0.062500 1.000000 +v 0.000000 0.500000 0.500000 +v 0.196295 -0.392238 0.562500 +v -0.138553 0.416156 0.562500 +v 0.196295 -0.392238 0.625000 +v -0.138553 0.416156 0.625000 +v 0.138553 -0.416156 0.562500 +v -0.196295 0.392238 0.562500 +v 0.138553 -0.416156 0.625000 +v -0.196295 0.392238 0.625000 +v 0.392238 0.196295 0.562500 +v -0.416156 -0.138553 0.562500 +v 0.392238 0.196295 0.625000 +v -0.416156 -0.138553 0.625000 +v 0.416156 0.138553 0.562500 +v -0.392238 -0.196295 0.562500 +v 0.416156 0.138553 0.625000 +v -0.392238 -0.196295 0.625000 +v 0.242511 -0.066627 0.812153 +v -0.218593 0.124369 0.812153 +v 0.242511 -0.066627 0.874653 +v -0.218593 0.124369 0.874653 +v 0.218593 -0.124369 0.812153 +v -0.242511 0.066627 0.812153 +v 0.218593 -0.124369 0.874653 +v -0.242511 0.066627 0.874653 +v 0.066750 0.242810 0.812153 +v -0.124493 -0.218892 0.812153 +v 0.066750 0.242810 0.874653 +v -0.124493 -0.218892 0.874653 +v 0.124493 0.218892 0.812153 +v -0.066750 -0.242810 0.812153 +v 0.124493 0.218892 0.874653 +v -0.066750 -0.242810 0.874653 +v -0.023918 0.057742 1.000000 +v -0.191342 0.461940 0.500000 +v -0.057742 0.023918 1.000000 +v -0.461940 0.191342 0.500000 +v -0.057742 -0.023918 1.000000 +v -0.461940 -0.191342 0.500000 +v -0.023918 -0.057742 1.000000 +v -0.191342 -0.461940 0.500000 +v 0.023918 -0.057742 1.000000 +v 0.191342 -0.461940 0.500000 +v 0.057742 -0.023918 1.000000 +v 0.461940 -0.191342 0.500000 +v 0.057743 0.023918 1.000000 +v 0.461940 0.191342 0.500000 +v 0.023918 0.057742 1.000000 +v 0.191342 0.461940 0.500000 +v 0.031250 -0.437500 0.562500 +v 0.031250 0.437500 0.562500 +v 0.031250 -0.437500 0.625000 +v 0.031250 0.437500 0.625000 +v -0.031250 -0.437500 0.562500 +v -0.031250 0.437500 0.562500 +v -0.031250 -0.437500 0.625000 +v -0.031250 0.437500 0.625000 +v 0.437500 0.031250 0.562500 +v -0.437500 0.031250 0.562500 +v 0.437500 0.031250 0.625000 +v -0.437500 0.031250 0.625000 +v 0.437500 -0.031250 0.562500 +v -0.437500 -0.031250 0.562500 +v 0.437500 -0.031250 0.625000 +v -0.437500 -0.031250 0.625000 +v 0.198554 -0.154360 0.812153 +v -0.154360 0.198554 0.812153 +v 0.198554 -0.154360 0.874653 +v -0.154360 0.198554 0.874653 +v 0.154360 -0.198554 0.812153 +v -0.198554 0.154360 0.812153 +v 0.154360 -0.198554 0.874653 +v -0.198554 0.154360 0.874653 +v 0.154589 0.198783 0.812153 +v -0.198783 -0.154589 0.812153 +v 0.154589 0.198783 0.874653 +v -0.198783 -0.154589 0.874653 +v 0.198783 0.154589 0.812153 +v -0.154589 -0.198783 0.812153 +v 0.198783 0.154589 0.874653 +v -0.154589 -0.198783 0.874653 +v 0.000000 0.062500 1.000000 +v 0.000000 0.500000 0.500000 +v -0.044194 0.044194 1.000000 +v -0.353553 0.353553 0.500000 +v -0.062500 0.000000 1.000000 +v -0.500000 -0.000000 0.500000 +v -0.044194 -0.044194 1.000000 +v -0.353553 -0.353553 0.500000 +v 0.000000 -0.062500 1.000000 +v 0.000000 -0.500000 0.500000 +v 0.044194 -0.044194 1.000000 +v 0.353553 -0.353553 0.500000 +v 0.062500 0.000000 1.000000 +v 0.500000 -0.000000 0.500000 +v 0.044194 0.044194 1.000000 +v 0.353553 0.353553 0.500000 +vt 0.769326 -0.005472 +vt 0.769326 0.993770 +vt 0.751482 0.993770 +vt 0.751482 -0.005472 +vt 0.769326 -0.005472 +vt 0.769326 0.993771 +vt 0.751482 0.993771 +vt 0.751482 -0.005472 +vt 0.769326 -0.005472 +vt 0.769326 0.993770 +vt 0.751482 0.993770 +vt 0.751482 -0.005472 +vt 0.769238 0.000130 +vt 0.769238 0.071504 +vt 0.751394 0.071504 +vt 0.751394 0.000130 +vt 0.769238 0.000130 +vt 0.769238 0.071504 +vt 0.751394 0.071504 +vt 0.751394 0.000130 +vt 0.751482 0.993770 +vt 0.751482 -0.005472 +vt 0.769326 -0.005472 +vt 0.769326 0.993770 +vt 0.751482 0.993771 +vt 0.751482 -0.005473 +vt 0.769326 -0.005473 +vt 0.769326 0.993771 +vt 0.751482 0.993770 +vt 0.751482 -0.005472 +vt 0.769326 -0.005472 +vt 0.769326 0.993770 +vt 0.769238 0.000130 +vt 0.769238 0.071504 +vt 0.751394 0.071504 +vt 0.751394 0.000130 +vt 0.769238 0.000130 +vt 0.769238 0.071504 +vt 0.751394 0.071504 +vt 0.751394 0.000130 +vt 0.768747 -0.002751 +vt 0.768747 0.567212 +vt 0.750904 0.567212 +vt 0.750904 -0.002751 +vt 0.750904 0.567212 +vt 0.750904 -0.002751 +vt 0.768747 -0.002751 +vt 0.768747 0.567212 +vt 0.750904 0.567212 +vt 0.750904 -0.002751 +vt 0.768747 -0.002751 +vt 0.768747 0.567212 +vt 0.750904 0.567212 +vt 0.750904 -0.002751 +vt 0.768747 -0.002751 +vt 0.768747 0.567212 +vt 0.769238 0.000130 +vt 0.769238 0.071504 +vt 0.751394 0.071504 +vt 0.751394 0.000130 +vt 0.769238 0.000130 +vt 0.769238 0.071504 +vt 0.751394 0.071504 +vt 0.751394 0.000130 +vt 0.750904 0.567582 +vt 0.750904 -0.003121 +vt 0.768747 -0.003121 +vt 0.768747 0.567582 +vt 0.750904 0.567582 +vt 0.750904 -0.003121 +vt 0.768747 -0.003121 +vt 0.768747 0.567582 +vt 0.750904 0.567582 +vt 0.750904 -0.003121 +vt 0.768747 -0.003121 +vt 0.768747 0.567582 +vt 0.750904 0.567582 +vt 0.750904 -0.003121 +vt 0.768747 -0.003121 +vt 0.768747 0.567582 +vt 0.769238 0.000130 +vt 0.769238 0.071504 +vt 0.751394 0.071504 +vt 0.751394 0.000130 +vt 0.769238 0.000130 +vt 0.769238 0.071504 +vt 0.751394 0.071504 +vt 0.751394 0.000130 +vt 0.832670 0.163342 +vt 0.978052 0.163342 +vt 0.929387 0.633286 +vt 0.826587 0.222085 +vt 0.811901 0.827944 +vt 0.811901 0.246417 +vt 0.832670 0.163343 +vt 0.978052 0.163342 +vt 0.929387 0.633287 +vt 0.826587 0.222086 +vt 0.811901 0.827944 +vt 0.811901 0.246418 +vt 0.832670 0.163342 +vt 0.978052 0.163342 +vt 0.929387 0.633287 +vt 0.826587 0.222085 +vt 0.811901 0.827944 +vt 0.811901 0.246417 +vt 0.832670 0.163341 +vt 0.978052 0.163342 +vt 0.929387 0.633286 +vt 0.826587 0.222084 +vt 0.811901 0.827944 +vt 0.811901 0.246417 +vt 0.797216 0.222085 +vt 0.791132 0.163341 +vt 0.797216 0.104598 +vt 0.811901 0.080266 +vt 0.826587 0.104598 +vt 0.519326 -0.005472 +vt 0.519326 0.993770 +vt 0.501482 0.993770 +vt 0.501482 -0.005472 +vt 0.519326 -0.005472 +vt 0.519326 0.993771 +vt 0.501482 0.993771 +vt 0.501482 -0.005472 +vt 0.519326 -0.005472 +vt 0.519326 0.993770 +vt 0.501482 0.993770 +vt 0.501482 -0.005472 +vt 0.519238 0.000130 +vt 0.519238 0.071504 +vt 0.501394 0.071504 +vt 0.501394 0.000130 +vt 0.519238 0.000130 +vt 0.519238 0.071504 +vt 0.501394 0.071504 +vt 0.501394 0.000130 +vt 0.501482 0.993770 +vt 0.501482 -0.005472 +vt 0.519326 -0.005472 +vt 0.519326 0.993770 +vt 0.501482 0.993771 +vt 0.501482 -0.005473 +vt 0.519326 -0.005473 +vt 0.519326 0.993771 +vt 0.501482 0.993770 +vt 0.501482 -0.005472 +vt 0.519326 -0.005472 +vt 0.519326 0.993770 +vt 0.519238 0.000130 +vt 0.519238 0.071504 +vt 0.501394 0.071504 +vt 0.501394 0.000130 +vt 0.519238 0.000130 +vt 0.519238 0.071504 +vt 0.501394 0.071504 +vt 0.501394 0.000130 +vt 0.518747 -0.002751 +vt 0.518747 0.567212 +vt 0.500904 0.567212 +vt 0.500904 -0.002751 +vt 0.500904 0.567212 +vt 0.500904 -0.002751 +vt 0.518747 -0.002751 +vt 0.518747 0.567212 +vt 0.500904 0.567212 +vt 0.500904 -0.002751 +vt 0.518747 -0.002751 +vt 0.518747 0.567212 +vt 0.500904 0.567212 +vt 0.500904 -0.002751 +vt 0.518747 -0.002751 +vt 0.518747 0.567212 +vt 0.519238 0.000130 +vt 0.519238 0.071504 +vt 0.501394 0.071504 +vt 0.501394 0.000130 +vt 0.519238 0.000130 +vt 0.519238 0.071504 +vt 0.501394 0.071504 +vt 0.501394 0.000130 +vt 0.500904 0.567582 +vt 0.500904 -0.003121 +vt 0.518747 -0.003121 +vt 0.518747 0.567582 +vt 0.500904 0.567582 +vt 0.500904 -0.003121 +vt 0.518747 -0.003121 +vt 0.518747 0.567582 +vt 0.500904 0.567582 +vt 0.500904 -0.003121 +vt 0.518747 -0.003121 +vt 0.518747 0.567582 +vt 0.500904 0.567582 +vt 0.500904 -0.003121 +vt 0.518747 -0.003121 +vt 0.518747 0.567582 +vt 0.519238 0.000130 +vt 0.519238 0.071504 +vt 0.501394 0.071504 +vt 0.501394 0.000130 +vt 0.519238 0.000130 +vt 0.519238 0.071504 +vt 0.501394 0.071504 +vt 0.501394 0.000130 +vt 0.582670 0.163342 +vt 0.728052 0.163342 +vt 0.679387 0.633286 +vt 0.576587 0.222085 +vt 0.561901 0.827944 +vt 0.561901 0.246417 +vt 0.582670 0.163343 +vt 0.728052 0.163342 +vt 0.679387 0.633287 +vt 0.576587 0.222086 +vt 0.561901 0.827944 +vt 0.561901 0.246418 +vt 0.582670 0.163342 +vt 0.728052 0.163342 +vt 0.679387 0.633287 +vt 0.576587 0.222085 +vt 0.561901 0.827944 +vt 0.561901 0.246417 +vt 0.582670 0.163341 +vt 0.728052 0.163342 +vt 0.679387 0.633286 +vt 0.576587 0.222084 +vt 0.561901 0.827944 +vt 0.561901 0.246417 +vt 0.547216 0.222085 +vt 0.541132 0.163341 +vt 0.547216 0.104598 +vt 0.561901 0.080266 +vt 0.576587 0.104598 +vt 0.269326 -0.005472 +vt 0.269326 0.993770 +vt 0.251482 0.993770 +vt 0.251482 -0.005472 +vt 0.269326 -0.005472 +vt 0.269326 0.993770 +vt 0.251482 0.993770 +vt 0.251482 -0.005472 +vt 0.269326 -0.005472 +vt 0.269326 0.993770 +vt 0.251482 0.993770 +vt 0.251482 -0.005472 +vt 0.269238 0.000130 +vt 0.269238 0.071504 +vt 0.251394 0.071504 +vt 0.251394 0.000130 +vt 0.269238 0.000130 +vt 0.269238 0.071504 +vt 0.251394 0.071504 +vt 0.251394 0.000130 +vt 0.251482 0.993770 +vt 0.251482 -0.005472 +vt 0.269326 -0.005472 +vt 0.269326 0.993770 +vt 0.251482 0.993770 +vt 0.251482 -0.005472 +vt 0.269326 -0.005472 +vt 0.269326 0.993770 +vt 0.251482 0.993770 +vt 0.251482 -0.005472 +vt 0.269326 -0.005472 +vt 0.269326 0.993770 +vt 0.269238 0.000130 +vt 0.269238 0.071504 +vt 0.251394 0.071504 +vt 0.251394 0.000130 +vt 0.269238 0.000130 +vt 0.269238 0.071504 +vt 0.251394 0.071504 +vt 0.251394 0.000130 +vt 0.268747 -0.002751 +vt 0.268747 0.567212 +vt 0.250904 0.567212 +vt 0.250904 -0.002751 +vt 0.250904 0.567212 +vt 0.250904 -0.002751 +vt 0.268747 -0.002751 +vt 0.268747 0.567212 +vt 0.250904 0.567212 +vt 0.250904 -0.002750 +vt 0.268747 -0.002751 +vt 0.268747 0.567212 +vt 0.250904 0.567212 +vt 0.250904 -0.002751 +vt 0.268747 -0.002751 +vt 0.268747 0.567212 +vt 0.269238 0.000130 +vt 0.269238 0.071504 +vt 0.251394 0.071504 +vt 0.251394 0.000130 +vt 0.269238 0.000130 +vt 0.269238 0.071504 +vt 0.251394 0.071504 +vt 0.251394 0.000130 +vt 0.250904 0.567582 +vt 0.250904 -0.003121 +vt 0.268747 -0.003121 +vt 0.268747 0.567582 +vt 0.250904 0.567582 +vt 0.250904 -0.003121 +vt 0.268747 -0.003121 +vt 0.268747 0.567582 +vt 0.250904 0.567582 +vt 0.250904 -0.003121 +vt 0.268747 -0.003121 +vt 0.268747 0.567582 +vt 0.250904 0.567582 +vt 0.250904 -0.003121 +vt 0.268747 -0.003121 +vt 0.268747 0.567582 +vt 0.269238 0.000130 +vt 0.269238 0.071504 +vt 0.251394 0.071504 +vt 0.251394 0.000130 +vt 0.269238 0.000130 +vt 0.269238 0.071504 +vt 0.251394 0.071504 +vt 0.251394 0.000130 +vt 0.332670 0.163342 +vt 0.478052 0.163342 +vt 0.429387 0.633286 +vt 0.326587 0.222085 +vt 0.311901 0.827944 +vt 0.311901 0.246417 +vt 0.332670 0.163343 +vt 0.478052 0.163342 +vt 0.429387 0.633287 +vt 0.326587 0.222086 +vt 0.311901 0.827944 +vt 0.311901 0.246418 +vt 0.332670 0.163342 +vt 0.478052 0.163342 +vt 0.429387 0.633287 +vt 0.326587 0.222085 +vt 0.311901 0.827944 +vt 0.311901 0.246417 +vt 0.332670 0.163341 +vt 0.478052 0.163342 +vt 0.429387 0.633286 +vt 0.326587 0.222085 +vt 0.311901 0.827944 +vt 0.311901 0.246417 +vt 0.297215 0.222085 +vt 0.291132 0.163342 +vt 0.297215 0.104598 +vt 0.311901 0.080266 +vt 0.326587 0.104598 +vt 0.019326 -0.005472 +vt 0.019326 0.993770 +vt 0.001482 0.993770 +vt 0.001482 -0.005472 +vt 0.019326 -0.005472 +vt 0.019326 0.993770 +vt 0.001482 0.993770 +vt 0.001482 -0.005472 +vt 0.019326 -0.005472 +vt 0.019326 0.993770 +vt 0.001482 0.993770 +vt 0.001482 -0.005472 +vt 0.019238 0.000130 +vt 0.019238 0.071505 +vt 0.001394 0.071504 +vt 0.001394 0.000130 +vt 0.019238 0.000130 +vt 0.019238 0.071504 +vt 0.001394 0.071504 +vt 0.001394 0.000130 +vt 0.001482 0.993770 +vt 0.001482 -0.005472 +vt 0.019326 -0.005472 +vt 0.019326 0.993770 +vt 0.001482 0.993770 +vt 0.001482 -0.005472 +vt 0.019326 -0.005472 +vt 0.019326 0.993770 +vt 0.001482 0.993770 +vt 0.001482 -0.005472 +vt 0.019326 -0.005472 +vt 0.019326 0.993770 +vt 0.019238 0.000130 +vt 0.019238 0.071504 +vt 0.001394 0.071504 +vt 0.001394 0.000130 +vt 0.019238 0.000130 +vt 0.019238 0.071504 +vt 0.001394 0.071504 +vt 0.001394 0.000130 +vt 0.018747 -0.002751 +vt 0.018747 0.567212 +vt 0.000904 0.567212 +vt 0.000904 -0.002751 +vt 0.000904 0.567212 +vt 0.000904 -0.002751 +vt 0.018747 -0.002751 +vt 0.018747 0.567212 +vt 0.000904 0.567212 +vt 0.000904 -0.002750 +vt 0.018747 -0.002751 +vt 0.018747 0.567212 +vt 0.000904 0.567212 +vt 0.000904 -0.002751 +vt 0.018747 -0.002751 +vt 0.018747 0.567212 +vt 0.019238 0.000130 +vt 0.019238 0.071504 +vt 0.001394 0.071504 +vt 0.001394 0.000130 +vt 0.019238 0.000130 +vt 0.019238 0.071504 +vt 0.001394 0.071504 +vt 0.001394 0.000130 +vt 0.000904 0.567582 +vt 0.000904 -0.003121 +vt 0.018747 -0.003121 +vt 0.018747 0.567582 +vt 0.000904 0.567582 +vt 0.000904 -0.003121 +vt 0.018747 -0.003121 +vt 0.018747 0.567582 +vt 0.000904 0.567582 +vt 0.000904 -0.003121 +vt 0.018747 -0.003121 +vt 0.018747 0.567582 +vt 0.000904 0.567582 +vt 0.000904 -0.003121 +vt 0.018747 -0.003121 +vt 0.018747 0.567582 +vt 0.019238 0.000130 +vt 0.019238 0.071504 +vt 0.001394 0.071504 +vt 0.001394 0.000130 +vt 0.019238 0.000130 +vt 0.019238 0.071505 +vt 0.001394 0.071504 +vt 0.001394 0.000130 +vt 0.082670 0.163342 +vt 0.228052 0.163342 +vt 0.179387 0.633286 +vt 0.076587 0.222085 +vt 0.061901 0.827944 +vt 0.061901 0.246417 +vt 0.082670 0.163343 +vt 0.228052 0.163342 +vt 0.179387 0.633287 +vt 0.076587 0.222086 +vt 0.061901 0.827944 +vt 0.061901 0.246418 +vt 0.082670 0.163342 +vt 0.228052 0.163342 +vt 0.179387 0.633287 +vt 0.076587 0.222085 +vt 0.061901 0.827944 +vt 0.061901 0.246417 +vt 0.082670 0.163341 +vt 0.228052 0.163342 +vt 0.179387 0.633286 +vt 0.076587 0.222085 +vt 0.061901 0.827944 +vt 0.061901 0.246417 +vt 0.047215 0.222085 +vt 0.041132 0.163342 +vt 0.047215 0.104598 +vt 0.061901 0.080266 +vt 0.076587 0.104598 +vn 0.3827 0.9239 0.0000 +vn 0.0000 0.0000 1.0000 +vn -0.3827 -0.9239 0.0000 +vn 0.9239 -0.3827 0.0000 +vn -0.9239 0.3827 0.0000 +vn -0.3827 0.9239 0.0000 +vn 0.3827 -0.9239 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 0.9239 0.3827 0.0000 +vn -0.9239 -0.3827 0.0000 +vn -0.7777 -0.0000 0.6287 +vn -0.5499 -0.5499 0.6287 +vn 0.0000 -0.7777 0.6287 +vn 0.5499 -0.5499 0.6287 +vn 0.7777 0.0000 0.6287 +vn 0.5499 0.5499 0.6287 +vn 0.0000 0.7777 0.6287 +vn -0.5499 0.5499 0.6287 +vn 0.7071 0.7071 0.0000 +vn -0.7071 -0.7071 0.0000 +vn 0.7071 -0.7071 0.0000 +vn -0.7071 0.7071 0.0000 +vn 0.0000 1.0000 0.0000 +vn -0.0000 -1.0000 0.0000 +vn 1.0000 0.0000 0.0000 +vn -1.0000 0.0000 0.0000 +vn -0.7185 0.2976 0.6287 +vn -0.7185 -0.2976 0.6287 +vn -0.2976 -0.7185 0.6287 +vn 0.2976 -0.7185 0.6287 +vn 0.7185 -0.2976 0.6287 +vn 0.7185 0.2976 0.6287 +vn 0.2976 0.7185 0.6287 +vn -0.2976 0.7185 0.6287 +s off +f 21/37/6 22/38/6 24/39/6 23/40/6 +f 23/41/7 24/42/7 28/43/7 27/44/7 +f 27/45/8 28/46/8 26/47/8 25/48/8 +f 23/49/9 27/50/9 25/51/9 21/52/9 +f 28/53/10 24/54/10 22/55/10 26/56/10 +f 29/57/10 30/58/10 32/59/10 31/60/10 +f 31/61/7 32/62/7 36/63/7 35/64/7 +f 35/65/9 36/66/9 34/67/9 33/68/9 +f 31/69/6 35/70/6 33/71/6 29/72/6 +f 36/73/8 32/74/8 30/75/8 34/76/8 +f 37/77/11 38/78/11 40/79/11 39/80/11 +f 39/81/7 40/82/7 44/83/7 43/84/7 +f 43/85/12 44/86/12 42/87/12 41/88/12 +f 41/89/13 42/90/13 38/91/13 37/92/13 +f 39/93/14 43/94/14 41/95/14 37/96/14 +f 44/97/15 40/98/15 38/99/15 42/100/15 +f 45/101/15 46/102/15 48/103/15 47/104/15 +f 47/105/7 48/106/7 52/107/7 51/108/7 +f 51/109/14 52/110/14 50/111/14 49/112/14 +f 49/113/13 50/114/13 46/115/13 45/116/13 +f 47/117/11 51/118/11 49/119/11 45/120/11 +f 52/121/12 48/122/12 46/123/12 50/124/12 +f 53/125/16 54/126/16 56/127/16 55/128/16 +f 55/128/17 56/127/17 58/129/17 57/130/17 +f 57/131/18 58/132/18 60/133/18 59/134/18 +f 59/134/19 60/133/19 62/135/19 61/136/19 +f 61/137/20 62/138/20 64/139/20 63/140/20 +f 63/140/21 64/139/21 66/141/21 65/142/21 +f 65/143/22 66/144/22 68/145/22 67/146/22 +f 67/146/23 68/145/23 54/147/23 53/148/23 +f 53/148/7 55/149/7 57/150/7 59/151/7 61/152/7 63/153/7 65/143/7 67/146/7 +f 69/154/24 70/155/24 72/156/24 71/157/24 +f 71/158/7 72/159/7 76/160/7 75/161/7 +f 75/162/25 76/163/25 74/164/25 73/165/25 +f 71/166/26 75/167/26 73/168/26 69/169/26 +f 76/170/27 72/171/27 70/172/27 74/173/27 +f 77/174/27 78/175/27 80/176/27 79/177/27 +f 79/178/7 80/179/7 84/180/7 83/181/7 +f 83/182/26 84/183/26 82/184/26 81/185/26 +f 79/186/24 83/187/24 81/188/24 77/189/24 +f 84/190/25 80/191/25 78/192/25 82/193/25 +f 85/194/28 86/195/28 88/196/28 87/197/28 +f 87/198/7 88/199/7 92/200/7 91/201/7 +f 91/202/29 92/203/29 90/204/29 89/205/29 +f 89/206/13 90/207/13 86/208/13 85/209/13 +f 87/210/30 91/211/30 89/212/30 85/213/30 +f 92/214/31 88/215/31 86/216/31 90/217/31 +f 93/218/31 94/219/31 96/220/31 95/221/31 +f 95/222/7 96/223/7 100/224/7 99/225/7 +f 99/226/30 100/227/30 98/228/30 97/229/30 +f 97/230/13 98/231/13 94/232/13 93/233/13 +f 95/234/28 99/235/28 97/236/28 93/237/28 +f 100/238/29 96/239/29 94/240/29 98/241/29 +f 101/242/32 102/243/32 104/244/32 103/245/32 +f 103/245/33 104/244/33 106/246/33 105/247/33 +f 105/248/34 106/249/34 108/250/34 107/251/34 +f 107/251/35 108/250/35 110/252/35 109/253/35 +f 109/254/36 110/255/36 112/256/36 111/257/36 +f 111/257/37 112/256/37 114/258/37 113/259/37 +f 113/260/38 114/261/38 116/262/38 115/263/38 +f 115/263/39 116/262/39 102/264/39 101/265/39 +f 101/265/7 103/266/7 105/267/7 107/268/7 109/269/7 111/270/7 113/260/7 115/263/7 +f 117/271/14 118/272/14 120/273/14 119/274/14 +f 119/275/7 120/276/7 124/277/7 123/278/7 +f 123/279/15 124/280/15 122/281/15 121/282/15 +f 119/283/12 123/284/12 121/285/12 117/286/12 +f 124/287/11 120/288/11 118/289/11 122/290/11 +f 125/291/11 126/292/11 128/293/11 127/294/11 +f 127/295/7 128/296/7 132/297/7 131/298/7 +f 131/299/12 132/300/12 130/301/12 129/302/12 +f 127/303/14 131/304/14 129/305/14 125/306/14 +f 132/307/15 128/308/15 126/309/15 130/310/15 +f 133/311/6 134/312/6 136/313/6 135/314/6 +f 135/315/7 136/316/7 140/317/7 139/318/7 +f 139/319/8 140/320/8 138/321/8 137/322/8 +f 137/323/13 138/324/13 134/325/13 133/326/13 +f 135/327/9 139/328/9 137/329/9 133/330/9 +f 140/331/10 136/332/10 134/333/10 138/334/10 +f 141/335/10 142/336/10 144/337/10 143/338/10 +f 143/339/7 144/340/7 148/341/7 147/342/7 +f 147/343/9 148/344/9 146/345/9 145/346/9 +f 145/347/13 146/348/13 142/349/13 141/350/13 +f 143/351/6 147/352/6 145/353/6 141/354/6 +f 148/355/8 144/356/8 142/357/8 146/358/8 +f 149/359/23 150/360/23 152/361/23 151/362/23 +f 151/362/16 152/361/16 154/363/16 153/364/16 +f 153/365/17 154/366/17 156/367/17 155/368/17 +f 155/368/18 156/367/18 158/369/18 157/370/18 +f 157/371/19 158/372/19 160/373/19 159/374/19 +f 159/374/20 160/373/20 162/375/20 161/376/20 +f 161/377/21 162/378/21 164/379/21 163/380/21 +f 163/380/22 164/379/22 150/381/22 149/382/22 +f 149/382/7 151/383/7 153/384/7 155/385/7 157/386/7 159/387/7 161/377/7 163/380/7 +f 165/388/30 166/389/30 168/390/30 167/391/30 +f 167/392/7 168/393/7 172/394/7 171/395/7 +f 171/396/31 172/397/31 170/398/31 169/399/31 +f 167/400/29 171/401/29 169/402/29 165/403/29 +f 172/404/28 168/405/28 166/406/28 170/407/28 +f 173/408/28 174/409/28 176/410/28 175/411/28 +f 175/412/7 176/413/7 180/414/7 179/415/7 +f 179/416/29 180/417/29 178/418/29 177/419/29 +f 175/420/30 179/421/30 177/422/30 173/423/30 +f 180/424/31 176/425/31 174/426/31 178/427/31 +f 181/428/24 182/429/24 184/430/24 183/431/24 +f 183/432/7 184/433/7 188/434/7 187/435/7 +f 187/436/25 188/437/25 186/438/25 185/439/25 +f 185/440/13 186/441/13 182/442/13 181/443/13 +f 183/444/26 187/445/26 185/446/26 181/447/26 +f 188/448/27 184/449/27 182/450/27 186/451/27 +f 189/452/27 190/453/27 192/454/27 191/455/27 +f 191/456/7 192/457/7 196/458/7 195/459/7 +f 195/460/26 196/461/26 194/462/26 193/463/26 +f 193/464/13 194/465/13 190/466/13 189/467/13 +f 191/468/24 195/469/24 193/470/24 189/471/24 +f 196/472/25 192/473/25 190/474/25 194/475/25 +f 197/476/39 198/477/39 200/478/39 199/479/39 +f 199/479/32 200/478/32 202/480/32 201/481/32 +f 201/482/33 202/483/33 204/484/33 203/485/33 +f 203/485/34 204/484/34 206/486/34 205/487/34 +f 205/488/35 206/489/35 208/490/35 207/491/35 +f 207/491/36 208/490/36 210/492/36 209/493/36 +f 209/494/37 210/495/37 212/496/37 211/497/37 +f 211/497/38 212/496/38 198/498/38 197/499/38 +f 197/499/7 199/500/7 201/501/7 203/502/7 205/503/7 207/504/7 209/494/7 211/497/7 +g drill_4_Cylinder.003 +v 0.500000 -0.500000 0.000000 +v 0.500000 0.500000 0.000000 +v -0.500000 -0.500000 0.000000 +v -0.500000 0.500000 0.000000 +v 0.250000 -0.250000 -0.500000 +v 0.250000 0.250000 -0.500000 +v -0.250000 -0.250000 -0.500000 +v -0.250000 0.250000 -0.500000 +vt 1.000000 -0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt -0.000000 0.000000 +vt 0.750000 0.250000 +vt 0.750000 0.750000 +vt 0.250000 0.750000 +vt 0.250000 0.250000 +vn 0.0000 0.0000 -1.0000 +s off +f 215/505/40 216/506/40 214/507/40 213/508/40 +f 219/509/40 220/510/40 218/511/40 217/512/40 diff --git a/models/digtron_digger_static.obj b/models/digtron_digger_static.obj new file mode 100644 index 0000000..5dba0bd --- /dev/null +++ b/models/digtron_digger_static.obj @@ -0,0 +1,313 @@ +# Blender v2.79 (sub 0) OBJ File: 'digger.blend' +# www.blender.org +g drill_4.003_Cylinder.006 +v 0.500000 -0.500000 0.500000 +v 0.500000 0.500000 0.500000 +v -0.500000 -0.500000 0.500000 +v -0.500000 0.500000 0.500000 +vt 1.000000 -0.000000 +vt 1.000000 1.000000 +vt -0.000000 1.000000 +vt -0.000000 -0.000000 +vn 0.0000 0.0000 1.0000 +s off +f 1/1/1 2/2/1 4/3/1 3/4/1 +g drill_4.002_Cylinder.005 +v 0.500000 -0.500000 0.000000 +v 0.500000 0.500000 0.000000 +v 0.500000 -0.500000 0.500000 +v 0.500000 0.500000 0.500000 +v -0.500000 -0.500000 0.000000 +v -0.500000 0.500000 0.000000 +v -0.500000 -0.500000 0.500000 +v -0.500000 0.500000 0.500000 +v 0.250000 -0.250000 -0.500000 +v 0.250000 0.250000 -0.500000 +v 0.250000 -0.250000 0.000000 +v 0.250000 0.250000 0.000000 +v -0.250000 -0.250000 -0.500000 +v -0.250000 0.250000 -0.500000 +v -0.250000 -0.250000 0.000000 +v -0.250000 0.250000 0.000000 +vt 0.500000 0.000000 +vt 0.500000 1.000000 +vt 0.000000 1.000000 +vt -0.000000 0.000000 +vt 0.000000 1.000000 +vt -0.000000 0.000000 +vt 0.500000 0.000000 +vt 0.500000 1.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vt 0.500000 0.000000 +vt 0.500000 1.000000 +vt 0.000000 1.000000 +vt -0.000000 0.000000 +vt 0.500000 0.000000 +vt 0.500000 1.000000 +vt 1.000000 0.250000 +vt 1.000000 0.750000 +vt 0.500000 0.750000 +vt 0.500000 0.250000 +vt 0.500000 0.750000 +vt 0.500000 0.250000 +vt 1.000000 0.250000 +vt 1.000000 0.750000 +vt 0.500000 0.750000 +vt 0.500000 0.250000 +vt 1.000000 0.250000 +vt 1.000000 0.750000 +vt 0.500000 0.750000 +vt 0.500000 0.250000 +vt 1.000000 0.250000 +vt 1.000000 0.750000 +vn 1.0000 0.0000 0.0000 +vn -1.0000 0.0000 0.0000 +vn -0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +s off +f 5/5/2 6/6/2 8/7/2 7/8/2 +f 11/9/3 12/10/3 10/11/3 9/12/3 +f 7/13/4 11/14/4 9/15/4 5/16/4 +f 12/17/5 8/18/5 6/19/5 10/20/5 +f 13/21/2 14/22/2 16/23/2 15/24/2 +f 19/25/3 20/26/3 18/27/3 17/28/3 +f 15/29/4 19/30/4 17/31/4 13/32/4 +f 20/33/5 16/34/5 14/35/5 18/36/5 +g drill_4.001_Cylinder.004 +v 0.031250 -0.437500 0.562500 +v 0.031250 0.437500 0.562500 +v 0.031250 -0.437500 0.625000 +v 0.031250 0.437500 0.625000 +v -0.031250 -0.437500 0.562500 +v -0.031250 0.437500 0.562500 +v -0.031250 -0.437500 0.625000 +v -0.031250 0.437500 0.625000 +v 0.437500 0.031250 0.562500 +v -0.437500 0.031250 0.562500 +v 0.437500 0.031250 0.625000 +v -0.437500 0.031250 0.625000 +v 0.437500 -0.031250 0.562500 +v -0.437500 -0.031250 0.562500 +v 0.437500 -0.031250 0.625000 +v -0.437500 -0.031250 0.625000 +v 0.198554 -0.154360 0.812153 +v -0.154360 0.198554 0.812153 +v 0.198554 -0.154360 0.874653 +v -0.154360 0.198554 0.874653 +v 0.154360 -0.198554 0.812153 +v -0.198554 0.154360 0.812153 +v 0.154360 -0.198554 0.874653 +v -0.198554 0.154360 0.874653 +v 0.154589 0.198783 0.812153 +v -0.198783 -0.154589 0.812153 +v 0.154589 0.198783 0.874653 +v -0.198783 -0.154589 0.874653 +v 0.198783 0.154589 0.812153 +v -0.154589 -0.198783 0.812153 +v 0.198783 0.154589 0.874653 +v -0.154589 -0.198783 0.874653 +v 0.000000 0.062500 1.000000 +v 0.000000 0.500000 0.500000 +v -0.044194 0.044194 1.000000 +v -0.353553 0.353553 0.500000 +v -0.062500 0.000000 1.000000 +v -0.500000 -0.000000 0.500000 +v -0.044194 -0.044194 1.000000 +v -0.353553 -0.353553 0.500000 +v 0.000000 -0.062500 1.000000 +v 0.000000 -0.500000 0.500000 +v 0.044194 -0.044194 1.000000 +v 0.353553 -0.353553 0.500000 +v 0.062500 0.000000 1.000000 +v 0.500000 -0.000000 0.500000 +v 0.044194 0.044194 1.000000 +v 0.353553 0.353553 0.500000 +vt 0.019326 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.249822 +vt 0.001482 0.000011 +vt 0.019238 0.001412 +vt 0.019238 0.019256 +vt 0.001394 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.019238 0.019256 +vt 0.001394 0.019256 +vt 0.001394 0.001412 +vt 0.001482 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.000011 +vt 0.019326 0.249822 +vt 0.019238 0.001412 +vt 0.019238 0.019256 +vt 0.001394 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.019238 0.019256 +vt 0.001394 0.019256 +vt 0.001394 0.001412 +vt 0.018747 0.000692 +vt 0.018747 0.143182 +vt 0.000904 0.143182 +vt 0.000904 0.000692 +vt 0.000904 0.143183 +vt 0.000904 0.000692 +vt 0.018747 0.000692 +vt 0.018747 0.143183 +vt 0.000904 0.143182 +vt 0.000904 0.000692 +vt 0.018747 0.000692 +vt 0.018747 0.143182 +vt 0.000904 0.143183 +vt 0.000904 0.000692 +vt 0.018747 0.000692 +vt 0.018747 0.143183 +vt 0.019238 0.001412 +vt 0.019238 0.019256 +vt 0.001394 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.019238 0.019256 +vt 0.001394 0.019256 +vt 0.001394 0.001412 +vt 0.000904 0.143275 +vt 0.000904 0.000599 +vt 0.018747 0.000599 +vt 0.018747 0.143275 +vt 0.000904 0.143275 +vt 0.000904 0.000599 +vt 0.018747 0.000599 +vt 0.018747 0.143275 +vt 0.000904 0.143275 +vt 0.000904 0.000599 +vt 0.018747 0.000599 +vt 0.018747 0.143275 +vt 0.000904 0.143275 +vt 0.000904 0.000599 +vt 0.018747 0.000599 +vt 0.018747 0.143275 +vt 0.019238 0.001412 +vt 0.019238 0.019256 +vt 0.001394 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.019238 0.019256 +vt 0.001394 0.019256 +vt 0.001394 0.001412 +vt 0.082670 0.042215 +vt 0.228052 0.042215 +vt 0.179387 0.159701 +vt 0.076587 0.056901 +vt 0.061901 0.208365 +vt 0.061901 0.062984 +vt 0.082670 0.042215 +vt 0.228052 0.042215 +vt 0.179387 0.159701 +vt 0.076587 0.056901 +vt 0.061901 0.208366 +vt 0.061901 0.062984 +vt 0.082670 0.042215 +vt 0.228052 0.042215 +vt 0.179387 0.159701 +vt 0.076587 0.056901 +vt 0.061901 0.208365 +vt 0.061901 0.062984 +vt 0.082670 0.042215 +vt 0.228052 0.042215 +vt 0.179387 0.159701 +vt 0.076587 0.056901 +vt 0.061901 0.208365 +vt 0.061901 0.062984 +vt 0.047215 0.056901 +vt 0.041132 0.042215 +vt 0.047215 0.027529 +vt 0.061901 0.021446 +vt 0.076587 0.027529 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +vn 0.7071 0.7071 0.0000 +vn -0.7071 -0.7071 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 0.7071 -0.7071 0.0000 +vn -0.7071 0.7071 0.0000 +vn -0.2976 0.7185 0.6287 +vn -0.7185 0.2976 0.6287 +vn -0.7185 -0.2976 0.6287 +vn -0.2976 -0.7185 0.6287 +vn 0.2976 -0.7185 0.6287 +vn 0.7185 -0.2976 0.6287 +vn 0.7185 0.2976 0.6287 +vn 0.2976 0.7185 0.6287 +s off +f 21/37/6 22/38/6 24/39/6 23/40/6 +f 23/41/7 24/42/7 28/43/7 27/44/7 +f 27/45/8 28/46/8 26/47/8 25/48/8 +f 23/49/9 27/50/9 25/51/9 21/52/9 +f 28/53/10 24/54/10 22/55/10 26/56/10 +f 29/57/10 30/58/10 32/59/10 31/60/10 +f 31/61/7 32/62/7 36/63/7 35/64/7 +f 35/65/9 36/66/9 34/67/9 33/68/9 +f 31/69/6 35/70/6 33/71/6 29/72/6 +f 36/73/8 32/74/8 30/75/8 34/76/8 +f 37/77/11 38/78/11 40/79/11 39/80/11 +f 39/81/7 40/82/7 44/83/7 43/84/7 +f 43/85/12 44/86/12 42/87/12 41/88/12 +f 41/89/13 42/90/13 38/91/13 37/92/13 +f 39/93/14 43/94/14 41/95/14 37/96/14 +f 44/97/15 40/98/15 38/99/15 42/100/15 +f 45/101/15 46/102/15 48/103/15 47/104/15 +f 47/105/7 48/106/7 52/107/7 51/108/7 +f 51/109/14 52/110/14 50/111/14 49/112/14 +f 49/113/13 50/114/13 46/115/13 45/116/13 +f 47/117/11 51/118/11 49/119/11 45/120/11 +f 52/121/12 48/122/12 46/123/12 50/124/12 +f 53/125/16 54/126/16 56/127/16 55/128/16 +f 55/128/17 56/127/17 58/129/17 57/130/17 +f 57/131/18 58/132/18 60/133/18 59/134/18 +f 59/134/19 60/133/19 62/135/19 61/136/19 +f 61/137/20 62/138/20 64/139/20 63/140/20 +f 63/140/21 64/139/21 66/141/21 65/142/21 +f 65/143/22 66/144/22 68/145/22 67/146/22 +f 67/146/23 68/145/23 54/147/23 53/148/23 +f 53/148/7 55/149/7 57/150/7 59/151/7 61/152/7 63/153/7 65/143/7 67/146/7 +g drill_4_Cylinder.003 +v 0.500000 -0.500000 0.000000 +v 0.500000 0.500000 0.000000 +v -0.500000 -0.500000 0.000000 +v -0.500000 0.500000 0.000000 +v 0.250000 -0.250000 -0.500000 +v 0.250000 0.250000 -0.500000 +v -0.250000 -0.250000 -0.500000 +v -0.250000 0.250000 -0.500000 +vt 1.000000 -0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt -0.000000 0.000000 +vt 0.750000 0.250000 +vt 0.750000 0.750000 +vt 0.250000 0.750000 +vt 0.250000 0.250000 +vn 0.0000 0.0000 -1.0000 +s off +f 71/154/24 72/155/24 70/156/24 69/157/24 +f 75/158/24 76/159/24 74/160/24 73/161/24 diff --git a/models/digtron_dual_digger.obj b/models/digtron_dual_digger.obj new file mode 100644 index 0000000..a2cd7a4 --- /dev/null +++ b/models/digtron_dual_digger.obj @@ -0,0 +1,2077 @@ +# Blender v2.79 (sub 0) OBJ File: 'dual digger.blend' +# www.blender.org +g drill_4.002_Cylinder.007 +v 0.500000 0.500000 0.500000 +v -0.500000 -0.500000 0.500000 +v 0.500000 -0.500000 0.500000 +v -0.500000 0.500000 0.500000 +v -0.500000 -0.500000 -0.500000 +v 0.500000 -0.500000 -0.500000 +vt 1.000000 1.000000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.000000 1.000000 +vt 1.000000 0.000000 +vn 0.0000 -0.0000 1.0000 +vn -0.0000 -1.0000 -0.0000 +s off +f 1/1/1 2/2/1 3/3/1 +f 3/4/2 5/5/2 6/6/2 +f 1/1/1 4/7/1 2/2/1 +f 3/4/2 2/8/2 5/5/2 +g drill_4.001_Cylinder.002 +v 0.250000 0.250000 -0.500000 +v -0.250000 0.250000 -0.500000 +v 0.500000 0.500000 0.500000 +v 0.500000 0.500000 0.000000 +v -0.500000 0.500000 0.000000 +v -0.500000 0.500000 0.500000 +v 0.250000 0.500000 0.000000 +v -0.250000 0.500000 0.000000 +v 0.500000 0.000000 -0.500000 +v -0.250000 0.500000 -0.250000 +v -0.500000 -0.500000 -0.500000 +v 0.500000 -0.500000 -0.500000 +v -0.500000 0.000000 -0.500000 +v 0.250000 0.500000 -0.250000 +v 0.250000 0.000000 -0.500000 +v -0.250000 0.000000 -0.500000 +v -0.500000 0.000000 0.000000 +v 0.500000 -0.000000 -0.000000 +v 0.250000 0.250000 -0.250000 +v -0.250000 0.250000 -0.250000 +vt 0.750000 0.500000 +vt 0.250000 0.750000 +vt 0.250000 0.500000 +vt 0.000000 1.000000 +vt 1.000000 0.500000 +vt 1.000000 1.000000 +vt 0.250000 0.250000 +vt 0.750000 0.000000 +vt 0.750000 0.250000 +vt 1.000000 0.000001 +vt 0.000000 0.500000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.000000 0.500000 +vt 0.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.750000 0.750000 +vt 0.250000 1.000000 +vt 0.250000 0.750000 +vt 0.750000 0.250000 +vt 0.250000 0.500000 +vt 0.250000 0.250000 +vt 0.750000 0.750000 +vt 0.000000 0.500000 +vt 0.250000 0.000000 +vt 1.000000 0.500000 +vt 1.000000 0.500000 +vt 0.750000 1.000000 +vt 0.750000 0.500000 +vn 0.0000 0.0000 -1.0000 +vn 0.0000 1.0000 0.0000 +s off +f 22/9/3 7/10/3 21/11/3 +f 9/12/4 11/13/4 12/14/4 +f 25/15/4 8/16/4 26/17/4 +f 19/18/4 24/19/4 15/20/4 +f 17/21/3 15/22/3 18/23/3 +f 24/19/3 11/24/3 10/25/3 +f 26/26/3 20/27/3 25/28/3 +f 16/29/4 13/30/4 20/31/4 +f 22/9/3 8/32/3 7/10/3 +f 9/12/4 10/33/4 11/13/4 +f 25/15/4 7/34/4 8/16/4 +f 19/18/4 23/35/4 24/19/4 +f 17/21/3 19/36/3 15/22/3 +f 24/19/3 23/35/3 11/24/3 +f 26/26/3 16/37/3 20/27/3 +f 16/29/4 14/38/4 13/30/4 +g drill_4.004_Cylinder.000 +v 0.416156 -0.562500 -0.138553 +v -0.392238 -0.562500 0.196295 +v 0.416156 -0.625000 -0.138553 +v -0.392238 -0.625000 0.196295 +v 0.392238 -0.562500 -0.196295 +v -0.416156 -0.562500 0.138553 +v 0.392238 -0.625000 -0.196295 +v -0.416156 -0.625000 0.138553 +v 0.138553 -0.562500 0.416156 +v -0.196295 -0.562500 -0.392238 +v 0.138553 -0.625000 0.416156 +v -0.196295 -0.625000 -0.392238 +v 0.196295 -0.562500 0.392238 +v -0.138553 -0.562500 -0.416156 +v 0.196295 -0.625000 0.392238 +v -0.138553 -0.625000 -0.416156 +v 0.218593 -0.812153 0.124369 +v -0.242511 -0.812153 -0.066627 +v 0.218593 -0.874653 0.124369 +v -0.242511 -0.874653 -0.066627 +v 0.242511 -0.812153 0.066627 +v -0.218593 -0.812153 -0.124369 +v 0.242511 -0.874653 0.066627 +v -0.218593 -0.874653 -0.124369 +v -0.124493 -0.812153 0.218892 +v 0.066751 -0.812153 -0.242810 +v -0.124493 -0.874653 0.218892 +v 0.066751 -0.874653 -0.242810 +v -0.066750 -0.812153 0.242810 +v 0.124493 -0.812153 -0.218892 +v -0.066750 -0.874653 0.242810 +v 0.124493 -0.874653 -0.218892 +v -0.057742 -1.000000 0.023918 +v -0.461940 -0.500000 0.191342 +v -0.057742 -1.000000 -0.023918 +v -0.461940 -0.500000 -0.191342 +v -0.023918 -1.000000 -0.057742 +v -0.191342 -0.500000 -0.461940 +v 0.023918 -1.000000 -0.057743 +v 0.191342 -0.500000 -0.461940 +v 0.057743 -1.000000 -0.023918 +v 0.461940 -0.500000 -0.191342 +v 0.057742 -1.000000 0.023918 +v 0.461940 -0.500000 0.191342 +v 0.023918 -1.000000 0.057742 +v 0.191342 -0.500000 0.461940 +v -0.023918 -1.000000 0.057742 +v -0.191342 -0.500000 0.461940 +v 0.331456 -0.562500 -0.287262 +v -0.287262 -0.562500 0.331456 +v 0.331456 -0.625000 -0.287262 +v -0.287262 -0.625000 0.331456 +v 0.287262 -0.562500 -0.331456 +v -0.331456 -0.562500 0.287262 +v 0.287262 -0.625000 -0.331456 +v -0.331456 -0.625000 0.287262 +v 0.287262 -0.562500 0.331456 +v -0.331456 -0.562500 -0.287262 +v 0.287262 -0.625000 0.331456 +v -0.331456 -0.625000 -0.287262 +v 0.331456 -0.562500 0.287262 +v -0.287262 -0.562500 -0.331456 +v 0.331456 -0.625000 0.287262 +v -0.287262 -0.625000 -0.331456 +v 0.249548 -0.812153 0.031250 +v -0.249548 -0.812153 0.031250 +v 0.249548 -0.874653 0.031250 +v -0.249548 -0.874653 0.031250 +v 0.249548 -0.812153 -0.031250 +v -0.249548 -0.812153 -0.031250 +v 0.249548 -0.874653 -0.031250 +v -0.249548 -0.874653 -0.031250 +v -0.031250 -0.812153 0.249872 +v -0.031250 -0.812153 -0.249872 +v -0.031250 -0.874653 0.249872 +v -0.031250 -0.874653 -0.249872 +v 0.031250 -0.812153 0.249872 +v 0.031250 -0.812153 -0.249872 +v 0.031250 -0.874653 0.249872 +v 0.031250 -0.874653 -0.249872 +v -0.044194 -1.000000 0.044194 +v -0.353553 -0.500000 0.353553 +v -0.062500 -1.000000 -0.000000 +v -0.500000 -0.500000 0.000000 +v -0.044194 -1.000000 -0.044194 +v -0.353553 -0.500000 -0.353553 +v 0.000000 -1.000000 -0.062500 +v 0.000000 -0.500000 -0.500000 +v 0.044194 -1.000000 -0.044194 +v 0.353553 -0.500000 -0.353553 +v 0.062500 -1.000000 -0.000000 +v 0.500000 -0.500000 0.000000 +v 0.044194 -1.000000 0.044194 +v 0.353553 -0.500000 0.353553 +v 0.000000 -1.000000 0.062500 +v 0.000000 -0.500000 0.500000 +v 0.196295 -0.562500 -0.392238 +v -0.138553 -0.562500 0.416156 +v 0.196295 -0.625000 -0.392238 +v -0.138553 -0.625000 0.416156 +v 0.138553 -0.562500 -0.416156 +v -0.196295 -0.562500 0.392238 +v 0.138553 -0.625000 -0.416156 +v -0.196295 -0.625000 0.392238 +v 0.392238 -0.562500 0.196295 +v -0.416156 -0.562500 -0.138553 +v 0.392238 -0.625000 0.196295 +v -0.416156 -0.625000 -0.138553 +v 0.416156 -0.562500 0.138553 +v -0.392238 -0.562500 -0.196295 +v 0.416156 -0.625000 0.138553 +v -0.392238 -0.625000 -0.196295 +v 0.242511 -0.812153 -0.066627 +v -0.218593 -0.812153 0.124369 +v 0.242511 -0.874653 -0.066627 +v -0.218593 -0.874653 0.124369 +v 0.218593 -0.812153 -0.124369 +v -0.242511 -0.812153 0.066627 +v 0.218593 -0.874653 -0.124369 +v -0.242511 -0.874653 0.066627 +v 0.066750 -0.812153 0.242810 +v -0.124493 -0.812153 -0.218892 +v 0.066750 -0.874653 0.242810 +v -0.124493 -0.874653 -0.218892 +v 0.124493 -0.812153 0.218892 +v -0.066750 -0.812153 -0.242810 +v 0.124493 -0.874653 0.218892 +v -0.066750 -0.874653 -0.242810 +v -0.023918 -1.000000 0.057742 +v -0.191342 -0.500000 0.461940 +v -0.057742 -1.000000 0.023918 +v -0.461940 -0.500000 0.191342 +v -0.057742 -1.000000 -0.023918 +v -0.461940 -0.500000 -0.191342 +v -0.023918 -1.000000 -0.057743 +v -0.191342 -0.500000 -0.461940 +v 0.023918 -1.000000 -0.057743 +v 0.191342 -0.500000 -0.461940 +v 0.057743 -1.000000 -0.023918 +v 0.461940 -0.500000 -0.191342 +v 0.057743 -1.000000 0.023918 +v 0.461940 -0.500000 0.191342 +v 0.023918 -1.000000 0.057742 +v 0.191342 -0.500000 0.461940 +v 0.031250 -0.562500 -0.437500 +v 0.031250 -0.562500 0.437500 +v 0.031250 -0.625000 -0.437500 +v 0.031250 -0.625000 0.437500 +v -0.031250 -0.562500 -0.437500 +v -0.031250 -0.562500 0.437500 +v -0.031250 -0.625000 -0.437500 +v -0.031250 -0.625000 0.437500 +v 0.437500 -0.562500 0.031250 +v -0.437500 -0.562500 0.031250 +v 0.437500 -0.625000 0.031250 +v -0.437500 -0.625000 0.031250 +v 0.437500 -0.562500 -0.031250 +v -0.437500 -0.562500 -0.031250 +v 0.437500 -0.625000 -0.031250 +v -0.437500 -0.625000 -0.031250 +v 0.198554 -0.812153 -0.154360 +v -0.154360 -0.812153 0.198554 +v 0.198554 -0.874653 -0.154360 +v -0.154360 -0.874653 0.198554 +v 0.154360 -0.812153 -0.198554 +v -0.198554 -0.812153 0.154360 +v 0.154360 -0.874653 -0.198554 +v -0.198554 -0.874653 0.154360 +v 0.154589 -0.812153 0.198783 +v -0.198783 -0.812153 -0.154589 +v 0.154589 -0.874653 0.198783 +v -0.198783 -0.874653 -0.154589 +v 0.198783 -0.812153 0.154589 +v -0.154589 -0.812153 -0.198783 +v 0.198783 -0.874653 0.154589 +v -0.154589 -0.874653 -0.198783 +v 0.000000 -1.000000 0.062500 +v 0.000000 -0.500000 0.500000 +v -0.044194 -1.000000 0.044194 +v -0.353553 -0.500000 0.353553 +v -0.062500 -1.000000 -0.000000 +v -0.500000 -0.500000 -0.000000 +v -0.044194 -1.000000 -0.044194 +v -0.353553 -0.500000 -0.353553 +v 0.000000 -1.000000 -0.062500 +v 0.000000 -0.500000 -0.500000 +v 0.044194 -1.000000 -0.044194 +v 0.353553 -0.500000 -0.353553 +v 0.062500 -1.000000 -0.000000 +v 0.500000 -0.500000 -0.000000 +v 0.044194 -1.000000 0.044194 +v 0.353553 -0.500000 0.353553 +v 0.416156 -0.138553 0.562500 +v -0.392238 0.196295 0.562500 +v 0.416156 -0.138553 0.625000 +v -0.392238 0.196295 0.625000 +v 0.392238 -0.196295 0.562500 +v -0.416156 0.138553 0.562500 +v 0.392238 -0.196295 0.625000 +v -0.416156 0.138553 0.625000 +v 0.138553 0.416156 0.562500 +v -0.196295 -0.392238 0.562500 +v 0.138553 0.416156 0.625000 +v -0.196295 -0.392238 0.625000 +v 0.196295 0.392238 0.562500 +v -0.138553 -0.416156 0.562500 +v 0.196295 0.392238 0.625000 +v -0.138553 -0.416156 0.625000 +v 0.218593 0.124369 0.812153 +v -0.242511 -0.066627 0.812153 +v 0.218593 0.124369 0.874653 +v -0.242511 -0.066627 0.874653 +v 0.242511 0.066627 0.812153 +v -0.218593 -0.124369 0.812153 +v 0.242511 0.066627 0.874653 +v -0.218593 -0.124369 0.874653 +v -0.124493 0.218892 0.812153 +v 0.066750 -0.242810 0.812153 +v -0.124493 0.218892 0.874653 +v 0.066751 -0.242810 0.874653 +v -0.066751 0.242810 0.812153 +v 0.124493 -0.218892 0.812153 +v -0.066751 0.242810 0.874653 +v 0.124493 -0.218892 0.874653 +v -0.057742 0.023918 1.000000 +v -0.461940 0.191342 0.500000 +v -0.057743 -0.023918 1.000000 +v -0.461940 -0.191342 0.500000 +v -0.023918 -0.057742 1.000000 +v -0.191342 -0.461940 0.500000 +v 0.023918 -0.057742 1.000000 +v 0.191342 -0.461940 0.500000 +v 0.057742 -0.023918 1.000000 +v 0.461940 -0.191342 0.500000 +v 0.057742 0.023918 1.000000 +v 0.461940 0.191342 0.500000 +v 0.023918 0.057742 1.000000 +v 0.191342 0.461940 0.500000 +v -0.023918 0.057742 1.000000 +v -0.191342 0.461940 0.500000 +v 0.331456 -0.287262 0.562500 +v -0.287262 0.331456 0.562500 +v 0.331456 -0.287262 0.625000 +v -0.287262 0.331456 0.625000 +v 0.287262 -0.331456 0.562500 +v -0.331456 0.287262 0.562500 +v 0.287262 -0.331456 0.625000 +v -0.331456 0.287262 0.625000 +v 0.287262 0.331456 0.562500 +v -0.331456 -0.287262 0.562500 +v 0.287262 0.331456 0.625000 +v -0.331456 -0.287262 0.625000 +v 0.331456 0.287262 0.562500 +v -0.287262 -0.331456 0.562500 +v 0.331456 0.287262 0.625000 +v -0.287262 -0.331456 0.625000 +v 0.249548 0.031250 0.812153 +v -0.249548 0.031250 0.812153 +v 0.249548 0.031250 0.874653 +v -0.249548 0.031250 0.874653 +v 0.249548 -0.031250 0.812153 +v -0.249548 -0.031250 0.812153 +v 0.249548 -0.031250 0.874653 +v -0.249548 -0.031250 0.874653 +v -0.031250 0.249872 0.812153 +v -0.031250 -0.249872 0.812153 +v -0.031250 0.249872 0.874653 +v -0.031250 -0.249872 0.874653 +v 0.031250 0.249872 0.812153 +v 0.031250 -0.249872 0.812153 +v 0.031250 0.249872 0.874653 +v 0.031250 -0.249872 0.874653 +v -0.044194 0.044194 1.000000 +v -0.353553 0.353553 0.500000 +v -0.062500 0.000000 1.000000 +v -0.500000 0.000000 0.500000 +v -0.044194 -0.044194 1.000000 +v -0.353553 -0.353553 0.500000 +v 0.000000 -0.062500 1.000000 +v 0.000000 -0.500000 0.500000 +v 0.044194 -0.044194 1.000000 +v 0.353553 -0.353553 0.500000 +v 0.062500 0.000000 1.000000 +v 0.500000 0.000000 0.500000 +v 0.044194 0.044194 1.000000 +v 0.353553 0.353553 0.500000 +v 0.000000 0.062500 1.000000 +v 0.000000 0.500000 0.500000 +v 0.196295 -0.392238 0.562500 +v -0.138553 0.416156 0.562500 +v 0.196295 -0.392238 0.625000 +v -0.138553 0.416156 0.625000 +v 0.138553 -0.416156 0.562500 +v -0.196295 0.392238 0.562500 +v 0.138553 -0.416156 0.625000 +v -0.196295 0.392238 0.625000 +v 0.392238 0.196295 0.562500 +v -0.416156 -0.138553 0.562500 +v 0.392238 0.196295 0.625000 +v -0.416156 -0.138553 0.625000 +v 0.416156 0.138553 0.562500 +v -0.392238 -0.196295 0.562500 +v 0.416156 0.138553 0.625000 +v -0.392238 -0.196295 0.625000 +v 0.242511 -0.066627 0.812153 +v -0.218593 0.124369 0.812153 +v 0.242511 -0.066627 0.874653 +v -0.218593 0.124369 0.874653 +v 0.218593 -0.124369 0.812153 +v -0.242511 0.066627 0.812153 +v 0.218593 -0.124369 0.874653 +v -0.242511 0.066627 0.874653 +v 0.066750 0.242810 0.812153 +v -0.124493 -0.218892 0.812153 +v 0.066750 0.242810 0.874653 +v -0.124493 -0.218892 0.874653 +v 0.124493 0.218892 0.812153 +v -0.066750 -0.242810 0.812153 +v 0.124493 0.218892 0.874653 +v -0.066750 -0.242810 0.874653 +v -0.023918 0.057742 1.000000 +v -0.191342 0.461940 0.500000 +v -0.057742 0.023918 1.000000 +v -0.461940 0.191342 0.500000 +v -0.057742 -0.023918 1.000000 +v -0.461940 -0.191342 0.500000 +v -0.023918 -0.057742 1.000000 +v -0.191342 -0.461940 0.500000 +v 0.023918 -0.057742 1.000000 +v 0.191342 -0.461940 0.500000 +v 0.057742 -0.023918 1.000000 +v 0.461940 -0.191342 0.500000 +v 0.057743 0.023918 1.000000 +v 0.461940 0.191342 0.500000 +v 0.023918 0.057742 1.000000 +v 0.191342 0.461940 0.500000 +v 0.031250 -0.437500 0.562500 +v 0.031250 0.437500 0.562500 +v 0.031250 -0.437500 0.625000 +v 0.031250 0.437500 0.625000 +v -0.031250 -0.437500 0.562500 +v -0.031250 0.437500 0.562500 +v -0.031250 -0.437500 0.625000 +v -0.031250 0.437500 0.625000 +v 0.437500 0.031250 0.562500 +v -0.437500 0.031250 0.562500 +v 0.437500 0.031250 0.625000 +v -0.437500 0.031250 0.625000 +v 0.437500 -0.031250 0.562500 +v -0.437500 -0.031250 0.562500 +v 0.437500 -0.031250 0.625000 +v -0.437500 -0.031250 0.625000 +v 0.198554 -0.154360 0.812153 +v -0.154360 0.198554 0.812153 +v 0.198554 -0.154360 0.874653 +v -0.154360 0.198554 0.874653 +v 0.154360 -0.198554 0.812153 +v -0.198554 0.154360 0.812153 +v 0.154360 -0.198554 0.874653 +v -0.198554 0.154360 0.874653 +v 0.154589 0.198783 0.812153 +v -0.198783 -0.154589 0.812153 +v 0.154589 0.198783 0.874653 +v -0.198783 -0.154589 0.874653 +v 0.198783 0.154589 0.812153 +v -0.154589 -0.198783 0.812153 +v 0.198783 0.154589 0.874653 +v -0.154589 -0.198783 0.874653 +v 0.000000 0.062500 1.000000 +v -0.000000 0.500000 0.500000 +v -0.044194 0.044194 1.000000 +v -0.353553 0.353553 0.500000 +v -0.062500 0.000000 1.000000 +v -0.500000 -0.000000 0.500000 +v -0.044194 -0.044194 1.000000 +v -0.353553 -0.353553 0.500000 +v 0.000000 -0.062500 1.000000 +v 0.000000 -0.500000 0.500000 +v 0.044194 -0.044194 1.000000 +v 0.353553 -0.353553 0.500000 +v 0.062500 0.000000 1.000000 +v 0.500000 -0.000000 0.500000 +v 0.044194 0.044194 1.000000 +v 0.353553 0.353553 0.500000 +vt 0.769326 0.999288 +vt 0.751482 0.000046 +vt 0.769326 0.000046 +vt 0.769326 0.999289 +vt 0.751482 0.000046 +vt 0.769326 0.000046 +vt 0.769326 0.999288 +vt 0.751482 0.000046 +vt 0.769326 0.000046 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.751482 0.000046 +vt 0.769326 0.999288 +vt 0.751482 0.999288 +vt 0.751482 0.000046 +vt 0.769326 0.999289 +vt 0.751482 0.999289 +vt 0.751482 0.000046 +vt 0.769326 0.999288 +vt 0.751482 0.999288 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.768747 0.572730 +vt 0.750904 0.002768 +vt 0.768747 0.002768 +vt 0.750904 0.572730 +vt 0.768747 0.002767 +vt 0.768747 0.572730 +vt 0.750904 0.002768 +vt 0.768747 0.572730 +vt 0.750904 0.572730 +vt 0.750904 0.572730 +vt 0.768747 0.002767 +vt 0.768747 0.572730 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.750904 0.002398 +vt 0.768747 0.573100 +vt 0.750904 0.573100 +vt 0.750904 0.573100 +vt 0.768747 0.002398 +vt 0.768747 0.573100 +vt 0.750904 0.002398 +vt 0.768747 0.573100 +vt 0.750904 0.573100 +vt 0.750904 0.002397 +vt 0.768747 0.573100 +vt 0.750904 0.573100 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.978052 0.168860 +vt 0.826587 0.227603 +vt 0.832670 0.168860 +vt 0.929387 0.638804 +vt 0.811901 0.251935 +vt 0.978052 0.168860 +vt 0.826587 0.227604 +vt 0.832670 0.168861 +vt 0.811901 0.833462 +vt 0.811901 0.251936 +vt 0.978052 0.168860 +vt 0.826587 0.227603 +vt 0.832670 0.168860 +vt 0.929387 0.638805 +vt 0.811901 0.251935 +vt 0.978052 0.168860 +vt 0.826587 0.227602 +vt 0.832670 0.168859 +vt 0.929387 0.638804 +vt 0.811901 0.251935 +vt 0.797216 0.110116 +vt 0.826587 0.110116 +vt 0.519326 0.999288 +vt 0.501482 0.000046 +vt 0.519326 0.000046 +vt 0.519326 0.999289 +vt 0.501482 0.000046 +vt 0.519326 0.000046 +vt 0.519326 0.999288 +vt 0.501482 0.000046 +vt 0.519326 0.000046 +vt 0.519238 0.077023 +vt 0.501394 0.005648 +vt 0.519238 0.005648 +vt 0.519238 0.077023 +vt 0.501394 0.005648 +vt 0.519238 0.005648 +vt 0.501482 0.000046 +vt 0.519326 0.999288 +vt 0.501482 0.999288 +vt 0.501482 0.000046 +vt 0.519326 0.999289 +vt 0.501482 0.999289 +vt 0.501482 0.000046 +vt 0.519326 0.999288 +vt 0.501482 0.999288 +vt 0.519238 0.077023 +vt 0.501394 0.005648 +vt 0.519238 0.005648 +vt 0.519238 0.077023 +vt 0.501394 0.005648 +vt 0.519238 0.005648 +vt 0.518747 0.572730 +vt 0.500904 0.002768 +vt 0.518747 0.002768 +vt 0.500904 0.002767 +vt 0.518747 0.572730 +vt 0.500904 0.572730 +vt 0.500904 0.002768 +vt 0.518747 0.572730 +vt 0.500904 0.572730 +vt 0.500904 0.002767 +vt 0.518747 0.572730 +vt 0.500904 0.572730 +vt 0.519238 0.077023 +vt 0.501394 0.005648 +vt 0.519238 0.005648 +vt 0.519238 0.077023 +vt 0.501394 0.005648 +vt 0.519238 0.005648 +vt 0.500904 0.002398 +vt 0.518747 0.573100 +vt 0.500904 0.573100 +vt 0.500904 0.002398 +vt 0.518747 0.573100 +vt 0.500904 0.573100 +vt 0.500904 0.002398 +vt 0.518747 0.573100 +vt 0.500904 0.573100 +vt 0.500904 0.573100 +vt 0.518747 0.002397 +vt 0.518747 0.573100 +vt 0.519238 0.077023 +vt 0.501394 0.005648 +vt 0.519238 0.005648 +vt 0.519238 0.077023 +vt 0.501394 0.005648 +vt 0.519238 0.005648 +vt 0.728052 0.168860 +vt 0.576587 0.227603 +vt 0.582670 0.168860 +vt 0.679387 0.638804 +vt 0.561901 0.251935 +vt 0.728052 0.168860 +vt 0.576587 0.227604 +vt 0.582670 0.168861 +vt 0.679387 0.638805 +vt 0.561901 0.251936 +vt 0.582670 0.168860 +vt 0.679387 0.638805 +vt 0.576587 0.227603 +vt 0.561901 0.251935 +vt 0.728052 0.168860 +vt 0.576587 0.227602 +vt 0.582670 0.168859 +vt 0.679387 0.638804 +vt 0.561901 0.251935 +vt 0.547216 0.110116 +vt 0.576587 0.110116 +vt 0.269326 0.999288 +vt 0.251482 0.000046 +vt 0.269326 0.000046 +vt 0.269326 0.000046 +vt 0.251482 0.999288 +vt 0.251482 0.000046 +vt 0.269326 0.999288 +vt 0.251482 0.000046 +vt 0.269326 0.000046 +vt 0.269238 0.077023 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.269238 0.077023 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.251482 0.000046 +vt 0.269326 0.999288 +vt 0.251482 0.999288 +vt 0.251482 0.999288 +vt 0.269326 0.000046 +vt 0.269326 0.999288 +vt 0.251482 0.000046 +vt 0.269326 0.999288 +vt 0.251482 0.999288 +vt 0.269238 0.077023 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.269238 0.077023 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.268747 0.572730 +vt 0.250904 0.002768 +vt 0.268747 0.002768 +vt 0.250904 0.002767 +vt 0.268747 0.572730 +vt 0.250904 0.572730 +vt 0.250904 0.002768 +vt 0.268747 0.572730 +vt 0.250904 0.572730 +vt 0.250904 0.002767 +vt 0.268747 0.572730 +vt 0.250904 0.572730 +vt 0.269238 0.077022 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.269238 0.077023 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.250904 0.002398 +vt 0.268747 0.573100 +vt 0.250904 0.573100 +vt 0.250904 0.002398 +vt 0.268747 0.573100 +vt 0.250904 0.573100 +vt 0.250904 0.002398 +vt 0.268747 0.573100 +vt 0.250904 0.573100 +vt 0.250904 0.002397 +vt 0.268747 0.573100 +vt 0.250904 0.573100 +vt 0.269238 0.077023 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.269238 0.077023 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.478052 0.168860 +vt 0.326587 0.227603 +vt 0.332670 0.168860 +vt 0.429387 0.638805 +vt 0.311901 0.251935 +vt 0.478052 0.168860 +vt 0.326587 0.227604 +vt 0.332670 0.168861 +vt 0.429387 0.638805 +vt 0.311901 0.251936 +vt 0.332670 0.168860 +vt 0.429387 0.638805 +vt 0.326587 0.227603 +vt 0.311901 0.251935 +vt 0.478052 0.168860 +vt 0.326587 0.227603 +vt 0.332670 0.168859 +vt 0.429387 0.638804 +vt 0.311901 0.251935 +vt 0.297215 0.110116 +vt 0.326587 0.110116 +vt 0.019326 0.999288 +vt 0.001482 0.000046 +vt 0.019326 0.000046 +vt 0.019326 0.999288 +vt 0.001482 0.000046 +vt 0.019326 0.000046 +vt 0.019326 0.999288 +vt 0.001482 0.000046 +vt 0.019326 0.000046 +vt 0.019238 0.077023 +vt 0.001394 0.005648 +vt 0.019238 0.005648 +vt 0.019238 0.077023 +vt 0.001394 0.005648 +vt 0.019238 0.005648 +vt 0.001482 0.000046 +vt 0.019326 0.999288 +vt 0.001482 0.999288 +vt 0.001482 0.000046 +vt 0.019326 0.999288 +vt 0.001482 0.999288 +vt 0.001482 0.000046 +vt 0.019326 0.999288 +vt 0.001482 0.999288 +vt 0.019238 0.077023 +vt 0.001394 0.005648 +vt 0.019238 0.005648 +vt 0.019238 0.077023 +vt 0.001394 0.005648 +vt 0.019238 0.005648 +vt 0.018747 0.572730 +vt 0.000904 0.002768 +vt 0.018747 0.002768 +vt 0.000904 0.002767 +vt 0.018747 0.572730 +vt 0.000904 0.572730 +vt 0.000904 0.002768 +vt 0.018747 0.572730 +vt 0.000904 0.572730 +vt 0.000904 0.002767 +vt 0.018747 0.572730 +vt 0.000904 0.572730 +vt 0.019238 0.077022 +vt 0.001394 0.005648 +vt 0.019238 0.005648 +vt 0.019238 0.077023 +vt 0.001394 0.005648 +vt 0.019238 0.005648 +vt 0.000904 0.002398 +vt 0.018747 0.573100 +vt 0.000904 0.573100 +vt 0.000904 0.002398 +vt 0.018747 0.573100 +vt 0.000904 0.573100 +vt 0.000904 0.002398 +vt 0.018747 0.573100 +vt 0.000904 0.573100 +vt 0.000904 0.573100 +vt 0.018747 0.002397 +vt 0.018747 0.573100 +vt 0.019238 0.077023 +vt 0.001394 0.005648 +vt 0.019238 0.005648 +vt 0.019238 0.077023 +vt 0.001394 0.005648 +vt 0.019238 0.005648 +vt 0.228052 0.168860 +vt 0.076587 0.227603 +vt 0.082670 0.168860 +vt 0.061901 0.833462 +vt 0.061901 0.251935 +vt 0.228052 0.168860 +vt 0.076587 0.227604 +vt 0.082670 0.168861 +vt 0.179387 0.638805 +vt 0.061901 0.251936 +vt 0.228052 0.168860 +vt 0.076587 0.227603 +vt 0.082670 0.168860 +vt 0.179387 0.638805 +vt 0.061901 0.251935 +vt 0.228052 0.168860 +vt 0.076587 0.227603 +vt 0.082670 0.168860 +vt 0.179387 0.638804 +vt 0.061901 0.251935 +vt 0.047215 0.110116 +vt 0.076587 0.110116 +vt 0.769326 0.000046 +vt 0.751482 0.999288 +vt 0.751482 0.000046 +vt 0.769326 0.999289 +vt 0.751482 0.000046 +vt 0.769326 0.000046 +vt 0.769326 0.000046 +vt 0.751482 0.999288 +vt 0.751482 0.000046 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.751482 0.999288 +vt 0.769326 0.000046 +vt 0.769326 0.999288 +vt 0.751482 0.000046 +vt 0.769326 0.999289 +vt 0.751482 0.999289 +vt 0.751482 0.000046 +vt 0.769326 0.999288 +vt 0.751482 0.999288 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.768747 0.002768 +vt 0.750904 0.572730 +vt 0.750904 0.002768 +vt 0.750904 0.572730 +vt 0.768747 0.002767 +vt 0.768747 0.572730 +vt 0.750904 0.002768 +vt 0.768747 0.572730 +vt 0.750904 0.572730 +vt 0.750904 0.002767 +vt 0.768747 0.572730 +vt 0.750904 0.572730 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.750904 0.573100 +vt 0.768747 0.002398 +vt 0.768747 0.573100 +vt 0.750904 0.002398 +vt 0.768747 0.573100 +vt 0.750904 0.573100 +vt 0.750904 0.573100 +vt 0.768747 0.002398 +vt 0.768747 0.573100 +vt 0.750904 0.002397 +vt 0.768747 0.573100 +vt 0.750904 0.573100 +vt 0.769238 0.005648 +vt 0.751394 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.077023 +vt 0.751394 0.005648 +vt 0.769238 0.005648 +vt 0.832670 0.168860 +vt 0.929387 0.638804 +vt 0.826587 0.227603 +vt 0.811901 0.833462 +vt 0.811901 0.251935 +vt 0.832670 0.168861 +vt 0.929387 0.638805 +vt 0.826587 0.227604 +vt 0.811901 0.833462 +vt 0.811901 0.251936 +vt 0.978052 0.168860 +vt 0.826587 0.227603 +vt 0.832670 0.168860 +vt 0.929387 0.638805 +vt 0.811901 0.251935 +vt 0.832670 0.168859 +vt 0.929387 0.638804 +vt 0.826587 0.227602 +vt 0.811901 0.251935 +vt 0.826587 0.110116 +vt 0.519326 0.999288 +vt 0.501482 0.000046 +vt 0.519326 0.000046 +vt 0.519326 0.999289 +vt 0.501482 0.000046 +vt 0.519326 0.000046 +vt 0.519326 0.000046 +vt 0.501482 0.999288 +vt 0.501482 0.000046 +vt 0.519238 0.077023 +vt 0.501394 0.005648 +vt 0.519238 0.005648 +vt 0.519238 0.005648 +vt 0.501394 0.077023 +vt 0.501394 0.005648 +vt 0.501482 0.000046 +vt 0.519326 0.999288 +vt 0.501482 0.999288 +vt 0.501482 0.000046 +vt 0.519326 0.999289 +vt 0.501482 0.999289 +vt 0.501482 0.000046 +vt 0.519326 0.999288 +vt 0.501482 0.999288 +vt 0.519238 0.005648 +vt 0.501394 0.077023 +vt 0.501394 0.005648 +vt 0.519238 0.005648 +vt 0.501394 0.077023 +vt 0.501394 0.005648 +vt 0.518747 0.572730 +vt 0.500904 0.002768 +vt 0.518747 0.002768 +vt 0.500904 0.002767 +vt 0.518747 0.572730 +vt 0.500904 0.572730 +vt 0.500904 0.002768 +vt 0.518747 0.572730 +vt 0.500904 0.572730 +vt 0.500904 0.002767 +vt 0.518747 0.572730 +vt 0.500904 0.572730 +vt 0.519238 0.077023 +vt 0.501394 0.005648 +vt 0.519238 0.005648 +vt 0.519238 0.077023 +vt 0.501394 0.005648 +vt 0.519238 0.005648 +vt 0.500904 0.002398 +vt 0.518747 0.573100 +vt 0.500904 0.573100 +vt 0.500904 0.002398 +vt 0.518747 0.573100 +vt 0.500904 0.573100 +vt 0.500904 0.002398 +vt 0.518747 0.573100 +vt 0.500904 0.573100 +vt 0.500904 0.002397 +vt 0.518747 0.573100 +vt 0.500904 0.573100 +vt 0.519238 0.077023 +vt 0.501394 0.005648 +vt 0.519238 0.005648 +vt 0.519238 0.077023 +vt 0.501394 0.005648 +vt 0.519238 0.005648 +vt 0.582670 0.168860 +vt 0.679387 0.638804 +vt 0.576587 0.227603 +vt 0.561901 0.251935 +vt 0.728052 0.168860 +vt 0.576587 0.227604 +vt 0.582670 0.168861 +vt 0.679387 0.638805 +vt 0.561901 0.251936 +vt 0.582670 0.168860 +vt 0.679387 0.638805 +vt 0.576587 0.227603 +vt 0.561901 0.251935 +vt 0.582670 0.168859 +vt 0.679387 0.638804 +vt 0.576587 0.227602 +vt 0.561901 0.251935 +vt 0.547216 0.110116 +vt 0.576587 0.110116 +vt 0.269326 0.999288 +vt 0.251482 0.000046 +vt 0.269326 0.000046 +vt 0.269326 0.999288 +vt 0.251482 0.000046 +vt 0.269326 0.000046 +vt 0.269326 0.999288 +vt 0.251482 0.000046 +vt 0.269326 0.000046 +vt 0.269238 0.005648 +vt 0.251394 0.077022 +vt 0.251394 0.005648 +vt 0.269238 0.077023 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.251482 0.000046 +vt 0.269326 0.999288 +vt 0.251482 0.999288 +vt 0.251482 0.999288 +vt 0.269326 0.000046 +vt 0.269326 0.999288 +vt 0.251482 0.000046 +vt 0.269326 0.999288 +vt 0.251482 0.999288 +vt 0.269238 0.077023 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.269238 0.077023 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.268747 0.572730 +vt 0.250904 0.002768 +vt 0.268747 0.002768 +vt 0.250904 0.002767 +vt 0.268747 0.572730 +vt 0.250904 0.572730 +vt 0.250904 0.002768 +vt 0.268747 0.572730 +vt 0.250904 0.572730 +vt 0.250904 0.002767 +vt 0.268747 0.572730 +vt 0.250904 0.572730 +vt 0.269238 0.077022 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.269238 0.077023 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.250904 0.002398 +vt 0.268747 0.573100 +vt 0.250904 0.573100 +vt 0.250904 0.002398 +vt 0.268747 0.573100 +vt 0.250904 0.573100 +vt 0.250904 0.002398 +vt 0.268747 0.573100 +vt 0.250904 0.573100 +vt 0.250904 0.573100 +vt 0.268747 0.002397 +vt 0.268747 0.573100 +vt 0.269238 0.077023 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.269238 0.077023 +vt 0.251394 0.005648 +vt 0.269238 0.005648 +vt 0.478052 0.168860 +vt 0.326587 0.227603 +vt 0.332670 0.168860 +vt 0.429387 0.638805 +vt 0.311901 0.251935 +vt 0.478052 0.168860 +vt 0.326587 0.227604 +vt 0.332670 0.168861 +vt 0.429387 0.638805 +vt 0.311901 0.251936 +vt 0.332670 0.168860 +vt 0.429387 0.638805 +vt 0.326587 0.227603 +vt 0.311901 0.251935 +vt 0.478052 0.168860 +vt 0.326587 0.227603 +vt 0.332670 0.168859 +vt 0.311901 0.833462 +vt 0.311901 0.251935 +vt 0.297215 0.110116 +vt 0.326587 0.110116 +vt 0.019326 0.999288 +vt 0.001482 0.000046 +vt 0.019326 0.000046 +vt 0.019326 0.999288 +vt 0.001482 0.000046 +vt 0.019326 0.000046 +vt 0.019326 0.999288 +vt 0.001482 0.000046 +vt 0.019326 0.000046 +vt 0.019238 0.077023 +vt 0.001394 0.005648 +vt 0.019238 0.005648 +vt 0.019238 0.005648 +vt 0.001394 0.077022 +vt 0.001394 0.005648 +vt 0.001482 0.000046 +vt 0.019326 0.999288 +vt 0.001482 0.999288 +vt 0.001482 0.000046 +vt 0.019326 0.999288 +vt 0.001482 0.999288 +vt 0.001482 0.000046 +vt 0.019326 0.999288 +vt 0.001482 0.999288 +vt 0.019238 0.077023 +vt 0.001394 0.005648 +vt 0.019238 0.005648 +vt 0.019238 0.077023 +vt 0.001394 0.005648 +vt 0.019238 0.005648 +vt 0.018747 0.572730 +vt 0.000904 0.002768 +vt 0.018747 0.002768 +vt 0.000904 0.572730 +vt 0.018747 0.002767 +vt 0.018747 0.572730 +vt 0.000904 0.002768 +vt 0.018747 0.572730 +vt 0.000904 0.572730 +vt 0.000904 0.572730 +vt 0.018747 0.002767 +vt 0.018747 0.572730 +vt 0.019238 0.077022 +vt 0.001394 0.005648 +vt 0.019238 0.005648 +vt 0.019238 0.005648 +vt 0.001394 0.077023 +vt 0.001394 0.005648 +vt 0.000904 0.002398 +vt 0.018747 0.573100 +vt 0.000904 0.573100 +vt 0.000904 0.573100 +vt 0.018747 0.002398 +vt 0.018747 0.573100 +vt 0.000904 0.573100 +vt 0.018747 0.002398 +vt 0.018747 0.573100 +vt 0.000904 0.002397 +vt 0.018747 0.573100 +vt 0.000904 0.573100 +vt 0.019238 0.077023 +vt 0.001394 0.005648 +vt 0.019238 0.005648 +vt 0.019238 0.077023 +vt 0.001394 0.005648 +vt 0.019238 0.005648 +vt 0.228052 0.168860 +vt 0.076587 0.227603 +vt 0.082670 0.168860 +vt 0.179387 0.638805 +vt 0.061901 0.251935 +vt 0.082670 0.168861 +vt 0.179387 0.638805 +vt 0.076587 0.227604 +vt 0.061901 0.251936 +vt 0.228052 0.168860 +vt 0.076587 0.227603 +vt 0.082670 0.168860 +vt 0.179387 0.638805 +vt 0.061901 0.251935 +vt 0.228052 0.168860 +vt 0.076587 0.227603 +vt 0.082670 0.168860 +vt 0.179387 0.638804 +vt 0.061901 0.251935 +vt 0.047215 0.110116 +vt 0.076587 0.110116 +vt 0.751482 0.999288 +vt 0.751482 0.999289 +vt 0.751482 0.999288 +vt 0.751394 0.077023 +vt 0.751394 0.077023 +vt 0.769326 0.000046 +vt 0.769326 0.000046 +vt 0.769326 0.000046 +vt 0.751394 0.077023 +vt 0.751394 0.077023 +vt 0.750904 0.572730 +vt 0.750904 0.002767 +vt 0.768747 0.002768 +vt 0.750904 0.002767 +vt 0.751394 0.077023 +vt 0.751394 0.077023 +vt 0.768747 0.002398 +vt 0.750904 0.002398 +vt 0.768747 0.002398 +vt 0.768747 0.002397 +vt 0.751394 0.077023 +vt 0.751394 0.077023 +vt 0.811901 0.833462 +vt 0.929387 0.638805 +vt 0.811901 0.833462 +vt 0.811901 0.833462 +vt 0.797216 0.227603 +vt 0.791132 0.168859 +vt 0.811901 0.085784 +vt 0.501482 0.999288 +vt 0.501482 0.999289 +vt 0.501482 0.999288 +vt 0.501394 0.077023 +vt 0.501394 0.077023 +vt 0.519326 0.000046 +vt 0.519326 0.000046 +vt 0.519326 0.000046 +vt 0.501394 0.077023 +vt 0.501394 0.077023 +vt 0.500904 0.572730 +vt 0.518747 0.002767 +vt 0.518747 0.002768 +vt 0.518747 0.002767 +vt 0.501394 0.077023 +vt 0.501394 0.077023 +vt 0.518747 0.002398 +vt 0.518747 0.002398 +vt 0.518747 0.002398 +vt 0.500904 0.002397 +vt 0.501394 0.077023 +vt 0.501394 0.077023 +vt 0.561901 0.833462 +vt 0.561901 0.833462 +vt 0.728052 0.168860 +vt 0.561901 0.833462 +vt 0.561901 0.833462 +vt 0.547216 0.227603 +vt 0.541132 0.168859 +vt 0.561901 0.085784 +vt 0.251482 0.999288 +vt 0.269326 0.999288 +vt 0.251482 0.999288 +vt 0.251394 0.077022 +vt 0.251394 0.077022 +vt 0.269326 0.000046 +vt 0.251482 0.000046 +vt 0.269326 0.000046 +vt 0.251394 0.077023 +vt 0.251394 0.077023 +vt 0.250904 0.572730 +vt 0.268747 0.002767 +vt 0.268747 0.002768 +vt 0.268747 0.002767 +vt 0.251394 0.077022 +vt 0.251394 0.077023 +vt 0.268747 0.002398 +vt 0.268747 0.002398 +vt 0.268747 0.002398 +vt 0.268747 0.002397 +vt 0.251394 0.077022 +vt 0.251394 0.077023 +vt 0.311901 0.833462 +vt 0.311901 0.833462 +vt 0.478052 0.168860 +vt 0.311901 0.833462 +vt 0.311901 0.833462 +vt 0.297215 0.227603 +vt 0.291132 0.168860 +vt 0.311901 0.085784 +vt 0.001482 0.999288 +vt 0.001482 0.999288 +vt 0.001482 0.999288 +vt 0.001394 0.077022 +vt 0.001394 0.077022 +vt 0.019326 0.000046 +vt 0.019326 0.000046 +vt 0.019326 0.000046 +vt 0.001394 0.077023 +vt 0.001394 0.077023 +vt 0.000904 0.572730 +vt 0.018747 0.002767 +vt 0.018747 0.002768 +vt 0.018747 0.002767 +vt 0.001394 0.077022 +vt 0.001394 0.077023 +vt 0.018747 0.002398 +vt 0.018747 0.002398 +vt 0.018747 0.002398 +vt 0.000904 0.002397 +vt 0.001394 0.077022 +vt 0.001394 0.077023 +vt 0.179387 0.638805 +vt 0.061901 0.833462 +vt 0.061901 0.833462 +vt 0.061901 0.833462 +vt 0.047215 0.227603 +vt 0.041132 0.168860 +vt 0.061901 0.085784 +vt 0.769326 0.999288 +vt 0.751482 0.999289 +vt 0.769326 0.999288 +vt 0.751394 0.077023 +vt 0.751394 0.077023 +vt 0.751482 0.000046 +vt 0.769326 0.000046 +vt 0.769326 0.000046 +vt 0.751394 0.077023 +vt 0.751394 0.077023 +vt 0.768747 0.572730 +vt 0.750904 0.002767 +vt 0.768747 0.002768 +vt 0.768747 0.002767 +vt 0.751394 0.077023 +vt 0.751394 0.077023 +vt 0.750904 0.002398 +vt 0.768747 0.002398 +vt 0.750904 0.002398 +vt 0.768747 0.002397 +vt 0.769238 0.077023 +vt 0.751394 0.077023 +vt 0.978052 0.168860 +vt 0.978052 0.168860 +vt 0.811901 0.833462 +vt 0.978052 0.168860 +vt 0.811901 0.833462 +vt 0.797216 0.227603 +vt 0.791132 0.168859 +vt 0.797216 0.110116 +vt 0.811901 0.085784 +vt 0.501482 0.999288 +vt 0.501482 0.999289 +vt 0.519326 0.999288 +vt 0.501394 0.077023 +vt 0.519238 0.077023 +vt 0.519326 0.000046 +vt 0.519326 0.000046 +vt 0.519326 0.000046 +vt 0.519238 0.077023 +vt 0.519238 0.077023 +vt 0.500904 0.572730 +vt 0.518747 0.002767 +vt 0.518747 0.002768 +vt 0.518747 0.002767 +vt 0.501394 0.077023 +vt 0.501394 0.077023 +vt 0.518747 0.002398 +vt 0.518747 0.002398 +vt 0.518747 0.002398 +vt 0.518747 0.002397 +vt 0.501394 0.077023 +vt 0.501394 0.077023 +vt 0.728052 0.168860 +vt 0.561901 0.833462 +vt 0.561901 0.833462 +vt 0.728052 0.168860 +vt 0.561901 0.833462 +vt 0.728052 0.168860 +vt 0.561901 0.833462 +vt 0.547216 0.227603 +vt 0.541132 0.168859 +vt 0.561901 0.085784 +vt 0.251482 0.999288 +vt 0.251482 0.999288 +vt 0.251482 0.999288 +vt 0.269238 0.077023 +vt 0.251394 0.077022 +vt 0.269326 0.000046 +vt 0.251482 0.000046 +vt 0.269326 0.000046 +vt 0.251394 0.077023 +vt 0.251394 0.077023 +vt 0.250904 0.572730 +vt 0.268747 0.002767 +vt 0.268747 0.002768 +vt 0.268747 0.002767 +vt 0.251394 0.077022 +vt 0.251394 0.077023 +vt 0.268747 0.002398 +vt 0.268747 0.002398 +vt 0.268747 0.002398 +vt 0.250904 0.002397 +vt 0.251394 0.077022 +vt 0.251394 0.077023 +vt 0.311901 0.833462 +vt 0.311901 0.833462 +vt 0.478052 0.168860 +vt 0.311901 0.833462 +vt 0.429387 0.638804 +vt 0.297215 0.227603 +vt 0.291132 0.168860 +vt 0.311901 0.085784 +vt 0.001482 0.999288 +vt 0.001482 0.999288 +vt 0.001482 0.999288 +vt 0.001394 0.077022 +vt 0.019238 0.077023 +vt 0.019326 0.000046 +vt 0.019326 0.000046 +vt 0.019326 0.000046 +vt 0.001394 0.077023 +vt 0.001394 0.077023 +vt 0.000904 0.572730 +vt 0.000904 0.002767 +vt 0.018747 0.002768 +vt 0.000904 0.002767 +vt 0.001394 0.077022 +vt 0.019238 0.077023 +vt 0.018747 0.002398 +vt 0.000904 0.002398 +vt 0.000904 0.002398 +vt 0.018747 0.002397 +vt 0.001394 0.077022 +vt 0.001394 0.077023 +vt 0.061901 0.833462 +vt 0.228052 0.168860 +vt 0.061901 0.833462 +vt 0.061901 0.833462 +vt 0.061901 0.833462 +vt 0.047215 0.227603 +vt 0.041132 0.168860 +vt 0.061901 0.085784 +vn 0.3827 0.0000 0.9239 +vn 0.0000 -1.0000 0.0000 +vn -0.3827 0.0000 -0.9239 +vn 0.9239 0.0000 -0.3827 +vn -0.9239 0.0000 0.3827 +vn -0.3827 0.0000 0.9239 +vn 0.3827 0.0000 -0.9239 +vn 0.0000 1.0000 0.0000 +vn 0.9239 0.0000 0.3827 +vn -0.9239 0.0000 -0.3827 +vn -0.7777 -0.6287 0.0000 +vn -0.5499 -0.6287 -0.5499 +vn 0.0000 -0.6287 -0.7777 +vn 0.5499 -0.6287 -0.5499 +vn 0.7777 -0.6287 0.0000 +vn 0.5499 -0.6287 0.5499 +vn 0.0000 -0.6287 0.7777 +vn -0.5499 -0.6287 0.5499 +vn 0.7071 0.0000 0.7071 +vn -0.7071 0.0000 -0.7071 +vn 0.7071 0.0000 -0.7071 +vn -0.7071 0.0000 0.7071 +vn 0.0000 0.0000 1.0000 +vn -0.0000 0.0000 -1.0000 +vn 1.0000 0.0000 0.0000 +vn -1.0000 0.0000 0.0000 +vn -0.7185 -0.6287 0.2976 +vn -0.7185 -0.6287 -0.2976 +vn -0.2976 -0.6287 -0.7185 +vn 0.2976 -0.6287 -0.7185 +vn 0.7185 -0.6287 -0.2976 +vn 0.7185 -0.6287 0.2976 +vn 0.2976 -0.6287 0.7185 +vn -0.2976 -0.6287 0.7185 +vn 0.3827 0.9239 0.0000 +vn -0.3827 -0.9239 0.0000 +vn 0.9239 -0.3827 0.0000 +vn -0.9239 0.3827 -0.0000 +vn -0.3827 0.9239 0.0000 +vn 0.3827 -0.9239 -0.0000 +vn 0.9239 0.3827 -0.0000 +vn -0.9239 -0.3827 0.0000 +vn -0.7777 0.0000 0.6287 +vn -0.5499 -0.5499 0.6287 +vn 0.0000 -0.7777 0.6287 +vn 0.5499 -0.5499 0.6287 +vn 0.7777 0.0000 0.6287 +vn 0.5499 0.5499 0.6287 +vn 0.0000 0.7777 0.6287 +vn -0.5499 0.5499 0.6287 +vn 0.7071 0.7071 -0.0000 +vn -0.7071 -0.7071 0.0000 +vn 0.7071 -0.7071 -0.0000 +vn -0.7071 0.7071 0.0000 +vn -0.7185 0.2976 0.6287 +vn -0.7185 -0.2976 0.6287 +vn -0.2976 -0.7185 0.6287 +vn 0.2976 -0.7185 0.6287 +vn 0.7185 -0.2976 0.6287 +vn 0.7185 0.2976 0.6287 +vn 0.2976 0.7185 0.6287 +vn -0.2976 0.7185 0.6287 +s off +f 28/39/5 29/40/5 27/41/5 +f 30/42/6 33/43/6 29/44/6 +f 34/45/7 31/46/7 33/47/7 +f 33/48/8 27/49/8 29/50/8 +f 30/51/9 32/52/9 34/53/9 +f 36/54/9 37/55/9 35/56/9 +f 38/57/6 41/58/6 37/59/6 +f 42/60/8 39/61/8 41/62/8 +f 41/63/5 35/64/5 37/65/5 +f 38/66/7 40/67/7 42/68/7 +f 44/69/10 45/70/10 43/71/10 +f 45/72/6 50/73/6 49/74/6 +f 50/75/11 47/76/11 49/77/11 +f 47/78/12 44/79/12 43/80/12 +f 49/81/13 43/82/13 45/83/13 +f 46/84/14 48/85/14 50/86/14 +f 52/87/14 53/88/14 51/89/14 +f 53/90/6 58/91/6 57/92/6 +f 58/93/13 55/94/13 57/95/13 +f 56/96/12 51/97/12 55/98/12 +f 57/99/10 51/100/10 53/101/10 +f 54/102/11 56/103/11 58/104/11 +f 60/105/15 61/106/15 59/107/15 +f 62/108/16 63/109/16 61/106/16 +f 64/110/17 65/111/17 63/112/17 +f 65/111/18 68/113/18 67/114/18 +f 68/115/19 69/116/19 67/117/19 +f 70/118/20 71/119/20 69/116/20 +f 72/120/21 73/121/21 71/122/21 +f 74/123/22 59/124/22 73/121/22 +f 65/125/6 69/126/6 73/121/6 +f 76/127/23 77/128/23 75/129/23 +f 78/130/6 81/131/6 77/132/6 +f 82/133/24 79/134/24 81/135/24 +f 81/136/25 75/137/25 77/138/25 +f 78/139/26 80/140/26 82/141/26 +f 84/142/26 85/143/26 83/144/26 +f 86/145/6 89/146/6 85/147/6 +f 90/148/25 87/149/25 89/150/25 +f 89/151/23 83/152/23 85/153/23 +f 86/154/24 88/155/24 90/156/24 +f 92/157/27 93/158/27 91/159/27 +f 94/160/6 97/161/6 93/162/6 +f 98/163/28 95/164/28 97/165/28 +f 96/166/12 91/167/12 95/168/12 +f 97/169/29 91/170/29 93/171/29 +f 94/172/30 96/173/30 98/174/30 +f 100/175/30 101/176/30 99/177/30 +f 102/178/6 105/179/6 101/180/6 +f 106/181/29 103/182/29 105/183/29 +f 103/184/12 100/185/12 99/186/12 +f 105/187/27 99/188/27 101/189/27 +f 102/190/28 104/191/28 106/192/28 +f 108/193/31 109/194/31 107/195/31 +f 110/196/32 111/197/32 109/194/32 +f 112/198/33 113/199/33 111/200/33 +f 114/201/34 115/202/34 113/199/34 +f 115/203/35 118/204/35 117/205/35 +f 118/204/36 119/206/36 117/205/36 +f 120/207/37 121/208/37 119/209/37 +f 122/210/38 107/211/38 121/208/38 +f 113/212/6 117/213/6 121/208/6 +f 124/214/13 125/215/13 123/216/13 +f 125/217/6 130/218/6 129/219/6 +f 130/220/14 127/221/14 129/222/14 +f 129/223/11 123/224/11 125/225/11 +f 126/226/10 128/227/10 130/228/10 +f 132/229/10 133/230/10 131/231/10 +f 133/232/6 138/233/6 137/234/6 +f 138/235/11 135/236/11 137/237/11 +f 137/238/13 131/239/13 133/240/13 +f 134/241/14 136/242/14 138/243/14 +f 140/244/5 141/245/5 139/246/5 +f 142/247/6 145/248/6 141/249/6 +f 146/250/7 143/251/7 145/252/7 +f 144/253/12 139/254/12 143/255/12 +f 145/256/8 139/257/8 141/258/8 +f 142/259/9 144/260/9 146/261/9 +f 148/262/9 149/263/9 147/264/9 +f 150/265/6 153/266/6 149/267/6 +f 154/268/8 151/269/8 153/270/8 +f 152/271/12 147/272/12 151/273/12 +f 153/274/5 147/275/5 149/276/5 +f 150/277/7 152/278/7 154/279/7 +f 156/280/22 157/281/22 155/282/22 +f 158/283/15 159/284/15 157/281/15 +f 160/285/16 161/286/16 159/287/16 +f 162/288/17 163/289/17 161/286/17 +f 163/290/18 166/291/18 165/292/18 +f 166/291/19 167/293/19 165/292/19 +f 168/294/20 169/295/20 167/296/20 +f 170/297/21 155/298/21 169/295/21 +f 161/299/6 165/300/6 169/295/6 +f 172/301/29 173/302/29 171/303/29 +f 174/304/6 177/305/6 173/306/6 +f 178/307/30 175/308/30 177/309/30 +f 177/310/28 171/311/28 173/312/28 +f 174/313/27 176/314/27 178/315/27 +f 180/316/27 181/317/27 179/318/27 +f 182/319/6 185/320/6 181/321/6 +f 186/322/28 183/323/28 185/324/28 +f 185/325/29 179/326/29 181/327/29 +f 182/328/30 184/329/30 186/330/30 +f 188/331/23 189/332/23 187/333/23 +f 190/334/6 193/335/6 189/336/6 +f 194/337/24 191/338/24 193/339/24 +f 192/340/12 187/341/12 191/342/12 +f 193/343/25 187/344/25 189/345/25 +f 190/346/26 192/347/26 194/348/26 +f 196/349/26 197/350/26 195/351/26 +f 198/352/6 201/353/6 197/354/6 +f 202/355/25 199/356/25 201/357/25 +f 199/358/12 196/359/12 195/360/12 +f 201/361/23 195/362/23 197/363/23 +f 198/364/24 200/365/24 202/366/24 +f 204/367/38 205/368/38 203/369/38 +f 205/368/31 208/370/31 207/371/31 +f 208/372/32 209/373/32 207/374/32 +f 210/375/33 211/376/33 209/373/33 +f 212/377/34 213/378/34 211/379/34 +f 214/380/35 215/381/35 213/378/35 +f 216/382/36 217/383/36 215/384/36 +f 218/385/37 203/386/37 217/383/37 +f 209/387/6 213/388/6 217/383/6 +f 219/389/39 222/390/39 221/391/39 +f 222/392/27 225/393/27 221/394/27 +f 225/395/40 224/396/40 223/397/40 +f 225/398/41 219/399/41 221/400/41 +f 222/401/42 224/402/42 226/403/42 +f 227/404/42 230/405/42 229/406/42 +f 230/407/27 233/408/27 229/409/27 +f 234/410/41 231/411/41 233/412/41 +f 233/413/39 227/414/39 229/415/39 +f 230/416/40 232/417/40 234/418/40 +f 235/419/43 238/420/43 237/421/43 +f 237/422/27 242/423/27 241/424/27 +f 242/425/44 239/426/44 241/427/44 +f 240/428/28 235/429/28 239/430/28 +f 241/431/45 235/432/45 237/433/45 +f 238/434/46 240/435/46 242/436/46 +f 243/437/46 246/438/46 245/439/46 +f 246/440/27 249/441/27 245/442/27 +f 249/443/45 248/444/45 247/445/45 +f 248/446/28 243/447/28 247/448/28 +f 245/449/43 247/450/43 243/451/43 +f 246/452/44 248/453/44 250/454/44 +f 251/455/47 254/456/47 253/457/47 +f 253/457/48 256/458/48 255/459/48 +f 255/460/49 258/461/49 257/462/49 +f 257/462/50 260/463/50 259/464/50 +f 260/465/51 261/466/51 259/467/51 +f 262/468/52 263/469/52 261/466/52 +f 263/470/53 266/471/53 265/472/53 +f 266/471/54 251/473/54 265/472/54 +f 261/474/27 263/470/27 265/472/27 +f 268/475/55 269/476/55 267/477/55 +f 270/478/27 273/479/27 269/480/27 +f 273/481/56 272/482/56 271/483/56 +f 273/484/57 267/485/57 269/486/57 +f 274/487/58 268/488/58 272/489/58 +f 276/490/58 277/491/58 275/492/58 +f 278/493/27 281/494/27 277/495/27 +f 282/496/57 279/497/57 281/498/57 +f 277/499/55 279/500/55 275/501/55 +f 282/502/56 276/503/56 280/504/56 +f 284/505/12 285/506/12 283/507/12 +f 286/508/27 289/509/27 285/510/27 +f 290/511/6 287/512/6 289/513/6 +f 288/514/28 283/515/28 287/516/28 +f 289/517/29 283/518/29 285/519/29 +f 286/520/30 288/521/30 290/522/30 +f 292/523/30 293/524/30 291/525/30 +f 294/526/27 297/527/27 293/528/27 +f 298/529/29 295/530/29 297/531/29 +f 296/532/28 291/533/28 295/534/28 +f 297/535/12 291/536/12 293/537/12 +f 294/538/6 296/539/6 298/540/6 +f 299/541/59 302/542/59 301/543/59 +f 302/542/60 303/544/60 301/543/60 +f 304/545/61 305/546/61 303/547/61 +f 306/548/62 307/549/62 305/546/62 +f 307/550/63 310/551/63 309/552/63 +f 310/551/64 311/553/64 309/552/64 +f 311/554/65 314/555/65 313/556/65 +f 314/555/66 299/557/66 313/556/66 +f 305/558/27 309/559/27 313/556/27 +f 316/560/45 317/561/45 315/562/45 +f 318/563/27 321/564/27 317/565/27 +f 322/566/46 319/567/46 321/568/46 +f 317/569/44 319/570/44 315/571/44 +f 318/572/43 320/573/43 322/574/43 +f 324/575/43 325/576/43 323/577/43 +f 325/578/27 330/579/27 329/580/27 +f 330/581/44 327/582/44 329/583/44 +f 329/584/45 323/585/45 325/586/45 +f 326/587/46 328/588/46 330/589/46 +f 332/590/39 333/591/39 331/592/39 +f 334/593/27 337/594/27 333/595/27 +f 338/596/40 335/597/40 337/598/40 +f 336/599/28 331/600/28 335/601/28 +f 337/602/41 331/603/41 333/604/41 +f 334/605/42 336/606/42 338/607/42 +f 340/608/42 341/609/42 339/610/42 +f 342/611/27 345/612/27 341/613/27 +f 346/614/41 343/615/41 345/616/41 +f 343/617/28 340/618/28 339/619/28 +f 345/620/39 339/621/39 341/622/39 +f 342/623/40 344/624/40 346/625/40 +f 348/626/54 349/627/54 347/628/54 +f 350/629/47 351/630/47 349/627/47 +f 352/631/48 353/632/48 351/633/48 +f 354/634/49 355/635/49 353/632/49 +f 355/636/50 358/637/50 357/638/50 +f 358/637/51 359/639/51 357/638/51 +f 360/640/52 361/641/52 359/642/52 +f 361/641/53 348/643/53 347/644/53 +f 353/645/27 357/646/27 361/641/27 +f 364/647/29 365/648/29 363/649/29 +f 366/650/27 369/651/27 365/652/27 +f 370/653/30 367/654/30 369/655/30 +f 369/656/6 363/657/6 365/658/6 +f 370/659/12 364/660/12 368/661/12 +f 372/662/12 373/663/12 371/664/12 +f 374/665/27 377/666/27 373/667/27 +f 378/668/6 375/669/6 377/670/6 +f 377/671/29 371/672/29 373/673/29 +f 374/674/30 376/675/30 378/676/30 +f 380/677/55 381/678/55 379/679/55 +f 381/680/27 386/681/27 385/682/27 +f 386/683/56 383/684/56 385/685/56 +f 383/686/28 380/687/28 379/688/28 +f 385/689/57 379/690/57 381/691/57 +f 386/692/58 380/693/58 384/694/58 +f 388/695/58 389/696/58 387/697/58 +f 389/698/27 394/699/27 393/700/27 +f 393/701/57 392/702/57 391/703/57 +f 392/704/28 387/705/28 391/706/28 +f 393/707/55 387/708/55 389/709/55 +f 390/710/56 392/711/56 394/712/56 +f 396/713/66 397/714/66 395/715/66 +f 398/716/59 399/717/59 397/714/59 +f 399/718/60 402/719/60 401/720/60 +f 402/719/61 403/721/61 401/720/61 +f 404/722/62 405/723/62 403/724/62 +f 406/725/63 407/726/63 405/723/63 +f 408/727/64 409/728/64 407/729/64 +f 410/730/65 395/731/65 409/728/65 +f 401/732/27 405/733/27 409/728/27 +f 28/39/5 30/734/5 29/40/5 +f 30/42/6 34/735/6 33/43/6 +f 34/45/7 32/736/7 31/46/7 +f 33/48/8 31/737/8 27/49/8 +f 30/51/9 28/738/9 32/52/9 +f 36/54/9 38/739/9 37/55/9 +f 38/57/6 42/740/6 41/58/6 +f 42/60/8 40/741/8 39/61/8 +f 41/63/5 39/742/5 35/64/5 +f 38/66/7 36/743/7 40/67/7 +f 44/69/10 46/744/10 45/70/10 +f 45/72/6 46/745/6 50/73/6 +f 50/75/11 48/746/11 47/76/11 +f 47/78/12 48/747/12 44/79/12 +f 49/81/13 47/748/13 43/82/13 +f 46/84/14 44/749/14 48/85/14 +f 52/87/14 54/750/14 53/88/14 +f 53/90/6 54/751/6 58/91/6 +f 58/93/13 56/752/13 55/94/13 +f 56/96/12 52/753/12 51/97/12 +f 57/99/10 55/754/10 51/100/10 +f 54/102/11 52/755/11 56/103/11 +f 60/105/15 62/108/15 61/106/15 +f 62/108/16 64/756/16 63/109/16 +f 64/110/17 66/757/17 65/111/17 +f 65/111/18 66/757/18 68/113/18 +f 68/115/19 70/118/19 69/116/19 +f 70/118/20 72/758/20 71/119/20 +f 72/120/21 74/123/21 73/121/21 +f 74/123/22 60/759/22 59/124/22 +f 73/121/6 59/124/6 61/760/6 +f 61/760/6 63/761/6 73/121/6 +f 63/761/6 65/125/6 73/121/6 +f 65/125/6 67/762/6 69/126/6 +f 69/126/6 71/122/6 73/121/6 +f 76/127/23 78/763/23 77/128/23 +f 78/130/6 82/764/6 81/131/6 +f 82/133/24 80/765/24 79/134/24 +f 81/136/25 79/766/25 75/137/25 +f 78/139/26 76/767/26 80/140/26 +f 84/142/26 86/768/26 85/143/26 +f 86/145/6 90/769/6 89/146/6 +f 90/148/25 88/770/25 87/149/25 +f 89/151/23 87/771/23 83/152/23 +f 86/154/24 84/772/24 88/155/24 +f 92/157/27 94/773/27 93/158/27 +f 94/160/6 98/774/6 97/161/6 +f 98/163/28 96/775/28 95/164/28 +f 96/166/12 92/776/12 91/167/12 +f 97/169/29 95/777/29 91/170/29 +f 94/172/30 92/778/30 96/173/30 +f 100/175/30 102/779/30 101/176/30 +f 102/178/6 106/780/6 105/179/6 +f 106/181/29 104/781/29 103/182/29 +f 103/184/12 104/782/12 100/185/12 +f 105/187/27 103/783/27 99/188/27 +f 102/190/28 100/784/28 104/191/28 +f 108/193/31 110/196/31 109/194/31 +f 110/196/32 112/785/32 111/197/32 +f 112/198/33 114/201/33 113/199/33 +f 114/201/34 116/786/34 115/202/34 +f 115/203/35 116/787/35 118/204/35 +f 118/204/36 120/788/36 119/206/36 +f 120/207/37 122/210/37 121/208/37 +f 122/210/38 108/789/38 107/211/38 +f 121/208/6 107/211/6 109/790/6 +f 109/790/6 111/791/6 121/208/6 +f 111/791/6 113/212/6 121/208/6 +f 113/212/6 115/792/6 117/213/6 +f 117/213/6 119/209/6 121/208/6 +f 124/214/13 126/793/13 125/215/13 +f 125/217/6 126/794/6 130/218/6 +f 130/220/14 128/795/14 127/221/14 +f 129/223/11 127/796/11 123/224/11 +f 126/226/10 124/797/10 128/227/10 +f 132/229/10 134/798/10 133/230/10 +f 133/232/6 134/799/6 138/233/6 +f 138/235/11 136/800/11 135/236/11 +f 137/238/13 135/801/13 131/239/13 +f 134/241/14 132/802/14 136/242/14 +f 140/244/5 142/803/5 141/245/5 +f 142/247/6 146/804/6 145/248/6 +f 146/250/7 144/805/7 143/251/7 +f 144/253/12 140/806/12 139/254/12 +f 145/256/8 143/807/8 139/257/8 +f 142/259/9 140/808/9 144/260/9 +f 148/262/9 150/809/9 149/263/9 +f 150/265/6 154/810/6 153/266/6 +f 154/268/8 152/811/8 151/269/8 +f 152/271/12 148/812/12 147/272/12 +f 153/274/5 151/813/5 147/275/5 +f 150/277/7 148/814/7 152/278/7 +f 156/280/22 158/283/22 157/281/22 +f 158/283/15 160/815/15 159/284/15 +f 160/285/16 162/288/16 161/286/16 +f 162/288/17 164/816/17 163/289/17 +f 163/290/18 164/817/18 166/291/18 +f 166/291/19 168/818/19 167/293/19 +f 168/294/20 170/297/20 169/295/20 +f 170/297/21 156/819/21 155/298/21 +f 169/295/6 155/298/6 157/820/6 +f 157/820/6 159/821/6 169/295/6 +f 159/821/6 161/299/6 169/295/6 +f 161/299/6 163/822/6 165/300/6 +f 165/300/6 167/296/6 169/295/6 +f 172/301/29 174/823/29 173/302/29 +f 174/304/6 178/824/6 177/305/6 +f 178/307/30 176/825/30 175/308/30 +f 177/310/28 175/826/28 171/311/28 +f 174/313/27 172/827/27 176/314/27 +f 180/316/27 182/828/27 181/317/27 +f 182/319/6 186/829/6 185/320/6 +f 186/322/28 184/830/28 183/323/28 +f 185/325/29 183/831/29 179/326/29 +f 182/328/30 180/832/30 184/329/30 +f 188/331/23 190/833/23 189/332/23 +f 190/334/6 194/834/6 193/335/6 +f 194/337/24 192/835/24 191/338/24 +f 192/340/12 188/836/12 187/341/12 +f 193/343/25 191/837/25 187/344/25 +f 190/346/26 188/838/26 192/347/26 +f 196/349/26 198/839/26 197/350/26 +f 198/352/6 202/840/6 201/353/6 +f 202/355/25 200/841/25 199/356/25 +f 199/358/12 200/842/12 196/359/12 +f 201/361/23 199/843/23 195/362/23 +f 198/364/24 196/844/24 200/365/24 +f 204/367/38 206/845/38 205/368/38 +f 205/368/31 206/845/31 208/370/31 +f 208/372/32 210/375/32 209/373/32 +f 210/375/33 212/846/33 211/376/33 +f 212/377/34 214/380/34 213/378/34 +f 214/380/35 216/847/35 215/381/35 +f 216/382/36 218/385/36 217/383/36 +f 218/385/37 204/848/37 203/386/37 +f 217/383/6 203/386/6 205/849/6 +f 205/849/6 207/850/6 217/383/6 +f 207/850/6 209/387/6 217/383/6 +f 209/387/6 211/851/6 213/388/6 +f 213/388/6 215/384/6 217/383/6 +f 219/389/39 220/852/39 222/390/39 +f 222/392/27 226/853/27 225/393/27 +f 225/395/40 226/854/40 224/396/40 +f 225/398/41 223/855/41 219/399/41 +f 222/401/42 220/856/42 224/402/42 +f 227/404/42 228/857/42 230/405/42 +f 230/407/27 234/858/27 233/408/27 +f 234/410/41 232/859/41 231/411/41 +f 233/413/39 231/860/39 227/414/39 +f 230/416/40 228/861/40 232/417/40 +f 235/419/43 236/862/43 238/420/43 +f 237/422/27 238/863/27 242/423/27 +f 242/425/44 240/864/44 239/426/44 +f 240/428/28 236/865/28 235/429/28 +f 241/431/45 239/866/45 235/432/45 +f 238/434/46 236/867/46 240/435/46 +f 243/437/46 244/868/46 246/438/46 +f 246/440/27 250/869/27 249/441/27 +f 249/443/45 250/870/45 248/444/45 +f 248/446/28 244/871/28 243/447/28 +f 245/449/43 249/872/43 247/450/43 +f 246/452/44 244/873/44 248/453/44 +f 251/455/47 252/874/47 254/456/47 +f 253/457/48 254/456/48 256/458/48 +f 255/460/49 256/875/49 258/461/49 +f 257/462/50 258/461/50 260/463/50 +f 260/465/51 262/468/51 261/466/51 +f 262/468/52 264/876/52 263/469/52 +f 263/470/53 264/877/53 266/471/53 +f 266/471/54 252/878/54 251/473/54 +f 265/472/27 251/473/27 253/879/27 +f 253/879/27 255/880/27 265/472/27 +f 255/880/27 257/881/27 265/472/27 +f 257/881/27 259/882/27 265/472/27 +f 259/882/27 261/474/27 265/472/27 +f 268/475/55 270/883/55 269/476/55 +f 270/478/27 274/884/27 273/479/27 +f 273/481/56 274/885/56 272/482/56 +f 273/484/57 271/886/57 267/485/57 +f 274/487/58 270/887/58 268/488/58 +f 276/490/58 278/888/58 277/491/58 +f 278/493/27 282/889/27 281/494/27 +f 282/496/57 280/890/57 279/497/57 +f 277/499/55 281/891/55 279/500/55 +f 282/502/56 278/892/56 276/503/56 +f 284/505/12 286/893/12 285/506/12 +f 286/508/27 290/894/27 289/509/27 +f 290/511/6 288/895/6 287/512/6 +f 288/514/28 284/896/28 283/515/28 +f 289/517/29 287/897/29 283/518/29 +f 286/520/30 284/898/30 288/521/30 +f 292/523/30 294/899/30 293/524/30 +f 294/526/27 298/900/27 297/527/27 +f 298/529/29 296/901/29 295/530/29 +f 296/532/28 292/902/28 291/533/28 +f 297/535/12 295/903/12 291/536/12 +f 294/538/6 292/904/6 296/539/6 +f 299/541/59 300/905/59 302/542/59 +f 302/542/60 304/906/60 303/544/60 +f 304/545/61 306/548/61 305/546/61 +f 306/548/62 308/907/62 307/549/62 +f 307/550/63 308/908/63 310/551/63 +f 310/551/64 312/909/64 311/553/64 +f 311/554/65 312/910/65 314/555/65 +f 314/555/66 300/911/66 299/557/66 +f 313/556/27 299/557/27 301/912/27 +f 301/912/27 303/913/27 313/556/27 +f 303/913/27 305/558/27 313/556/27 +f 305/558/27 307/914/27 309/559/27 +f 309/559/27 311/554/27 313/556/27 +f 316/560/45 318/915/45 317/561/45 +f 318/563/27 322/916/27 321/564/27 +f 322/566/46 320/917/46 319/567/46 +f 317/569/44 321/918/44 319/570/44 +f 318/572/43 316/919/43 320/573/43 +f 324/575/43 326/920/43 325/576/43 +f 325/578/27 326/921/27 330/579/27 +f 330/581/44 328/922/44 327/582/44 +f 329/584/45 327/923/45 323/585/45 +f 326/587/46 324/924/46 328/588/46 +f 332/590/39 334/925/39 333/591/39 +f 334/593/27 338/926/27 337/594/27 +f 338/596/40 336/927/40 335/597/40 +f 336/599/28 332/928/28 331/600/28 +f 337/602/41 335/929/41 331/603/41 +f 334/605/42 332/930/42 336/606/42 +f 340/608/42 342/931/42 341/609/42 +f 342/611/27 346/932/27 345/612/27 +f 346/614/41 344/933/41 343/615/41 +f 343/617/28 344/934/28 340/618/28 +f 345/620/39 343/935/39 339/621/39 +f 342/623/40 340/936/40 344/624/40 +f 348/626/54 350/629/54 349/627/54 +f 350/629/47 352/937/47 351/630/47 +f 352/631/48 354/634/48 353/632/48 +f 354/634/49 356/938/49 355/635/49 +f 355/636/50 356/939/50 358/637/50 +f 358/637/51 360/940/51 359/639/51 +f 360/640/52 362/941/52 361/641/52 +f 361/641/53 362/941/53 348/643/53 +f 361/641/27 347/644/27 353/645/27 +f 347/644/27 349/942/27 353/645/27 +f 349/942/27 351/943/27 353/645/27 +f 353/645/27 355/944/27 357/646/27 +f 357/646/27 359/642/27 361/641/27 +f 364/647/29 366/945/29 365/648/29 +f 366/650/27 370/946/27 369/651/27 +f 370/653/30 368/947/30 367/654/30 +f 369/656/6 367/948/6 363/657/6 +f 370/659/12 366/949/12 364/660/12 +f 372/662/12 374/950/12 373/663/12 +f 374/665/27 378/951/27 377/666/27 +f 378/668/6 376/952/6 375/669/6 +f 377/671/29 375/953/29 371/672/29 +f 374/674/30 372/954/30 376/675/30 +f 380/677/55 382/955/55 381/678/55 +f 381/680/27 382/956/27 386/681/27 +f 386/683/56 384/957/56 383/684/56 +f 383/686/28 384/958/28 380/687/28 +f 385/689/57 383/959/57 379/690/57 +f 386/692/58 382/960/58 380/693/58 +f 388/695/58 390/961/58 389/696/58 +f 389/698/27 390/962/27 394/699/27 +f 393/701/57 394/963/57 392/702/57 +f 392/704/28 388/964/28 387/705/28 +f 393/707/55 391/965/55 387/708/55 +f 390/710/56 388/966/56 392/711/56 +f 396/713/66 398/716/66 397/714/66 +f 398/716/59 400/967/59 399/717/59 +f 399/718/60 400/968/60 402/719/60 +f 402/719/61 404/969/61 403/721/61 +f 404/722/62 406/725/62 405/723/62 +f 406/725/63 408/970/63 407/726/63 +f 408/727/64 410/730/64 409/728/64 +f 410/730/65 396/971/65 395/731/65 +f 409/728/27 395/731/27 397/972/27 +f 397/972/27 399/973/27 401/732/27 +f 401/732/27 403/974/27 405/733/27 +f 405/733/27 407/729/27 409/728/27 +f 409/728/27 397/972/27 401/732/27 +g drill_4_Cylinder.003 +v 0.250000 0.250000 -0.500000 +v -0.250000 0.250000 -0.500000 +v 0.500000 0.500000 0.500000 +v -0.500000 -0.500000 0.500000 +v 0.500000 0.500000 0.000000 +v 0.500000 -0.500000 0.500000 +v -0.500000 0.500000 0.000000 +v -0.500000 0.500000 0.500000 +v 0.250000 0.500000 0.000000 +v -0.250000 0.500000 0.000000 +v 0.500000 0.000000 -0.500000 +v -0.250000 0.500000 -0.250000 +v -0.500000 -0.500000 -0.500000 +v 0.500000 -0.500000 -0.500000 +v -0.500000 0.000000 -0.500000 +v 0.250000 0.500000 -0.250000 +v 0.250000 0.000000 -0.500000 +v -0.250000 0.000000 -0.500000 +v -0.500000 0.000000 0.000000 +v 0.500000 -0.000000 -0.000000 +v -0.250000 0.000000 0.000000 +v 0.250000 0.250000 -0.250000 +v -0.250000 0.250000 -0.250000 +v 0.250000 -0.000000 -0.000000 +v -0.250000 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 0.500000 +vt 0.500000 0.500000 +vt 0.500000 0.500000 +vt 1.000000 0.000000 +vt 0.000000 0.000000 +vt 0.750000 0.750000 +vt 0.500000 1.000000 +vt 0.500000 0.500000 +vt 0.750000 0.750000 +vt 1.000000 0.750000 +vt 1.000000 0.500000 +vt 0.500000 1.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vt 0.000000 1.000000 +vt 0.500000 1.000000 +vt 1.000000 0.500000 +vt 1.000000 0.500000 +vt 1.000000 0.750000 +vt 0.750000 1.000000 +vt 0.500000 0.500000 +vt 0.500000 1.000000 +vt 0.750000 1.000000 +vn 1.0000 0.0000 0.0000 +vn -1.0000 -0.0000 -0.0000 +s off +f 424/975/67 421/976/67 430/977/67 +f 429/978/68 423/979/68 414/980/68 +f 432/981/67 419/982/67 434/983/67 +f 433/984/68 412/985/68 428/986/68 +f 430/977/67 415/987/67 413/988/67 +f 413/988/67 416/989/67 430/977/67 +f 416/989/67 424/975/67 430/977/67 +f 414/980/68 418/990/68 429/978/68 +f 418/990/68 417/991/68 429/978/68 +f 429/978/68 425/992/68 423/979/68 +f 434/983/67 427/993/67 432/981/67 +f 427/993/67 411/994/67 432/981/67 +f 432/981/67 426/995/67 419/982/67 +f 428/986/68 431/996/68 433/984/68 +f 431/996/68 420/997/68 433/984/68 +f 420/997/68 422/998/68 433/984/68 +l 435 428 +l 420 435 diff --git a/models/digtron_dual_digger_static.obj b/models/digtron_dual_digger_static.obj new file mode 100644 index 0000000..baa50ab --- /dev/null +++ b/models/digtron_dual_digger_static.obj @@ -0,0 +1,659 @@ +# Blender v2.79 (sub 0) OBJ File: 'dual digger.blend' +# www.blender.org +g drill_4.002_Cylinder.002 +v 0.500000 0.500000 0.500000 +v -0.500000 -0.500000 0.500000 +v 0.500000 -0.500000 0.500000 +v -0.500000 0.500000 0.500000 +v -0.500000 -0.500000 -0.500000 +v 0.500000 -0.500000 -0.500000 +vt 1.000000 1.000000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.000000 1.000000 +vt 1.000000 0.000000 +vn 0.0000 -0.0000 1.0000 +vn -0.0000 -1.0000 -0.0000 +s off +f 1/1/1 2/2/1 3/3/1 +f 3/4/2 5/5/2 6/6/2 +f 1/1/1 4/7/1 2/2/1 +f 3/4/2 2/8/2 5/5/2 +g drill_4.001_Cylinder.001 +v 0.250000 0.250000 -0.500000 +v -0.250000 0.250000 -0.500000 +v 0.500000 0.500000 0.500000 +v 0.500000 0.500000 0.000000 +v -0.500000 0.500000 0.000000 +v -0.500000 0.500000 0.500000 +v 0.250000 0.500000 0.000000 +v -0.250000 0.500000 0.000000 +v 0.500000 0.000000 -0.500000 +v -0.250000 0.500000 -0.250000 +v -0.500000 -0.500000 -0.500000 +v 0.500000 -0.500000 -0.500000 +v -0.500000 0.000000 -0.500000 +v 0.250000 0.500000 -0.250000 +v 0.250000 0.000000 -0.500000 +v -0.250000 0.000000 -0.500000 +v -0.500000 0.000000 0.000000 +v 0.500000 -0.000000 -0.000000 +v 0.250000 0.250000 -0.250000 +v -0.250000 0.250000 -0.250000 +vt 0.750000 0.500000 +vt 0.250000 0.750000 +vt 0.250000 0.500000 +vt 0.000000 1.000000 +vt 1.000000 0.500000 +vt 1.000000 1.000000 +vt 0.250000 0.250000 +vt 0.750000 0.000000 +vt 0.750000 0.250000 +vt 1.000000 0.000001 +vt 0.000000 0.500000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.000000 0.500000 +vt 0.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.750000 0.750000 +vt 0.250000 1.000000 +vt 0.250000 0.750000 +vt 0.750000 0.250000 +vt 0.250000 0.500000 +vt 0.250000 0.250000 +vt 0.750000 0.750000 +vt 0.000000 0.500000 +vt 0.250000 0.000000 +vt 1.000000 0.500000 +vt 1.000000 0.500000 +vt 0.750000 1.000000 +vt 0.750000 0.500000 +vn 0.0000 0.0000 -1.0000 +vn 0.0000 1.0000 0.0000 +s off +f 22/9/3 7/10/3 21/11/3 +f 9/12/4 11/13/4 12/14/4 +f 25/15/4 8/16/4 26/17/4 +f 19/18/4 24/19/4 15/20/4 +f 17/21/3 15/22/3 18/23/3 +f 24/19/3 11/24/3 10/25/3 +f 26/26/3 20/27/3 25/28/3 +f 16/29/4 13/30/4 20/31/4 +f 22/9/3 8/32/3 7/10/3 +f 9/12/4 10/33/4 11/13/4 +f 25/15/4 7/34/4 8/16/4 +f 19/18/4 23/35/4 24/19/4 +f 17/21/3 19/36/3 15/22/3 +f 24/19/3 23/35/3 11/24/3 +f 26/26/3 16/37/3 20/27/3 +f 16/29/4 14/38/4 13/30/4 +g drill_4.004_Cylinder.000 +v 0.031250 -0.562500 -0.437500 +v 0.031250 -0.562500 0.437500 +v 0.031250 -0.625000 -0.437500 +v 0.031250 -0.625000 0.437500 +v -0.031250 -0.562500 -0.437500 +v -0.031250 -0.562500 0.437500 +v -0.031250 -0.625000 -0.437500 +v -0.031250 -0.625000 0.437500 +v 0.437500 -0.562500 0.031250 +v -0.437500 -0.562500 0.031250 +v 0.437500 -0.625000 0.031250 +v -0.437500 -0.625000 0.031250 +v 0.437500 -0.562500 -0.031250 +v -0.437500 -0.562500 -0.031250 +v 0.437500 -0.625000 -0.031250 +v -0.437500 -0.625000 -0.031250 +v 0.198554 -0.812153 -0.154360 +v -0.154360 -0.812153 0.198554 +v 0.198554 -0.874653 -0.154360 +v -0.154360 -0.874653 0.198554 +v 0.154360 -0.812153 -0.198554 +v -0.198554 -0.812153 0.154360 +v 0.154360 -0.874653 -0.198554 +v -0.198554 -0.874653 0.154360 +v 0.154589 -0.812153 0.198783 +v -0.198783 -0.812153 -0.154589 +v 0.154589 -0.874653 0.198783 +v -0.198783 -0.874653 -0.154589 +v 0.198783 -0.812153 0.154589 +v -0.154589 -0.812153 -0.198783 +v 0.198783 -0.874653 0.154589 +v -0.154589 -0.874653 -0.198783 +v 0.000000 -1.000000 0.062500 +v 0.000000 -0.500000 0.500000 +v -0.044194 -1.000000 0.044194 +v -0.353553 -0.500000 0.353553 +v -0.062500 -1.000000 -0.000000 +v -0.500000 -0.500000 -0.000000 +v -0.044194 -1.000000 -0.044194 +v -0.353553 -0.500000 -0.353553 +v 0.000000 -1.000000 -0.062500 +v 0.000000 -0.500000 -0.500000 +v 0.044194 -1.000000 -0.044194 +v 0.353553 -0.500000 -0.353553 +v 0.062500 -1.000000 -0.000000 +v 0.500000 -0.500000 -0.000000 +v 0.044194 -1.000000 0.044194 +v 0.353553 -0.500000 0.353553 +v 0.031250 -0.437500 0.562500 +v 0.031250 0.437500 0.562500 +v 0.031250 -0.437500 0.625000 +v 0.031250 0.437500 0.625000 +v -0.031250 -0.437500 0.562500 +v -0.031250 0.437500 0.562500 +v -0.031250 -0.437500 0.625000 +v -0.031250 0.437500 0.625000 +v 0.437500 0.031250 0.562500 +v -0.437500 0.031250 0.562500 +v 0.437500 0.031250 0.625000 +v -0.437500 0.031250 0.625000 +v 0.437500 -0.031250 0.562500 +v -0.437500 -0.031250 0.562500 +v 0.437500 -0.031250 0.625000 +v -0.437500 -0.031250 0.625000 +v 0.198554 -0.154360 0.812153 +v -0.154360 0.198554 0.812153 +v 0.198554 -0.154360 0.874653 +v -0.154360 0.198554 0.874653 +v 0.154360 -0.198554 0.812153 +v -0.198554 0.154360 0.812153 +v 0.154360 -0.198554 0.874653 +v -0.198554 0.154360 0.874653 +v 0.154589 0.198783 0.812153 +v -0.198783 -0.154589 0.812153 +v 0.154589 0.198783 0.874653 +v -0.198783 -0.154589 0.874653 +v 0.198783 0.154589 0.812153 +v -0.154589 -0.198783 0.812153 +v 0.198783 0.154589 0.874653 +v -0.154589 -0.198783 0.874653 +v 0.000000 0.062500 1.000000 +v -0.000000 0.500000 0.500000 +v -0.044194 0.044194 1.000000 +v -0.353553 0.353553 0.500000 +v -0.062500 0.000000 1.000000 +v -0.500000 -0.000000 0.500000 +v -0.044194 -0.044194 1.000000 +v -0.353553 -0.353553 0.500000 +v 0.000000 -0.062500 1.000000 +v 0.000000 -0.500000 0.500000 +v 0.044194 -0.044194 1.000000 +v 0.353553 -0.353553 0.500000 +v 0.062500 0.000000 1.000000 +v 0.500000 -0.000000 0.500000 +v 0.044194 0.044194 1.000000 +v 0.353553 0.353553 0.500000 +vt 0.019326 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.000011 +vt 0.019238 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.019238 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.001482 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.249822 +vt 0.019238 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.019238 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.018747 0.143182 +vt 0.000904 0.000692 +vt 0.018747 0.000692 +vt 0.000904 0.000692 +vt 0.018747 0.143183 +vt 0.000904 0.143183 +vt 0.000904 0.000692 +vt 0.018747 0.143182 +vt 0.000904 0.143182 +vt 0.000904 0.000692 +vt 0.018747 0.143183 +vt 0.000904 0.143183 +vt 0.019238 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.019238 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.000904 0.000599 +vt 0.018747 0.143275 +vt 0.000904 0.143275 +vt 0.000904 0.000599 +vt 0.018747 0.143275 +vt 0.000904 0.143275 +vt 0.000904 0.000599 +vt 0.018747 0.143275 +vt 0.000904 0.143275 +vt 0.000904 0.143275 +vt 0.018747 0.000599 +vt 0.018747 0.143275 +vt 0.019238 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.019238 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.228052 0.042215 +vt 0.076587 0.056901 +vt 0.082670 0.042215 +vt 0.061901 0.208365 +vt 0.061901 0.062984 +vt 0.228052 0.042215 +vt 0.076587 0.056901 +vt 0.082670 0.042215 +vt 0.179387 0.159701 +vt 0.061901 0.062984 +vt 0.228052 0.042215 +vt 0.076587 0.056901 +vt 0.082670 0.042215 +vt 0.179387 0.159701 +vt 0.061901 0.062984 +vt 0.228052 0.042215 +vt 0.076587 0.056901 +vt 0.082670 0.042215 +vt 0.179387 0.159701 +vt 0.061901 0.062984 +vt 0.047215 0.027529 +vt 0.076587 0.027529 +vt 0.019326 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.000011 +vt 0.019238 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.019238 0.001412 +vt 0.001394 0.019256 +vt 0.001394 0.001412 +vt 0.001482 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.249822 +vt 0.001482 0.000011 +vt 0.019326 0.249822 +vt 0.001482 0.249822 +vt 0.019238 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.019238 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.018747 0.143182 +vt 0.000904 0.000692 +vt 0.018747 0.000692 +vt 0.000904 0.143183 +vt 0.018747 0.000692 +vt 0.018747 0.143183 +vt 0.000904 0.000692 +vt 0.018747 0.143182 +vt 0.000904 0.143182 +vt 0.000904 0.143183 +vt 0.018747 0.000692 +vt 0.018747 0.143183 +vt 0.019238 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.019238 0.001412 +vt 0.001394 0.019256 +vt 0.001394 0.001412 +vt 0.000904 0.000599 +vt 0.018747 0.143275 +vt 0.000904 0.143275 +vt 0.000904 0.143275 +vt 0.018747 0.000599 +vt 0.018747 0.143275 +vt 0.000904 0.143275 +vt 0.018747 0.000599 +vt 0.018747 0.143275 +vt 0.000904 0.000599 +vt 0.018747 0.143275 +vt 0.000904 0.143275 +vt 0.019238 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.019238 0.019256 +vt 0.001394 0.001412 +vt 0.019238 0.001412 +vt 0.228052 0.042215 +vt 0.076587 0.056901 +vt 0.082670 0.042215 +vt 0.179387 0.159701 +vt 0.061901 0.062984 +vt 0.082670 0.042215 +vt 0.179387 0.159701 +vt 0.076587 0.056901 +vt 0.061901 0.062984 +vt 0.228052 0.042215 +vt 0.076587 0.056901 +vt 0.082670 0.042215 +vt 0.179387 0.159701 +vt 0.061901 0.062984 +vt 0.228052 0.042215 +vt 0.076587 0.056901 +vt 0.082670 0.042215 +vt 0.179387 0.159701 +vt 0.061901 0.062984 +vt 0.047215 0.027529 +vt 0.076587 0.027529 +vt 0.001482 0.249822 +vt 0.001482 0.249822 +vt 0.001482 0.249822 +vt 0.001394 0.019256 +vt 0.001394 0.019256 +vt 0.019326 0.000011 +vt 0.019326 0.000011 +vt 0.019326 0.000011 +vt 0.001394 0.019256 +vt 0.001394 0.019256 +vt 0.000904 0.143182 +vt 0.018747 0.000692 +vt 0.018747 0.000692 +vt 0.018747 0.000692 +vt 0.001394 0.019256 +vt 0.001394 0.019256 +vt 0.018747 0.000599 +vt 0.018747 0.000599 +vt 0.018747 0.000599 +vt 0.000904 0.000599 +vt 0.001394 0.019256 +vt 0.001394 0.019256 +vt 0.179387 0.159701 +vt 0.061901 0.208366 +vt 0.061901 0.208365 +vt 0.061901 0.208365 +vt 0.047215 0.056901 +vt 0.041132 0.042215 +vt 0.061901 0.021446 +vt 0.001482 0.249822 +vt 0.001482 0.249822 +vt 0.001482 0.249822 +vt 0.001394 0.019256 +vt 0.019238 0.019256 +vt 0.019326 0.000011 +vt 0.019326 0.000011 +vt 0.019326 0.000011 +vt 0.001394 0.019256 +vt 0.001394 0.019256 +vt 0.000904 0.143182 +vt 0.000904 0.000692 +vt 0.018747 0.000692 +vt 0.000904 0.000692 +vt 0.001394 0.019256 +vt 0.019238 0.019256 +vt 0.018747 0.000599 +vt 0.000904 0.000599 +vt 0.000904 0.000599 +vt 0.018747 0.000599 +vt 0.001394 0.019256 +vt 0.001394 0.019256 +vt 0.061901 0.208365 +vt 0.228052 0.042215 +vt 0.061901 0.208366 +vt 0.061901 0.208365 +vt 0.061901 0.208365 +vt 0.047215 0.056901 +vt 0.041132 0.042215 +vt 0.061901 0.021446 +vn 1.0000 0.0000 0.0000 +vn 0.0000 -1.0000 0.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 0.0000 0.0000 1.0000 +vn 0.7071 0.0000 0.7071 +vn -0.7071 -0.0000 -0.7071 +vn 0.0000 1.0000 -0.0000 +vn 0.7071 0.0000 -0.7071 +vn -0.7071 0.0000 0.7071 +vn -0.2976 -0.6287 0.7185 +vn -0.7185 -0.6287 0.2976 +vn -0.7185 -0.6287 -0.2976 +vn -0.2976 -0.6287 -0.7185 +vn 0.2976 -0.6287 -0.7185 +vn 0.7185 -0.6287 -0.2976 +vn 0.7185 -0.6287 0.2976 +vn 0.2976 -0.6287 0.7185 +vn 0.7071 0.7071 0.0000 +vn -0.7071 -0.7071 -0.0000 +vn 0.7071 -0.7071 0.0000 +vn -0.7071 0.7071 0.0000 +vn -0.2976 0.7185 0.6287 +vn -0.7185 0.2976 0.6287 +vn -0.7185 -0.2976 0.6287 +vn -0.2976 -0.7185 0.6287 +vn 0.2976 -0.7185 0.6287 +vn 0.7185 -0.2976 0.6287 +vn 0.7185 0.2976 0.6287 +vn 0.2976 0.7185 0.6287 +s off +f 28/39/5 29/40/5 27/41/5 +f 30/42/6 33/43/6 29/44/6 +f 34/45/7 31/46/7 33/47/7 +f 33/48/8 27/49/8 29/50/8 +f 30/51/9 32/52/9 34/53/9 +f 36/54/9 37/55/9 35/56/9 +f 38/57/6 41/58/6 37/59/6 +f 42/60/8 39/61/8 41/62/8 +f 41/63/5 35/64/5 37/65/5 +f 38/66/7 40/67/7 42/68/7 +f 44/69/10 45/70/10 43/71/10 +f 46/72/6 49/73/6 45/74/6 +f 50/75/11 47/76/11 49/77/11 +f 48/78/12 43/79/12 47/80/12 +f 49/81/13 43/82/13 45/83/13 +f 46/84/14 48/85/14 50/86/14 +f 52/87/14 53/88/14 51/89/14 +f 54/90/6 57/91/6 53/92/6 +f 58/93/13 55/94/13 57/95/13 +f 55/96/12 52/97/12 51/98/12 +f 57/99/10 51/100/10 53/101/10 +f 54/102/11 56/103/11 58/104/11 +f 60/105/15 61/106/15 59/107/15 +f 61/106/16 64/108/16 63/109/16 +f 64/110/17 65/111/17 63/112/17 +f 66/113/18 67/114/18 65/111/18 +f 68/115/19 69/116/19 67/117/19 +f 70/118/20 71/119/20 69/116/20 +f 72/120/21 73/121/21 71/122/21 +f 74/123/22 59/124/22 73/121/22 +f 65/125/6 69/126/6 73/121/6 +f 76/127/5 77/128/5 75/129/5 +f 78/130/9 81/131/9 77/132/9 +f 82/133/7 79/134/7 81/135/7 +f 81/136/6 75/137/6 77/138/6 +f 82/139/12 76/140/12 80/141/12 +f 84/142/12 85/143/12 83/144/12 +f 86/145/9 89/146/9 85/147/9 +f 90/148/6 87/149/6 89/150/6 +f 89/151/5 83/152/5 85/153/5 +f 86/154/7 88/155/7 90/156/7 +f 92/157/23 93/158/23 91/159/23 +f 93/160/9 98/161/9 97/162/9 +f 98/163/24 95/164/24 97/165/24 +f 95/166/8 92/167/8 91/168/8 +f 97/169/25 91/170/25 93/171/25 +f 98/172/26 92/173/26 96/174/26 +f 100/175/26 101/176/26 99/177/26 +f 101/178/9 106/179/9 105/180/9 +f 105/181/25 104/182/25 103/183/25 +f 104/184/8 99/185/8 103/186/8 +f 105/187/23 99/188/23 101/189/23 +f 102/190/24 104/191/24 106/192/24 +f 108/193/27 109/194/27 107/195/27 +f 110/196/28 111/197/28 109/194/28 +f 111/198/29 114/199/29 113/200/29 +f 114/199/30 115/201/30 113/200/30 +f 116/202/31 117/203/31 115/204/31 +f 118/205/32 119/206/32 117/203/32 +f 120/207/33 121/208/33 119/209/33 +f 122/210/34 107/211/34 121/208/34 +f 113/212/9 117/213/9 121/208/9 +f 28/39/5 30/214/5 29/40/5 +f 30/42/6 34/215/6 33/43/6 +f 34/45/7 32/216/7 31/46/7 +f 33/48/8 31/217/8 27/49/8 +f 30/51/9 28/218/9 32/52/9 +f 36/54/9 38/219/9 37/55/9 +f 38/57/6 42/220/6 41/58/6 +f 42/60/8 40/221/8 39/61/8 +f 41/63/5 39/222/5 35/64/5 +f 38/66/7 36/223/7 40/67/7 +f 44/69/10 46/224/10 45/70/10 +f 46/72/6 50/225/6 49/73/6 +f 50/75/11 48/226/11 47/76/11 +f 48/78/12 44/227/12 43/79/12 +f 49/81/13 47/228/13 43/82/13 +f 46/84/14 44/229/14 48/85/14 +f 52/87/14 54/230/14 53/88/14 +f 54/90/6 58/231/6 57/91/6 +f 58/93/13 56/232/13 55/94/13 +f 55/96/12 56/233/12 52/97/12 +f 57/99/10 55/234/10 51/100/10 +f 54/102/11 52/235/11 56/103/11 +f 60/105/15 62/236/15 61/106/15 +f 61/106/16 62/236/16 64/108/16 +f 64/110/17 66/113/17 65/111/17 +f 66/113/18 68/237/18 67/114/18 +f 68/115/19 70/118/19 69/116/19 +f 70/118/20 72/238/20 71/119/20 +f 72/120/21 74/123/21 73/121/21 +f 74/123/22 60/239/22 59/124/22 +f 73/121/6 59/124/6 61/240/6 +f 61/240/6 63/241/6 73/121/6 +f 63/241/6 65/125/6 73/121/6 +f 65/125/6 67/242/6 69/126/6 +f 69/126/6 71/122/6 73/121/6 +f 76/127/5 78/243/5 77/128/5 +f 78/130/9 82/244/9 81/131/9 +f 82/133/7 80/245/7 79/134/7 +f 81/136/6 79/246/6 75/137/6 +f 82/139/12 78/247/12 76/140/12 +f 84/142/12 86/248/12 85/143/12 +f 86/145/9 90/249/9 89/146/9 +f 90/148/6 88/250/6 87/149/6 +f 89/151/5 87/251/5 83/152/5 +f 86/154/7 84/252/7 88/155/7 +f 92/157/23 94/253/23 93/158/23 +f 93/160/9 94/254/9 98/161/9 +f 98/163/24 96/255/24 95/164/24 +f 95/166/8 96/256/8 92/167/8 +f 97/169/25 95/257/25 91/170/25 +f 98/172/26 94/258/26 92/173/26 +f 100/175/26 102/259/26 101/176/26 +f 101/178/9 102/260/9 106/179/9 +f 105/181/25 106/261/25 104/182/25 +f 104/184/8 100/262/8 99/185/8 +f 105/187/23 103/263/23 99/188/23 +f 102/190/24 100/264/24 104/191/24 +f 108/193/27 110/196/27 109/194/27 +f 110/196/28 112/265/28 111/197/28 +f 111/198/29 112/266/29 114/199/29 +f 114/199/30 116/267/30 115/201/30 +f 116/202/31 118/205/31 117/203/31 +f 118/205/32 120/268/32 119/206/32 +f 120/207/33 122/210/33 121/208/33 +f 122/210/34 108/269/34 107/211/34 +f 121/208/9 107/211/9 109/270/9 +f 109/270/9 111/271/9 113/212/9 +f 113/212/9 115/272/9 117/213/9 +f 117/213/9 119/209/9 121/208/9 +f 121/208/9 109/270/9 113/212/9 +g drill_4_Cylinder.003 +v 0.250000 0.250000 -0.500000 +v -0.250000 0.250000 -0.500000 +v 0.500000 0.500000 0.500000 +v -0.500000 -0.500000 0.500000 +v 0.500000 0.500000 0.000000 +v 0.500000 -0.500000 0.500000 +v -0.500000 0.500000 0.000000 +v -0.500000 0.500000 0.500000 +v 0.250000 0.500000 0.000000 +v -0.250000 0.500000 0.000000 +v 0.500000 0.000000 -0.500000 +v -0.250000 0.500000 -0.250000 +v -0.500000 -0.500000 -0.500000 +v 0.500000 -0.500000 -0.500000 +v -0.500000 0.000000 -0.500000 +v 0.250000 0.500000 -0.250000 +v 0.250000 0.000000 -0.500000 +v -0.250000 0.000000 -0.500000 +v -0.500000 0.000000 0.000000 +v 0.500000 -0.000000 -0.000000 +v -0.250000 0.000000 0.000000 +v 0.250000 0.250000 -0.250000 +v -0.250000 0.250000 -0.250000 +v 0.250000 -0.000000 -0.000000 +v -0.250000 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 0.500000 +vt 0.500000 0.500000 +vt 0.500000 0.500000 +vt 1.000000 0.000000 +vt 0.000000 0.000000 +vt 0.750000 0.750000 +vt 0.500000 1.000000 +vt 0.500000 0.500000 +vt 0.750000 0.750000 +vt 1.000000 0.750000 +vt 1.000000 0.500000 +vt 0.500000 1.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vt 0.000000 1.000000 +vt 0.500000 1.000000 +vt 1.000000 0.500000 +vt 1.000000 0.500000 +vt 1.000000 0.750000 +vt 0.750000 1.000000 +vt 0.500000 0.500000 +vt 0.500000 1.000000 +vt 0.750000 1.000000 +vn 1.0000 0.0000 0.0000 +vn -1.0000 -0.0000 -0.0000 +s off +f 136/273/35 133/274/35 142/275/35 +f 141/276/36 135/277/36 126/278/36 +f 144/279/35 131/280/35 146/281/35 +f 145/282/36 124/283/36 140/284/36 +f 142/275/35 127/285/35 125/286/35 +f 125/286/35 128/287/35 142/275/35 +f 128/287/35 136/273/35 142/275/35 +f 126/278/36 130/288/36 141/276/36 +f 130/288/36 129/289/36 141/276/36 +f 141/276/36 137/290/36 135/277/36 +f 146/281/35 139/291/35 144/279/35 +f 139/291/35 123/292/35 144/279/35 +f 144/279/35 138/293/35 131/280/35 +f 140/284/36 143/294/36 145/282/36 +f 143/294/36 132/295/36 145/282/36 +f 132/295/36 134/296/36 145/282/36 +l 147 140 +l 132 147 diff --git a/nodes/node_axle.lua b/nodes/node_axle.lua deleted file mode 100644 index ac9ea2d..0000000 --- a/nodes/node_axle.lua +++ /dev/null @@ -1,69 +0,0 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - -minetest.register_node("digtron:axle", { - description = S("Digtron Rotation Axle"), - _doc_items_longdesc = digtron.doc.axle_longdesc, - _doc_items_usagehelp = digtron.doc.axle_usagehelp, - groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 1}, - drop = "digtron:axle", - sounds = digtron.metal_sounds, - paramtype = "light", - paramtype2= "facedir", - is_ground_content = false, - -- Aims in the +Z direction by default - tiles = { - "digtron_plate.png^digtron_axel_top.png", - "digtron_plate.png^digtron_axel_top.png", - "digtron_plate.png^digtron_axel_side.png", - "digtron_plate.png^digtron_axel_side.png", - "digtron_plate.png^digtron_axel_side.png", - "digtron_plate.png^digtron_axel_side.png", - }, - - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { - {-0.5, 0.3125, -0.3125, 0.5, 0.5, 0.3125}, -- Uppercap - {-0.5, -0.5, -0.3125, 0.5, -0.3125, 0.3125}, -- Lowercap - {-0.3125, 0.3125, -0.5, 0.3125, 0.5, -0.3125}, -- Uppercap_edge2 - {-0.3125, 0.3125, 0.3125, 0.3125, 0.5, 0.5}, -- Uppercap_edge1 - {-0.3125, -0.5, -0.5, 0.3125, -0.3125, -0.3125}, -- Lowercap_edge1 - {-0.3125, -0.5, 0.3125, 0.3125, -0.3125, 0.5}, -- Lowercap_edge2 - {-0.25, -0.3125, -0.25, 0.25, 0.3125, 0.25}, -- Axle - } - }, - - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - local meta = minetest.get_meta(pos) - if meta:get_string("waiting") == "true" then - -- Been too soon since last time the digtron rotated. - return - end - local image = DigtronLayout.create(pos, clicker) - if image:rotate_layout_image(node.param2) == false then - -- This should be impossible, but if self-validation fails abort. - return - end - if image:can_write_layout_image() then - if image:write_layout_image(clicker) then - minetest.sound_play("whirr", {gain=1.0, pos=pos}) - meta = minetest.get_meta(pos) - meta:set_string("waiting", "true") - meta:set_string("infotext", nil) - minetest.get_node_timer(pos):start(digtron.config.cycle_time*2) - else - meta:set_string("infotext", "unrecoverable write_layout_image error") - end - else - minetest.sound_play("buzzer", {gain=1.0, pos=pos}) - meta:set_string("infotext", S("Digtron is obstructed.")) - end - end, - - on_timer = function(pos, elapsed) - minetest.get_meta(pos):set_string("waiting", nil) - end, -}) \ No newline at end of file diff --git a/nodes/node_battery_holder.lua b/nodes/node_battery_holder.lua deleted file mode 100644 index 456f9cc..0000000 --- a/nodes/node_battery_holder.lua +++ /dev/null @@ -1,125 +0,0 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - - --- Battery storage. Controller node draws electrical power from here. --- Note that batttery boxes are digtron group 7. - -local battery_holder_formspec_string = "size[8,9.3]" .. - default.gui_bg .. - default.gui_bg_img .. - default.gui_slots .. - "label[0,0;" .. S("Batteries") .. "]" .. - "list[current_name;batteries;0,0.6;8,4;]" .. - "list[current_player;main;0,5.15;8,1;]" .. - "list[current_player;main;0,6.38;8,3;8]" .. - "listring[current_name;batteries]" .. - "listring[current_player;main]" .. - default.get_hotbar_bg(0,5.15) - -local battery_holder_formspec = function(pos, meta) - return battery_holder_formspec_string -end - -local holder_groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 7, tubedevice = 1, tubedevice_receiver = 1} -if not minetest.get_modpath("technic") then - -- if technic isn't installed there's no point in offering battery holders. - -- leave them registered, though, in case technic is being removed from an existing server. - holder_groups.not_in_creative_inventory = 1 -end - -minetest.register_node("digtron:battery_holder", { - description = S("Digtron Battery Holder"), - _doc_items_longdesc = digtron.doc.battery_holder_longdesc, - _doc_items_usagehelp = digtron.doc.battery_holder_usagehelp, - _digtron_formspec = battery_holder_formspec, - groups = holder_groups, - drop = "digtron:battery_holder", - sounds = digtron.metal_sounds, - paramtype2= "facedir", - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { - {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, - }, - }, - paramtype = "light", - is_ground_content = false, - tiles = { - "digtron_plate.png^digtron_crossbrace.png^digtron_battery.png", - "digtron_plate.png^digtron_crossbrace.png^digtron_battery.png", - "digtron_plate.png^digtron_crossbrace.png^digtron_battery.png^digtron_storage.png", - "digtron_plate.png^digtron_crossbrace.png^digtron_battery.png^digtron_storage.png", - "digtron_plate.png^digtron_crossbrace.png^digtron_battery.png^digtron_storage.png", - "digtron_plate.png^digtron_crossbrace.png^digtron_battery.png^digtron_storage.png", - }, - - on_construct = function(pos) - local meta = minetest.get_meta(pos) - meta:set_string("formspec", battery_holder_formspec(pos, meta)) - local inv = meta:get_inventory() - inv:set_size("batteries", 8*4) - end, - - -- Allow all items with energy storage to be placed in the inventory - allow_metadata_inventory_put = function(pos, listname, index, stack, player) - if listname == "batteries" then - local node_name = stack:get_name() - - -- Allow all items with energy storage from technic mod - if technic.power_tools[node_name] ~= nil then - local meta = stack:get_metadata() - local md = minetest.deserialize(meta) - -- And specifically if they hold any charge - -- Disregard empty batteries, the player should know better - if md and md.charge > 0 then - return stack:get_count() - else - return 0 - end - - else - return 0 - end - end - return 0 - end, - - - can_dig = function(pos,player) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:is_empty("batteries") - end, - - -- Pipeworks compatibility - -- Because who wouldn't send batteries through pipes if he could? - ----------------------------------------------------------------- - - tube = (function() if minetest.get_modpath("pipeworks") then return { - insert_object = function(pos, node, stack, direction) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:add_item("batteries", stack) - end, - can_insert = function(pos, node, stack, direction) - local meta = stack:get_metadata() - local md = minetest.deserialize(meta) - -- And specifically if they hold any charge - -- Disregard empty batteries, the player should know better - if md and md.charge > 0 then - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:room_for_item("batteries", stack) - end - return false - end, - input_inventory = "batteries", - connect_sides = {left = 1, right = 1, back = 1, front = 1, bottom = 1, top = 1} - } end end)(), - - after_place_node = (function() if minetest.get_modpath("pipeworks") then return pipeworks.after_place end end)(), - after_dig_node = (function() if minetest.get_modpath("pipeworks")then return pipeworks.after_dig end end)() -}) diff --git a/nodes/node_builder.lua b/nodes/node_builder.lua new file mode 100644 index 0000000..c8a4bc5 --- /dev/null +++ b/nodes/node_builder.lua @@ -0,0 +1,331 @@ +local S = digtron.S + + +-- Note: builders go in group 4 + +-- TODO make this global +local player_interacting_with_builder_pos = {} + +local get_formspec = function(meta_fields) + local period = tonumber(meta_fields.period) + if period < 1 then period = 1 end + local offset = tonumber(meta_fields.offset) + local extrusion = tonumber(meta_fields.extrusion) + local facing = tonumber(meta_fields.facing) + local item_name = meta_fields.item + + return "size[8,5.2]" .. + "item_image[0,0;1,1;" .. item_name .. "]".. + "listcolors[#00000069;#5A5A5A00;#141318;#30434C;#FFF]" .. + "list[detached:digtron:builder_item;main;0,0;1,1;]" .. + "field[1.3,0.8;1,0.1;extrusion;" .. S("Extrusion") .. ";" ..extrusion .. "]" .. + "field_close_on_enter[extrusion;false]" .. + "tooltip[extrusion;" .. S("Builder will extrude this many blocks in the direction it is facing.\nCan be set from 1 to @1.\nNote that Digtron won't build into unloaded map regions.", digtron.config.maximum_extrusion) .. "]" .. + "field[2.3,0.8;1,0.1;period;" .. S("Periodicity") .. ";".. period .. "]" .. + "field_close_on_enter[period;false]" .. + "tooltip[period;" .. S("Builder will build once every n steps.\nThese steps are globally aligned, so all builders with the\nsame period and offset will build on the same location.") .. "]" .. + "field[3.3,0.8;1,0.1;offset;" .. S("Offset") .. ";" .. offset .. "]" .. + "field_close_on_enter[offset;false]" .. + "tooltip[offset;" .. S("Offsets the start of periodicity counting by this amount.\nFor example, a builder with period 2 and offset 0 builds\nevery even-numbered block and one with period 2 and\noffset 1 builds every odd-numbered block.") .. "]" .. + "button[4.0,0.5;1,0.1;set;" .. S("Save &\nShow") .. "]" .. + "tooltip[set;" .. S("Saves settings, closes interface, and shows the locations this builder will build to in-world.") .. "]" .. + "field[5.3,0.8;1,0.1;facing;" .. S("Facing") .. ";" .. facing .. "]" .. + "field_close_on_enter[facing;false]" .. + "tooltip[facing;" .. S("Value from 0-23. Not all block types make use of this.\nUse the 'Read & Save' button to copy the facing of the block\ncurrently in the builder output location.") .. "]" .. + "button[6.0,0.5;1,0.1;read;" .. S("Read") .. "]" .. + "tooltip[read;" .. S("Reads the facing of the block currently in the build location.") .. "]" .. + "list[current_player;main;0,1.3;8,1;]" .. + default.get_hotbar_bg(0,1.3) .. + "list[current_player;main;0,2.5;8,3;8]" .. + "listring[current_player;main]" .. + "listring[detached:digtron:builder_item;main]" +end + +---------------------------------------------------------------------- +-- Detached inventory for setting the builder item + +local is_item_allowed = function(item) + -- Ignore unknown items + if minetest.registered_items[item] == nil then return false end + + local stack_def = minetest.registered_nodes[item] + if not stack_def and not digtron.whitelisted_on_place(item) then + return false -- don't allow craft items unless their on_place is whitelisted. + end + + return true +end + + +local inv = minetest.create_detached_inventory("digtron:builder_item", { + allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) + return 0 + end, + allow_put = function(inv, listname, index, stack, player) + -- Always disallow put, but use this to read what the player *tried* adding and set the builder appropriately + local item = stack:get_name() + + if not is_item_allowed(item) then + return 0 + end + + local player_name = player:get_player_name() + local pos = player_interacting_with_builder_pos[player_name] + if pos == nil then + return 0 + end + + local node = minetest.get_node(pos) + if node.name ~= "digtron:builder" then + minetest.log("warning", "[Digtron] builder detached inventory had player " .. player_name + .. " attempt to set " .. item .. " at " .. minetest.pos_to_string(pos) .. + " but the node at that location was a " .. node.name) + return 0 + end + + local meta = minetest.get_meta(pos) + local digtron_id = meta:get_string("digtron_id") + local digtron_layout_id = meta:get_int("digtron_layout_node_id") + + local layout + local layout_fields + minetest.chat_send_all(digtron_id) + minetest.chat_send_all(digtron_layout_id) + if digtron_id ~= "" and digtron_layout_id ~= 0 then + layout = digtron.get_layout(digtron_id) + layout_fields = layout[digtron_layout_id].meta.fields + end + + -- If we're adding a wallmounted item and the build facing is greater than 5, reset it to 0 + if stack_def ~= nil and stack_def.paramtype2 == "wallmounted" and tonumber(meta:get_int("facing")) > 5 then + meta:set_int("facing", 0) + if layout_fields then + layout_fields.facing = 0 + end + end + + meta:set_string("item", item) + if layout_fields then + layout_fields.item = item + end + + if layout_fields then + minetest.chat_send_all("setting layout") + digtron.set_layout(digtron_id, layout) + else + layout_fields = meta:to_table().fields + end + + digtron.update_builder_item(pos) + + minetest.show_formspec(player_name, "digtron:builder", get_formspec(layout_fields)) + return 0 + end, + allow_take = function(inv, listname, index, stack, player) + return 0 + end, +}) +inv:set_size("main", 1) + +local builder_on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + local returnstack, success = digtron.on_rightclick(pos, node, clicker, itemstack, pointed_thing) + if returnstack then + return returnstack, success + end + + if clicker == nil then return end + local player_name = clicker:get_player_name() + + local meta = minetest.get_meta(pos) + + local digtron_id = meta:get_string("digtron_id") + local digtron_node_id = meta:get_int("digtron_layout_node_id") + if digtron_id ~= "" and digtron_node_id ~= 0 then + player_interacting_with_builder_pos[player_name] = pos + local layout = digtron.get_layout(digtron_id) + local data = layout[digtron_node_id] + if data then + minetest.show_formspec(player_name, "digtron:builder", get_formspec(data.meta.fields)) + end + return + end + + player_interacting_with_builder_pos[player_name] = pos + minetest.show_formspec(player_name, "digtron:builder", get_formspec(meta:to_table().fields)) +end + +minetest.register_on_player_receive_fields(function(sender, formname, fields) + if formname ~= "digtron:builder" then + return + end + + local player_name = sender:get_player_name() + local pos = player_interacting_with_builder_pos[player_name] + if pos == nil then + minetest.log("error", "[Digtron] ".. player_name .. " tried interacting with a Digtron builder but" + .. " no position was recorded.") + return + end + + local meta = minetest.get_meta(pos) + local meta_table + + local digtron_id = meta:get_string("digtron_id") + local digtron_layout_id = meta:get_int("digtron_layout_node_id") + + local layout + if digtron_id ~= "" and digtron_layout_id ~= 0 then + -- If this builder is part of an assembled Digtron, then the persisted Digtron + -- layout will have primacy over any other metadata this node might have. + layout = digtron.get_layout(digtron_id) + meta_table = layout[digtron_layout_id].meta + else + meta_table = meta:to_table() + end + local meta_fields = meta_table.fields + + local item = meta_fields.item + + local period = tonumber(fields.period) + if period and period > 0 then + meta_fields.period = math.floor(period) + else + period = tonumber(meta_fields.period) or 1 + end + + local offset = tonumber(fields.offset) + if offset then + meta_fields.offset = math.floor(offset) + else + offset = tonumber(meta_fields.offset) or 0 + end + + local facing = tonumber(fields.facing) + if facing and facing >= 0 and facing < 24 then + local target_item = ItemStack(item) + if target_item:get_definition().paramtype2 == "wallmounted" then + -- wallmounted facings only run from 0-5 + if facing < 6 then + meta_fields.facing = math.floor(facing) + end + else + meta_fields.facing = math.floor(facing) + end + else + facing = tonumber(meta_fields.facing) or 0 + end + + local extrusion = tonumber(fields.extrusion) + if extrusion and extrusion > 0 and extrusion <= digtron.config.maximum_extrusion then + meta_fields.extrusion = math.floor(extrusion) + else + extrusion = tonumber(meta_fields.extrusion) or 1 + end + + if fields.set then + --TODO digtron.show_offset_markers(pos, offset, period) + end + + if fields.read then + local builder_facing = minetest.get_node(pos).param2 + local buildpos = vector.add(minetest.facedir_to_dir(builder_facing), pos) + local target_node = minetest.get_node(buildpos) + local target_name = target_node.name + if digtron.builder_read_item_substitutions[target_name] then + target_name = digtron.builder_read_item_substitutions[target_name] + end + if target_name ~= "air" and is_item_allowed(target_name) then + item = target_name + meta_fields.item = item + meta_fields.facing = target_node.param2 + end + end + + if fields.help then + minetest.after(0.5, doc.show_entry, sender:get_player_name(), "nodes", "digtron:builder", true) + end + + local item_def = minetest.registered_items[item] + local item_desc = S("Nothing") + if item_def then + item_desc = item_def.description + end + + meta_fields.infotext = S("Builder for @1\nperiod @2, offset @3, extrusion @4", item_desc, period, offset, extrusion) + if layout then + digtron.set_layout(digtron_id, layout) + end + meta_fields.digtron_id = digtron_id + meta_fields.digtron_layout_node_id = digtron_layout_id + meta:from_table(meta_table) + + digtron.update_builder_item(pos) + minetest.show_formspec(player_name, "digtron:builder", get_formspec(meta_fields)) + +end) + + +-- Builds objects in the targeted node. +minetest.register_node("digtron:builder", { + description = S("Digtron Builder Module"), + _doc_items_longdesc = digtron.doc.builder_longdesc, + _doc_items_usagehelp = digtron.doc.builder_usagehelp, + groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 4}, + drop = "digtron:builder", + sounds = default.node_sound_metal_defaults(), + paramtype = "light", + paramtype2= "facedir", + is_ground_content = false, + tiles = { + "digtron_plate.png^[transformR90", + "digtron_plate.png^[transformR270", + "digtron_plate.png", + "digtron_plate.png^[transformR180", + "digtron_plate.png^digtron_builder.png", + "digtron_plate.png", + }, + + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-0.25, 0.3125, 0.3125, 0.25, 0.5, 0.5}, -- FrontFrame_top + {-0.25, -0.5, 0.3125, 0.25, -0.3125, 0.5}, -- FrontFrame_bottom + {0.3125, -0.25, 0.3125, 0.5, 0.25, 0.5}, -- FrontFrame_right + {-0.5, -0.25, 0.3125, -0.3125, 0.25, 0.5}, -- FrontFrame_left + {-0.5, 0.25, -0.5, -0.25, 0.5, 0.5}, -- edge_topright + {-0.5, -0.5, -0.5, -0.25, -0.25, 0.5}, -- edge_bottomright + {0.25, 0.25, -0.5, 0.5, 0.5, 0.5}, -- edge_topleft + {0.25, -0.5, -0.5, 0.5, -0.25, 0.5}, -- edge_bottomleft + {-0.25, 0.4375, -0.5, 0.25, 0.5, -0.4375}, -- backframe_top + {-0.25, -0.5, -0.5, 0.25, -0.4375, -0.4375}, -- backframe_bottom + {-0.5, -0.25, -0.5, -0.4375, 0.25, -0.4375}, -- backframe_left + {0.4375, -0.25, -0.5, 0.5, 0.25, -0.4375}, -- Backframe_right + {-0.0625, -0.3125, 0.3125, 0.0625, 0.3125, 0.375}, -- frontcross_vertical + {-0.3125, -0.0625, 0.3125, 0.3125, 0.0625, 0.375}, -- frontcross_horizontal + } + }, + + on_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_int("period", 1) + meta:set_int("offset", 0) + meta:set_int("facing", 0) + meta:set_int("extrusion", 1) + end, + + on_rightclick = builder_on_rightclick, + + on_destruct = function(pos) + local node = minetest.get_node(pos) + local target_pos = vector.add(pos, minetest.facedir_to_dir(node.param2)) + digtron.remove_builder_item(target_pos) + end, + + after_place_node = function(pos) + digtron.update_builder_item(pos) + end, + + can_dig = digtron.can_dig, + on_blast = digtron.on_blast, +}) \ No newline at end of file diff --git a/nodes/node_builders.lua b/nodes/node_builders.lua deleted file mode 100644 index a9d783d..0000000 --- a/nodes/node_builders.lua +++ /dev/null @@ -1,357 +0,0 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - --- Note: builders go in group 4 and have both test_build and execute_build methods. - -local node_inventory_table = {type="node"} -- a reusable parameter for get_inventory calls, set the pos parameter before using. - -local displace_due_to_help_button = 1.0 -if minetest.get_modpath("doc") then - displace_due_to_help_button = 0.0 -end - -local builder_formspec_string = - "size[8,5.2]" .. - default.gui_bg .. - default.gui_bg_img .. - default.gui_slots .. - "list[current_name;main;".. tostring(displace_due_to_help_button/2) ..",0;1,1;]" .. - "label[" .. tostring(displace_due_to_help_button/2).. ",0.8;" .. S("Block to build") .. "]" .. - "field[" .. tostring(displace_due_to_help_button + 1.3) ..",0.8;1,0.1;extrusion;" .. S("Extrusion") .. ";${extrusion}]" .. - "tooltip[extrusion;" .. S("Builder will extrude this many blocks in the direction it is facing.\nCan be set from 1 to @1.\nNote that Digtron won't build into unloaded map regions.", digtron.config.maximum_extrusion) .. "]" .. - "field[" .. tostring(displace_due_to_help_button + 2.3) ..",0.8;1,0.1;period;" .. S("Periodicity") .. ";${period}]" .. - "tooltip[period;" .. S("Builder will build once every n steps.\nThese steps are globally aligned, so all builders with the\nsame period and offset will build on the same location.") .. "]" .. - "field[" .. tostring(displace_due_to_help_button + 3.3) ..",0.8;1,0.1;offset;" .. S("Offset") .. ";${offset}]" .. - "tooltip[offset;" .. S("Offsets the start of periodicity counting by this amount.\nFor example, a builder with period 2 and offset 0 builds\nevery even-numbered block and one with period 2 and\noffset 1 builds every odd-numbered block.") .. "]" .. - "button_exit[" .. tostring(displace_due_to_help_button + 4.0) ..",0.5;1,0.1;set;" .. S("Save &\nShow") .. "]" .. - "tooltip[set;" .. S("Saves settings") .. "]" .. - "field[" .. tostring(displace_due_to_help_button + 5.3) .. ",0.8;1,0.1;build_facing;" .. S("Facing") .. ";${build_facing}]" .. - "tooltip[build_facing;" .. S("Value from 0-23. Not all block types make use of this.\nUse the 'Read & Save' button to copy the facing of the block\ncurrently in the builder output location.") .. "]" .. - "button_exit[" .. tostring(displace_due_to_help_button + 6.0) ..",0.5;1,0.1;read;" .. S("Read &\nSave") .. "]" .. - "tooltip[read;" .. S("Reads the facing of the block currently in the build location,\nthen saves all settings.") .. "]" .. - "list[current_player;main;0,1.3;8,1;]" .. - default.get_hotbar_bg(0,1.3) .. - "list[current_player;main;0,2.5;8,3;8]" .. - "listring[current_player;main]" .. - "listring[current_name;main]" - -if minetest.get_modpath("doc") then - builder_formspec_string = builder_formspec_string .. - "button_exit[7.0,0.5;1,0.1;help;" .. S("Help") .. "]" .. - "tooltip[help;" .. S("Show documentation about this block") .. "]" -end - -local builder_formspec = function(pos, meta) - local nodemeta = "nodemeta:"..pos.x .. "," .. pos.y .. "," ..pos.z - return builder_formspec_string - :gsub("${extrusion}", meta:get_int("extrusion"), 1) - :gsub("${period}", meta:get_int("period"), 1) - :gsub("${offset}", meta:get_int("offset"), 1) - :gsub("${build_facing}", meta:get_int("build_facing"), 1) - :gsub("current_name", "nodemeta:"..pos.x .. "," .. pos.y .. "," ..pos.z, 2) -end - -local builder_on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - local item_def = itemstack:get_definition() - if item_def.type == "node" and minetest.get_item_group(itemstack:get_name(), "digtron") > 0 then - local returnstack, success = minetest.item_place_node(itemstack, clicker, pointed_thing) - if success and item_def.sounds and item_def.sounds.place and item_def.sounds.place.name then - minetest.sound_play(item_def.sounds.place, {pos = pos}) - end - return returnstack, success - end - local meta = minetest.get_meta(pos) - minetest.show_formspec(clicker:get_player_name(), - "digtron:builder"..minetest.pos_to_string(pos), - builder_formspec(pos, meta)) -end - -minetest.register_on_player_receive_fields(function(sender, formname, fields) - - if formname:sub(1, 15) ~= "digtron:builder" then - return - end - local pos = minetest.string_to_pos(formname:sub(16, -1)) - - local meta = minetest.get_meta(pos) - local period = tonumber(fields.period) - local offset = tonumber(fields.offset) - local build_facing = tonumber(fields.build_facing) - local extrusion = tonumber(fields.extrusion) - - if period and period > 0 then - meta:set_int("period", math.floor(tonumber(fields.period))) - else - period = meta:get_int("period") - end - if offset then - meta:set_int("offset", math.floor(tonumber(fields.offset))) - else - offset = meta:get_int("offset") - end - if build_facing and build_facing >= 0 and build_facing < 24 then - local inv = meta:get_inventory() - local target_item = inv:get_stack("main",1) - if target_item:get_definition().paramtype2 == "wallmounted" then - if build_facing < 6 then - meta:set_int("build_facing", math.floor(build_facing)) - -- wallmounted facings only run from 0-5 - end - else - meta:set_int("build_facing", math.floor(build_facing)) - end - end - if extrusion and extrusion > 0 and extrusion <= digtron.config.maximum_extrusion then - meta:set_int("extrusion", math.floor(tonumber(fields.extrusion))) - else - extrusion = meta:get_int("extrusion") - end - - if fields.set then - digtron.show_offset_markers(pos, offset, period) - - elseif fields.read then - local facing = minetest.get_node(pos).param2 - local buildpos = digtron.find_new_pos(pos, facing) - local target_node = minetest.get_node(buildpos) - if target_node.name ~= "air" and minetest.get_item_group(target_node.name, "digtron") == 0 then - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - local target_name = digtron.builder_read_item_substitutions[target_node.name] or target_node.name - inv:set_stack("main", 1, target_name) - meta:set_int("build_facing", target_node.param2) - end - end - - if fields.help and minetest.get_modpath("doc") then --check for mod in case someone disabled it after this digger was built - minetest.after(0.5, doc.show_entry, sender:get_player_name(), "nodes", "digtron:builder", true) - end - - digtron.update_builder_item(pos) -end) - - --- Builds objects in the targeted node. This is a complicated beastie. -minetest.register_node("digtron:builder", { - description = S("Digtron Builder Module"), - _doc_items_longdesc = digtron.doc.builder_longdesc, - _doc_items_usagehelp = digtron.doc.builder_usagehelp, - groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 4}, - drop = "digtron:builder", - sounds = digtron.metal_sounds, - paramtype = "light", - paramtype2= "facedir", - is_ground_content = false, - tiles = { - "digtron_plate.png^[transformR90", - "digtron_plate.png^[transformR270", - "digtron_plate.png", - "digtron_plate.png^[transformR180", - "digtron_plate.png^digtron_builder.png", - "digtron_plate.png", - }, - - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { - {-0.25, 0.3125, 0.3125, 0.25, 0.5, 0.5}, -- FrontFrame_top - {-0.25, -0.5, 0.3125, 0.25, -0.3125, 0.5}, -- FrontFrame_bottom - {0.3125, -0.25, 0.3125, 0.5, 0.25, 0.5}, -- FrontFrame_right - {-0.5, -0.25, 0.3125, -0.3125, 0.25, 0.5}, -- FrontFrame_left - {-0.5, 0.25, -0.5, -0.25, 0.5, 0.5}, -- edge_topright - {-0.5, -0.5, -0.5, -0.25, -0.25, 0.5}, -- edge_bottomright - {0.25, 0.25, -0.5, 0.5, 0.5, 0.5}, -- edge_topleft - {0.25, -0.5, -0.5, 0.5, -0.25, 0.5}, -- edge_bottomleft - {-0.25, 0.4375, -0.5, 0.25, 0.5, -0.4375}, -- backframe_top - {-0.25, -0.5, -0.5, 0.25, -0.4375, -0.4375}, -- backframe_bottom - {-0.5, -0.25, -0.5, -0.4375, 0.25, -0.4375}, -- backframe_left - {0.4375, -0.25, -0.5, 0.5, 0.25, -0.4375}, -- Backframe_right - {-0.0625, -0.3125, 0.3125, 0.0625, 0.3125, 0.375}, -- frontcross_vertical - {-0.3125, -0.0625, 0.3125, 0.3125, 0.0625, 0.375}, -- frontcross_horizontal - } - }, - - on_construct = function(pos) - local meta = minetest.get_meta(pos) - meta:set_int("period", 1) - meta:set_int("offset", 0) - meta:set_int("build_facing", 0) - meta:set_int("extrusion", 1) - - local inv = meta:get_inventory() - inv:set_size("main", 1) - end, - - on_rightclick = builder_on_rightclick, - - on_destruct = function(pos) - digtron.remove_builder_item(pos) - end, - - after_place_node = function(pos) - digtron.update_builder_item(pos) - end, - - allow_metadata_inventory_put = function(pos, listname, index, stack, player) - local stack_name = stack:get_name() - - if minetest.get_item_group(stack_name, "digtron") ~= 0 then - return 0 -- don't allow builders to be set to build Digtron nodes, they'll just clog the output. - end - - local stack_def = minetest.registered_nodes[stack_name] - if not stack_def and not digtron.whitelisted_on_place(stack_name) then - return 0 -- don't allow craft items unless their on_place is whitelisted. - end - - node_inventory_table.pos = pos - local inv = minetest.get_inventory(node_inventory_table) - inv:set_stack(listname, index, stack:take_item(1)) - - -- If we're adding a wallmounted item and the build facing is greater than 5, reset it to 0 - local meta = minetest.get_meta(pos) - if stack_def ~= nil and stack_def.paramtype2 == "wallmounted" and tonumber(meta:get_int("build_facing")) > 5 then - meta:set_int("build_facing", 0) - end - - return 0 - end, - - allow_metadata_inventory_take = function(pos, listname, index, stack, player) - node_inventory_table.pos = pos - local inv = minetest.get_inventory(node_inventory_table) - inv:set_stack(listname, index, ItemStack("")) - return 0 - end, - - -- "builder at pos, imagine that you're in test_pos. If you're willing and able to build from there, take the item you need from inventory. - -- return the item you took and the inventory location you took it from so it can be put back after all the other builders have been tested. - -- If you couldn't get the item from inventory, return an error code so we can abort the cycle. - -- If you're not supposed to build at all, or the location is obstructed, return 0 to let us know you're okay and we shouldn't abort." - - --return code and accompanying value: - -- 0, {} -- not supposed to build, no error - -- 1, {{itemstack, source inventory pos}, ...} -- can build, took items from inventory - -- 2, {{itemstack, source inventory pos}, ...}, itemstack -- was supposed to build, but couldn't get the item from inventory - -- 3, {} -- builder configuration error - test_build = function(pos, test_pos, inventory_positions, protected_nodes, nodes_dug, controlling_coordinate, controller_pos) - local meta = minetest.get_meta(pos) - local facing = minetest.get_node(pos).param2 - local buildpos = digtron.find_new_pos(test_pos, facing) - - if (buildpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") ~= 0 then - --It's not the builder's turn to build right now. - return 0, {} - end - - local extrusion_count = 0 - local extrusion_target = meta:get_int("extrusion") - if extrusion_target == nil or extrusion_target < 1 or extrusion_target > 100 then - extrusion_target = 1 -- failsafe - end - - local return_items = {} - - node_inventory_table.pos = pos - local inv = minetest.get_inventory(node_inventory_table) - local item_stack = inv:get_stack("main", 1) - - if item_stack:is_empty() then - return 3, {} -- error code for "this builder's item slot is unset" - end - - while extrusion_count < extrusion_target do - if not digtron.can_move_to(buildpos, protected_nodes, nodes_dug) then - --using "can_move_to" instead of "can_build_to" test case in case the builder is pointed "backward", and will thus - --be building into the space that it's currently in and will be vacating after moving, or in case the builder is aimed - --sideways and a fellow digtron node was ahead of it (will also be moving out of the way). - - --If the player has built his digtron stupid (eg has another digtron node in the place the builder wants to build) this - --assumption is wrong, but I can't hold the player's hand through *every* possible bad design decision. Worst case, - --the digtron will think its inventory can't handle the next build step and abort the build when it actually could have - --managed one more cycle. That's not a bad outcome for a digtron array that was built stupidly to begin with. - return 1, return_items - end - - local source_location = digtron.take_from_inventory(item_stack:get_name(), inventory_positions) - if source_location ~= nil then - table.insert(return_items, {item=item_stack, location=source_location}) - else - return 2, return_items, item_stack -- error code for "needed an item but couldn't get it from inventory" - end - extrusion_count = extrusion_count + 1 - buildpos = digtron.find_new_pos(buildpos, facing) - end - - return 1, return_items - end, - - execute_build = function(pos, player, inventory_positions, protected_nodes, nodes_dug, controlling_coordinate, controller_pos) - local meta = minetest.get_meta(pos) - local build_facing = tonumber(meta:get_int("build_facing")) - local facing = minetest.get_node(pos).param2 - local buildpos = digtron.find_new_pos(pos, facing) - - if (buildpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") ~= 0 then - return 0 - end - - local extrusion_count = 0 - local extrusion_target = meta:get_int("extrusion") - if extrusion_target == nil or extrusion_target < 1 or extrusion_target > 100 then - extrusion_target = 1 -- failsafe - end - local built_count = 0 - - node_inventory_table.pos = pos - local inv = minetest.get_inventory(node_inventory_table) - local item_stack = inv:get_stack("main", 1) - if item_stack:is_empty() then - return built_count - end - - while extrusion_count < extrusion_target do - if not digtron.can_build_to(buildpos, protected_nodes, nodes_dug) then - return built_count - end - - local oldnode = minetest.get_node(buildpos) - - if not digtron.config.uses_resources then - local returned_stack, success = digtron.item_place_node(item_stack, player, buildpos, build_facing) - if success == true then - minetest.log("action", string.format("%s uses Digtron to build %s at (%d, %d, %d), displacing %s", player:get_player_name(), item_stack:get_name(), buildpos.x, buildpos.y, buildpos.z, oldnode.name)) - nodes_dug:set(buildpos.x, buildpos.y, buildpos.z, false) - built_count = built_count + 1 - else - return built_count - end - end - - local sourcepos = digtron.take_from_inventory(item_stack:get_name(), inventory_positions) - if sourcepos == nil then - -- item not in inventory! Need to sound the angry buzzer to let the player know, so return a negative number. - return (built_count + 1) * -1 - end - local returned_stack, success = digtron.item_place_node(ItemStack(item_stack), player, buildpos, build_facing) - if success == true then - minetest.log("action", string.format("%s uses Digtron to build %s at (%d, %d, %d), displacing %s", player:get_player_name(), item_stack:get_name(), buildpos.x, buildpos.y, buildpos.z, oldnode.name)) - --flag this node as *not* to be dug. - nodes_dug:set(buildpos.x, buildpos.y, buildpos.z, false) - digtron.award_item_built(item_stack:get_name(), player) - built_count = built_count + 1 - else - --failed to build, target node probably obstructed. Put the item back in inventory. - --Should probably never reach this since we're guarding against can_build_to, above, but this makes things safe if we somehow do. - digtron.place_in_specific_inventory(item_stack, sourcepos, inventory_positions, controller_pos) - return built_count - end - - extrusion_count = extrusion_count + 1 - buildpos = digtron.find_new_pos(buildpos, facing) - end - return built_count - end, -}) \ No newline at end of file diff --git a/nodes/node_controllers.lua b/nodes/node_controllers.lua deleted file mode 100644 index a644052..0000000 --- a/nodes/node_controllers.lua +++ /dev/null @@ -1,350 +0,0 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - -local controller_nodebox ={ - {-0.3125, -0.3125, -0.3125, 0.3125, 0.3125, 0.3125}, -- Core - {-0.1875, 0.3125, -0.1875, 0.1875, 0.5, 0.1875}, -- +y_connector - {-0.1875, -0.5, -0.1875, 0.1875, -0.3125, 0.1875}, -- -y_Connector - {0.3125, -0.1875, -0.1875, 0.5, 0.1875, 0.1875}, -- +x_connector - {-0.5, -0.1875, -0.1875, -0.3125, 0.1875, 0.1875}, -- -x_connector - {-0.1875, -0.1875, 0.3125, 0.1875, 0.1875, 0.5}, -- +z_connector - {-0.5, 0.125, -0.5, -0.125, 0.5, -0.3125}, -- back_connector_3 - {0.125, 0.125, -0.5, 0.5, 0.5, -0.3125}, -- back_connector_1 - {0.125, -0.5, -0.5, 0.5, -0.125, -0.3125}, -- back_connector_2 - {-0.5, -0.5, -0.5, -0.125, -0.125, -0.3125}, -- back_connector_4 -} - -local node_inventory_table = {type="node"} -- a reusable parameter for get_inventory calls, set the pos parameter before using. - --- Master controller. Most complicated part of the whole system. Determines which direction a digtron moves and triggers all of its component parts. -minetest.register_node("digtron:controller", { - description = S("Digtron Control Module"), - _doc_items_longdesc = digtron.doc.controller_longdesc, - _doc_items_usagehelp = digtron.doc.controller_usagehelp, - groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, - drop = "digtron:controller", - sounds = digtron.metal_sounds, - paramtype = "light", - paramtype2= "facedir", - is_ground_content = false, - -- Aims in the +Z direction by default - tiles = { - "digtron_plate.png^[transformR90", - "digtron_plate.png^[transformR270", - "digtron_plate.png", - "digtron_plate.png^[transformR180", - "digtron_plate.png", - "digtron_plate.png^digtron_control.png", - }, - - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = controller_nodebox, - }, - - on_construct = function(pos) - local meta = minetest.get_meta(pos) - meta:set_float("fuel_burning", 0.0) - meta:set_string("infotext", S("Heat remaining in controller furnace: @1", 0)) - end, - - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - local meta = minetest.get_meta(pos) - if meta:get_string("waiting") == "true" then - -- Been too soon since last time the digtron did a cycle. - return - end - - local newpos, status, return_code = digtron.execute_dig_cycle(pos, clicker) - - meta = minetest.get_meta(newpos) - if status ~= nil then - meta:set_string("infotext", status) - end - - -- Start the delay before digtron can run again. - minetest.get_meta(newpos):set_string("waiting", "true") - minetest.get_node_timer(newpos):start(digtron.config.cycle_time) - end, - - on_timer = function(pos, elapsed) - minetest.get_meta(pos):set_string("waiting", nil) - end, -}) - --- Auto-controller ---------------------------------------------------------------------------------------------------------------- - -local auto_formspec = "size[8,6.2]" .. - default.gui_bg .. - default.gui_bg_img .. - default.gui_slots .. - "container[2.0,0]" .. - "field[0.0,0.8;1,0.1;cycles;" .. S("Cycles").. ";${cycles}]" .. - "tooltip[cycles;" .. S("When triggered, this controller will try to run for the given number of cycles.\nThe cycle count will decrement as it runs, so if it gets halted by a problem\nyou can fix the problem and restart.").. "]" .. - "button_exit[0.7,0.5;1,0.1;set;" .. S("Set").. "]" .. - "tooltip[set;" .. S("Saves the cycle setting without starting the controller running").. "]" .. - "button_exit[1.7,0.5;1,0.1;execute;" .. S("Set &\nExecute").. "]" .. - "tooltip[execute;" .. S("Begins executing the given number of cycles").. "]" .. - "field[0.0,2.0;1,0.1;slope;" .. S("Slope").. ";${slope}]" .. - "tooltip[slope;" .. S("For diagonal digging. After moving forward this number of nodes the auto controller\nwill add an additional cycle moving the digtron laterally in the\ndirection of the arrows on the side of this controller.\nSet to 0 for no lateral digging.").. "]" .. - "field[1.0,2.0;1,0.1;offset;" .. S("Offset").. ";${offset}]" .. - "tooltip[offset;" .. S("Sets the offset of the lateral motion defined in the Slope field.\nNote: this offset is relative to the controller's location.\nThe controller will move laterally when it reaches the indicated point.").. "]" .. - "field[2.0,2.0;1,0.1;period;" .. S("Delay").. ";${period}]" .. - "tooltip[period;" .. S("Number of seconds to wait between each cycle").. "]" .. - "list[current_name;stop;3.0,0.7;1,1;]" .. - "label[3.0,1.5;" .. S("Stop block").. "]" .. - "container_end[]" .. - "list[current_player;main;0,2.3;8,1;]" .. - default.get_hotbar_bg(0,2.3) .. - "list[current_player;main;0,3.5;8,3;8]" .. - "listring[current_player;main]" .. - "listring[current_name;stop]" - -if minetest.get_modpath("doc") then - auto_formspec = auto_formspec .. - "button_exit[7.0,0.5;1,0.1;help;" .. S("Help") .. "]" .. - "tooltip[help;" .. S("Show documentation about this block").. "]" -end - -local function auto_cycle(pos) - local node = minetest.get_node(pos) - local controlling_coordinate = digtron.get_controlling_coordinate(pos, node.param2) - local meta = minetest.get_meta(pos) - local player = minetest.get_player_by_name(meta:get_string("triggering_player")) - if player == nil or meta:get_string("waiting") == "true" then - return - end - - local cycle = meta:get_int("cycles") - local slope = meta:get_int("slope") - - if meta:get_string("lateral_done") ~= "true" and slope ~= 0 and (pos[controlling_coordinate] + meta:get_int("offset")) % slope == 0 then - --Do a downward dig cycle. Don't update the "cycles" count, these don't count towards that. - local newpos, status, return_code = digtron.execute_downward_dig_cycle(pos, player) - - if vector.equals(pos, newpos) then - status = status .. "\n" .. S("Cycles remaining: @1", cycle) .. "\n" .. S("Halted!") - meta:set_string("infotext", status) - if return_code == 1 then --return code 1 happens when there's unloaded nodes adjacent, just keep trying. - if digtron.config.emerge_unloaded_mapblocks then - minetest.emerge_area(vector.add(pos, -80), vector.add(pos, 80)) - end - minetest.after(meta:get_int("period"), auto_cycle, newpos) - else - meta:set_string("formspec", auto_formspec) - end - else - meta = minetest.get_meta(newpos) - minetest.after(meta:get_int("period"), auto_cycle, newpos) - meta:set_string("infotext", status) - meta:set_string("lateral_done", "true") - end - return - end - - local newpos, status, return_code = digtron.execute_dig_cycle(pos, player) - - if vector.equals(pos, newpos) then - status = status .. "\n" .. S("Cycles remaining: @1", cycle) .. "\n" .. S("Halted!") - meta:set_string("infotext", status) - if return_code == 1 then --return code 1 happens when there's unloaded nodes adjacent, call emerge and keep trying. - if digtron.config.emerge_unloaded_mapblocks then - minetest.emerge_area(vector.add(pos, -80), vector.add(pos, 80)) - end - minetest.after(meta:get_int("period"), auto_cycle, newpos) - else - meta:set_string("formspec", auto_formspec) - end - return - end - - meta = minetest.get_meta(newpos) - cycle = meta:get_int("cycles") - 1 - meta:set_int("cycles", cycle) - status = status .. "\n" .. S("Cycles remaining: @1", cycle) - meta:set_string("infotext", status) - meta:set_string("lateral_done", nil) - - if cycle > 0 then - minetest.after(meta:get_int("period"), auto_cycle, newpos) - else - meta:set_string("formspec", auto_formspec) - end -end - -minetest.register_node("digtron:auto_controller", { - description = S("Digtron Automatic Control Module"), - _doc_items_longdesc = digtron.doc.auto_controller_longdesc, - _doc_items_usagehelp = digtron.doc.auto_controller_usagehelp, - --Don't set a _digtron_formspec for this node_def. - --Auto-controller has special formspec handling, while active it has no formspec and right-clicking interrupts it. - groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, - drop = "digtron:auto_controller", - sounds = digtron.metal_sounds, - paramtype = "light", - paramtype2= "facedir", - is_ground_content = false, - -- Aims in the +Z direction by default - tiles = { - "digtron_plate.png^[transformR90^[colorize:" .. digtron.auto_controller_colorize, - "digtron_plate.png^[transformR270^[colorize:" .. digtron.auto_controller_colorize, - "digtron_plate.png^digtron_axel_side.png^[transformR270^[colorize:" .. digtron.auto_controller_colorize, - "digtron_plate.png^digtron_axel_side.png^[transformR270^[colorize:" .. digtron.auto_controller_colorize, - "digtron_plate.png^[colorize:" .. digtron.auto_controller_colorize, - "digtron_plate.png^digtron_control.png^[colorize:" .. digtron.auto_controller_colorize, - }, - - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = controller_nodebox, - }, - - on_construct = function(pos) - local meta = minetest.get_meta(pos) - meta:set_float("fuel_burning", 0.0) - meta:set_string("infotext", S("Heat remaining in controller furnace: @1", 0)) - meta:set_string("formspec", auto_formspec) - -- Reusing offset and period to keep the digtron node-moving code simple, and the names still fit well - meta:set_int("period", digtron.config.cycle_time) - meta:set_int("offset", 0) - meta:set_int("cycles", 0) - meta:set_int("slope", 0) - - local inv = meta:get_inventory() - inv:set_size("stop", 1) - end, - - allow_metadata_inventory_put = function(pos, listname, index, stack, player) - if minetest.get_item_group(stack:get_name(), "digtron") ~= 0 then - return 0 -- pointless setting a Digtron node as a stop block - end - node_inventory_table.pos = pos - local inv = minetest.get_inventory(node_inventory_table) - inv:set_stack(listname, index, stack:take_item(1)) - return 0 - end, - - allow_metadata_inventory_take = function(pos, listname, index, stack, player) - node_inventory_table.pos = pos - local inv = minetest.get_inventory(node_inventory_table) - inv:set_stack(listname, index, ItemStack("")) - return 0 - end, - - on_receive_fields = function(pos, formname, fields, sender) - local meta = minetest.get_meta(pos) - local offset = tonumber(fields.offset) - local period = tonumber(fields.period) - local slope = tonumber(fields.slope) - local cycles = tonumber(fields.cycles) - - if period and period > 0 then - meta:set_int("period", math.max(digtron.config.cycle_time, math.floor(period))) - end - - if offset then - meta:set_int("offset", offset) - end - - if slope and slope >= 0 then - meta:set_int("slope", slope) - end - - if cycles and cycles >= 0 then - meta:set_int("cycles", math.floor(cycles)) - if sender:is_player() and cycles > 0 then - meta:set_string("triggering_player", sender:get_player_name()) - if fields.execute then - meta:set_string("waiting", nil) - meta:set_string("formspec", nil) - auto_cycle(pos) - end - end - end - - if fields.set and slope and slope > 0 then - local node = minetest.get_node(pos) - local controlling_coordinate = digtron.get_controlling_coordinate(pos, node.param2) - - local newpos = pos - local markerpos = {x=newpos.x, y=newpos.y, z=newpos.z} - local x_pos = math.floor((newpos[controlling_coordinate]+offset)/slope)*slope - offset - markerpos[controlling_coordinate] = x_pos - minetest.add_entity(markerpos, "digtron:marker_vertical") - if x_pos >= newpos[controlling_coordinate] then - markerpos[controlling_coordinate] = x_pos - slope - minetest.add_entity(markerpos, "digtron:marker_vertical") - end - if x_pos <= newpos[controlling_coordinate] then - markerpos[controlling_coordinate] = x_pos + slope - minetest.add_entity(markerpos, "digtron:marker_vertical") - end - end - - if fields.help and minetest.get_modpath("doc") then --check for mod in case someone disabled it after this digger was built - minetest.after(0.5, doc.show_entry, sender:get_player_name(), "nodes", "digtron:auto_controller", true) - end - end, - - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - local meta = minetest.get_meta(pos) - meta:set_string("infotext", meta:get_string("infotext") .. "\n" .. S("Interrupted!")) - meta:set_string("waiting", "true") - meta:set_string("formspec", auto_formspec) - end, -}) - ---------------------------------------------------------------------------------------------------------------- - --- A much simplified control unit that only moves the digtron, and doesn't trigger the diggers or builders. --- Handy for shoving a digtron to the side if it's been built a bit off. -minetest.register_node("digtron:pusher", { - description = S("Digtron Pusher Module"), - _doc_items_longdesc = digtron.doc.pusher_longdesc, - _doc_items_usagehelp = digtron.doc.pusher_usagehelp, - groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 1}, - drop = "digtron:pusher", - sounds = digtron.metal_sounds, - paramtype = "light", - paramtype2= "facedir", - is_ground_content = false, - -- Aims in the +Z direction by default - tiles = { - "digtron_plate.png^[transformR90^[colorize:" .. digtron.pusher_controller_colorize, - "digtron_plate.png^[transformR270^[colorize:" .. digtron.pusher_controller_colorize, - "digtron_plate.png^[colorize:" .. digtron.pusher_controller_colorize, - "digtron_plate.png^[transformR180^[colorize:" .. digtron.pusher_controller_colorize, - "digtron_plate.png^[colorize:" .. digtron.pusher_controller_colorize, - "digtron_plate.png^digtron_control.png^[colorize:" .. digtron.pusher_controller_colorize, - }, - - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = controller_nodebox, - }, - - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - local meta = minetest.get_meta(pos) - if meta:get_string("waiting") == "true" then - -- Been too soon since last time the digtron did a cycle. - return - end - - local newpos, status_text, return_code = digtron.execute_move_cycle(pos, clicker) - meta = minetest.get_meta(newpos) - meta:set_string("infotext", status_text) - - -- Start the delay before digtron can run again. - minetest.get_meta(newpos):set_string("waiting", "true") - minetest.get_node_timer(newpos):start(digtron.config.cycle_time) - end, - - on_timer = function(pos, elapsed) - minetest.get_meta(pos):set_string("waiting", nil) - end, -}) diff --git a/nodes/node_crate.lua b/nodes/node_crate.lua deleted file mode 100644 index 196b21f..0000000 --- a/nodes/node_crate.lua +++ /dev/null @@ -1,385 +0,0 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - -local modpath_awards = minetest.get_modpath("awards") - - -local player_permitted = function(pos, player) - if player then - if minetest.check_player_privs(player, "protection_bypass") then - return true - end - else - return false - end - - local meta = minetest.get_meta(pos) - local owner = meta:get_string("owner") - - if not owner or owner == "" or owner == player:get_player_name() then - return true - end -end - -local store_digtron = function(pos, clicker, loaded_node_name, protected) - local layout = DigtronLayout.create(pos, clicker) - local protection_prefix = "" - local protection_suffix = "" - if protected then - protection_prefix = S("Digtron Crate") .. "\n" .. S("Owned by @1", clicker:get_player_name() or "") - protection_suffix = S("Owned by @1", clicker:get_player_name() or "") - end - - if layout.contains_protected_node then - local meta = minetest.get_meta(pos) - minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - meta:set_string("infotext", protection_prefix .. "\n" .. S("Digtron can't be packaged, it contains protected blocks")) - -- no stealing other peoples' digtrons - return - end - - if #layout.all == 1 then - local meta = minetest.get_meta(pos) - minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - meta:set_string("infotext", protection_prefix .. "\n" .. S("No Digtron components adjacent to package")) - return - end - - digtron.award_crate(layout, clicker:get_player_name()) - - local layout_string = layout:serialize() - - -- destroy everything. Note that this includes the empty crate, which will be bundled up with the layout. - for _, node_image in pairs(layout.all) do - local old_pos = node_image.pos - local old_node = node_image.node - minetest.remove_node(old_pos) - - if modpath_awards then - -- We're about to tell the awards mod that we're digging a node, but we - -- don't want it to count toward any actual awards. Pre-decrement. - local data = awards.player(clicker:get_player_name()) - awards.increment_item_counter(data, "dig", old_node.name, -1) - end - - for _, callback in ipairs(minetest.registered_on_dignodes) do - -- Copy pos and node because callback can modify them - local pos_copy = {x=old_pos.x, y=old_pos.y, z=old_pos.z} - local oldnode_copy = {name=old_node.name, param1=old_node.param1, param2=old_node.param2} - callback(pos_copy, oldnode_copy, clicker) - end - end - - -- Create the loaded crate node - minetest.set_node(pos, {name=loaded_node_name}) - minetest.sound_play("machine1", {gain=1.0, pos=pos}) - - local meta = minetest.get_meta(pos) - meta:set_string("crated_layout", layout_string) - - if protected then - -- only set owner if protected - meta:set_string("owner", clicker:get_player_name() or "") - end - - local titlestring = S("Crated @1-block Digtron", tostring(#layout.all-1)) - meta:set_string("title", titlestring ) - meta:set_string("infotext", titlestring .. "\n" .. protection_suffix) -end - -minetest.register_node("digtron:empty_crate", { - description = S("Digtron Crate (Empty)"), - _doc_items_longdesc = digtron.doc.empty_crate_longdesc, - _doc_items_usagehelp = digtron.doc.empty_crate_usagehelp, - groups = {cracky = 3, oddly_breakable_by_hand=3}, - sounds = default.node_sound_wood_defaults(), - tiles = {"digtron_crate.png"}, - is_ground_content = false, - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { - {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, - }, - }, - paramtype = "light", - - can_dig = function(pos, player) - return player and not minetest.is_protected(pos, player:get_player_name()) - end, - - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - store_digtron(pos, clicker, "digtron:loaded_crate") - end -}) - -minetest.register_node("digtron:empty_locked_crate", { - description = S("Digtron Locked Crate (Empty)"), - _doc_items_longdesc = digtron.doc.empty_locked_crate_longdesc, - _doc_items_usagehelp = digtron.doc.empty_locked_crate_usagehelp, - groups = {cracky = 3, oddly_breakable_by_hand=3}, - sounds = default.node_sound_wood_defaults(), - tiles = {"digtron_crate.png","digtron_crate.png","digtron_crate.png^digtron_lock.png","digtron_crate.png^digtron_lock.png","digtron_crate.png^digtron_lock.png","digtron_crate.png^digtron_lock.png"}, - is_ground_content = false, - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { - {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, - }, - }, - paramtype = "light", - on_construct = function(pos) - local meta = minetest.get_meta(pos) - meta:set_string("owner", "") - meta:set_string("infotext", "") - end, - after_place_node = function(pos, placer) - local meta = minetest.get_meta(pos) - meta:set_string("owner", placer:get_player_name() or "") - meta:set_string("infotext", S("Digtron Crate") .. "\n" .. S("Owned by @1", placer:get_player_name() or "")) - end, - can_dig = function(pos,player) - return player and not minetest.is_protected(pos, player:get_player_name()) and player_permitted(pos, player) - end, - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - if player_permitted(pos,clicker) then - store_digtron(pos, clicker, "digtron:loaded_locked_crate", true) - end - end, -}) - -local modpath_doc = minetest.get_modpath("doc") -local loaded_formspec_string -if modpath_doc then - loaded_formspec_string = - "size[4.1,1.5]" .. - default.gui_bg .. - default.gui_bg_img .. - default.gui_slots .. - "field[0.3,0.5;4,0.5;title;" .. S("Digtron Name") .. ";${title}]" .. - "button_exit[0.0,1.2;1,0.1;save;" .. S("Save\nTitle") .. "]" .. - "tooltip[save;" .. S("Saves the title of this Digtron") .. "]" .. - "button_exit[1.0,1.2;1,0.1;show;" .. S("Show\nBlocks") .. "]" .. - "tooltip[show;" .. S("Shows which blocks the packed Digtron will occupy if unpacked") .. "]" .. - "button_exit[2.0,1.2;1,0.1;unpack;" .. S("Unpack") .. "]" .. - "tooltip[unpack;" .. S("Attempts to unpack the Digtron on this location") .. "]" .. - "button_exit[3.0,1.2;1,0.1;help;" .. S("Help") .. "]" .. - "tooltip[help;" .. S("Show documentation about this block") .. "]" -else - loaded_formspec_string = - "size[4,1.5]" .. - default.gui_bg .. - default.gui_bg_img .. - default.gui_slots .. - "field[0.3,0.5;4,0.5;title;" .. S("Digtron Name") .. ";${title}]" .. - "button_exit[0.5,1.2;1,0.1;save;" .. S("Save\nTitle") .. "]" .. - "tooltip[show;" .. S("Saves the title of this Digtron") .. "]" .. - "button_exit[1.5,1.2;1,0.1;show;" .. S("Show\nBlocks") .. "]" .. - "tooltip[save;" .. S("Shows which blocks the packed Digtron will occupy if unpacked") .. "]" .. - "button_exit[2.5,1.2;1,0.1;unpack;" .. S("Unpack") .. "]" .. - "tooltip[unpack;" .. S("Attempts to unpack the Digtron on this location") .. "]" -end - -local loaded_formspec = function(pos, meta) - return loaded_formspec_string -end - -local loaded_on_recieve = function(pos, fields, sender, protected) - local meta = minetest.get_meta(pos) - - if fields.unpack or fields.save or fields.show or fields.key_enter then - meta:set_string("title", minetest.formspec_escape(fields.title)) - end - local title = meta:get_string("title") - local infotext - - if protected then - infotext = title .. "\n" .. S("Owned by @1", sender:get_player_name()) - else - infotext = title - end - meta:set_string("infotext", infotext) - - if fields.help and minetest.get_modpath("doc") then --check for mod in case someone disabled it after this digger was built - minetest.after(0.5, doc.show_entry, sender:get_player_name(), "nodes", "digtron:loaded_crate", true) - end - - if not (fields.unpack or fields.show) then - return - end - - local layout_string = meta:get_string("crated_layout") - local layout = DigtronLayout.deserialize(layout_string) - - if layout == nil then - meta:set_string("infotext", infotext .. "\n" .. S("Unable to read layout from crate metadata, regrettably this Digtron may be corrupted.")) - minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - -- Something went horribly wrong - return - end - - local protected_node = false - local obstructed_node = false - - local pos_diff = vector.subtract(pos, layout.controller) - layout.controller = pos - for _, node_image in pairs(layout.all) do - node_image.pos = vector.add(pos_diff, node_image.pos) - if not vector.equals(pos, node_image.pos) then - if minetest.is_protected(node_image.pos, sender:get_player_name()) and not minetest.check_player_privs(sender, "protection_bypass") then - protected_node = true - minetest.add_entity(node_image.pos, "digtron:marker_crate_bad") - elseif not minetest.registered_nodes[minetest.get_node(node_image.pos).name].buildable_to then - obstructed_node = true - minetest.add_entity(node_image.pos, "digtron:marker_crate_bad") - else - minetest.add_entity(node_image.pos, "digtron:marker_crate_good") - end - end - end - - if not fields.unpack then - return - end - - if protected_node then - meta:set_string("infotext", infotext .. "\n" .. S("Unable to deploy Digtron due to protected blocks in target area")) - minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - return - end - - if obstructed_node then - meta:set_string("infotext", infotext .. "\n" .. S("Unable to deploy Digtron due to obstruction in target area")) - minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - return - end - - -- build digtron. Since the empty crate was included in the layout, that will overwrite this loaded crate and destroy it. - minetest.sound_play("machine2", {gain=1.0, pos=pos}) - layout:write_layout_image(sender) -end - -local loaded_on_dig = function(pos, player, loaded_node_name) - local meta = minetest.get_meta(pos) - - local stack = ItemStack({name=loaded_node_name, count=1, wear=0}) - local stack_meta = stack:get_meta() - stack_meta:set_string("crated_layout", meta:get_string("crated_layout")) - stack_meta:set_string("description", meta:get_string("title")) - local inv = player:get_inventory() - local stack = inv:add_item("main", stack) - if stack:get_count() > 0 then - minetest.add_item(pos, stack) - end - -- call on_dignodes callback - minetest.remove_node(pos) -end - -local loaded_after_place = function(pos, itemstack) - - -- Older versions of Digtron used this deprecated method for saving layout data on items. - -- Maintain backward compatibility here. - local deprecated_metadata = itemstack:get_metadata() - if deprecated_metadata ~= "" then - deprecated_metadata = minetest.deserialize(deprecated_metadata) - local meta = minetest.get_meta(pos) - meta:set_string("crated_layout", deprecated_metadata.layout) - meta:set_string("title", deprecated_metadata.title) - meta:set_string("infotext", deprecated_metadata.title) - return - end - - local stack_meta = itemstack:get_meta() - local layout = stack_meta:get_string("crated_layout") - local title = stack_meta:get_string("description") - if layout ~= "" then - local meta = minetest.get_meta(pos) - - meta:set_string("crated_layout", layout) - meta:set_string("title", title) - meta:set_string("infotext", title) - --meta:set_string("formspec", loaded_formspec(pos, meta)) -- not needed, on_construct handles this - end -end - -minetest.register_node("digtron:loaded_crate", { - description = S("Digtron Crate (Loaded)"), - _doc_items_longdesc = digtron.doc.loaded_crate_longdesc, - _doc_items_usagehelp = digtron.doc.loaded_crate_usagehelp, - _digtron_formspec = loaded_formspec, - groups = {cracky = 3, oddly_breakable_by_hand=3, not_in_creative_inventory=1, digtron_protected=1}, - stack_max = 1, - sounds = default.node_sound_wood_defaults(), - tiles = {"digtron_plate.png^digtron_crate.png"}, - is_ground_content = false, - - on_construct = function(pos) - local meta = minetest.get_meta(pos) - meta:set_string("formspec", loaded_formspec(pos, meta)) - end, - - on_receive_fields = function(pos, formname, fields, sender) - return loaded_on_recieve(pos, fields, sender) - end, - - on_dig = function(pos, node, player) - if player and not minetest.is_protected(pos, player:get_player_name()) then - return loaded_on_dig(pos, player, "digtron:loaded_crate") - end - end, - - after_place_node = function(pos, placer, itemstack, pointed_thing) - loaded_after_place(pos, itemstack) - end, -}) - -minetest.register_node("digtron:loaded_locked_crate", { - description = S("Digtron Locked Crate (Loaded)"), - _doc_items_longdesc = digtron.doc.loaded_locked_crate_longdesc, - _doc_items_usagehelp = digtron.doc.loaded_locked_crate_usagehelp, - groups = {cracky = 3, oddly_breakable_by_hand=3, not_in_creative_inventory=1, digtron_protected=1}, - stack_max = 1, - sounds = default.node_sound_wood_defaults(), - tiles = {"digtron_plate.png^digtron_crate.png","digtron_plate.png^digtron_crate.png","digtron_plate.png^digtron_crate.png^digtron_lock.png","digtron_plate.png^digtron_crate.png^digtron_lock.png","digtron_plate.png^digtron_crate.png^digtron_lock.png","digtron_plate.png^digtron_crate.png^digtron_lock.png"}, - is_ground_content = false, - - on_construct = function(pos) - local meta = minetest.get_meta(pos) - meta:set_string("owner", "") - end, - - on_dig = function(pos, node, player) - if player and not minetest.is_protected(pos, player:get_player_name()) and player_permitted(pos,player) then - return loaded_on_dig(pos, player, "digtron:loaded_locked_crate") - else - return false - end - end, - - after_place_node = function(pos, placer, itemstack, pointed_thing) - local meta = minetest.get_meta(pos) - meta:set_string("owner", placer:get_player_name() or "") - loaded_after_place(pos, itemstack) - meta:set_string("infotext", meta:get_string("infotext") .. "\n" .. S("Owned by @1", meta:get_string("owner"))) - end, - - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - if player_permitted(pos,clicker) then - local meta = minetest.get_meta(pos) - minetest.show_formspec( - clicker:get_player_name(), - "digtron:loaded_locked_crate"..minetest.pos_to_string(pos), - loaded_formspec_string:gsub("${title}", meta:get_string("title"), 1)) - end - end, -}) - -minetest.register_on_player_receive_fields(function(player, formname, fields) - if formname:sub(1, 27) == "digtron:loaded_locked_crate" then - local pos = minetest.string_to_pos(formname:sub(28, -1)) - loaded_on_recieve(pos, fields, player, true) - return true - end -end) diff --git a/nodes/node_digger.lua b/nodes/node_digger.lua new file mode 100644 index 0000000..001ae53 --- /dev/null +++ b/nodes/node_digger.lua @@ -0,0 +1,337 @@ +local S = digtron.S + +-- TODO: make global, is used by builders too +local player_interacting_with_digtron_pos = {} + +local get_formspec = function(pos, player_name) + local meta = minetest.get_meta(pos) + + local period = meta:get_int("period") + if period < 1 then period = 1 end + local offset = meta:get_int("offset") + + return + "size[5,3]" .. + default.gui_bg .. + default.gui_bg_img .. + default.gui_slots .. + "field[0.5,0.8;1,0.1;period;" .. S("Periodicity") .. ";" .. period .. "]" .. + "field_close_on_enter[period;false]" .. + "tooltip[period;" .. S("Digger will dig once every n steps.\nThese steps are globally aligned, all diggers with\nthe same period and offset will dig on the same location.") .. "]" .. + "field[1.5,0.8;1,0.1;offset;" .. S("Offset") .. ";" .. offset .. "]" .. + "field_close_on_enter[offset;false]" .. + "tooltip[offset;" .. S("Offsets the start of periodicity counting by this amount.\nFor example, a digger with period 2 and offset 0 digs\nevery even-numbered block and one with period 2 and\noffset 1 digs every odd-numbered block.") .. "]" .. + "button[2.2,0.5;1,0.1;set;" .. S("Save &\nShow") .. "]" .. + "tooltip[set;" .. S("Saves settings") .. "]" +end + +local update_infotext = function(meta) + local period = meta:get_int("period") + if period < 1 then period = 1 end + local offset = meta:get_int("offset") + + meta:set_string("infotext", S("Digger\nperiod @1, offset @2", period, offset)) +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname ~= "digtron:digger" then + return + end + local player_name = player:get_player_name() + local pos = player_interacting_with_digtron_pos[player_name] + if pos == nil then return end + local meta = minetest.get_meta(pos) + + --TODO: this isn't recording the field when using ESC to exit the formspec + if fields.key_enter_field == "offset" or fields.offset then + local val = tonumber(fields.offset) + if val ~= nil and val >= 0 then + meta:set_int("offset", val) + end + end + if fields.key_enter_field == "period" or fields.period then + local val = tonumber(fields.period) + if val ~= nil and val >= 1 then + meta:set_int("period", val) + end + end + + update_infotext(meta) +end) + + +local single_drill_nodebox = { + type = "fixed", + fixed = { + {-0.25, -0.25, 0.5, 0.25, 0.25, 0.8125}, -- Drill + {-0.5, -0.5, 0, 0.5, 0.5, 0.5}, -- Block + {-0.25, -0.25, -0.5, 0.25, 0.25, 0}, -- Drive + }, +} + +local dual_drill_nodebox = { + type = "fixed", + fixed = { + {-0.25, -0.25, 0.5, 0.25, 0.25, 0.8125}, -- Drill + {-0.25, -0.5, -0.25, 0.25, -0.8125, 0.25}, -- Drill Down + {-0.5, -0.5, 0, 0.5, 0.5, 0.5}, -- Block + {-0.5, -0.5, -0.5, 0.5, 0, 0}, -- Block Down + {-0.25, 0, -0.5, 0.25, 0.25, 0}, -- Drive + {-0.25, 0.25, -0.25, 0.25, 0.5, 0}, -- Drive Up + }, +} + +local digger_on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + local returnstack, success = digtron.on_rightclick(pos, node, clicker, itemstack, pointed_thing) + if returnstack then + return returnstack, success + end + if clicker == nil then return end + local player_name = clicker:get_player_name() + player_interacting_with_digtron_pos[player_name] = pos + minetest.show_formspec(player_name, "digtron:digger", get_formspec(pos, player_name)) +end + +local assembled_digger_on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + if clicker == nil then return end + local player_name = clicker:get_player_name() + minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=player_name}) + minetest.chat_send_player(clicker:get_player_name(), S("This Digtron is active, interact with it via the controller node.")) +end + +------------------------------------------------------------------------------------------------------ +-- Regular digger + +minetest.register_node("digtron:digger", { + description = S("Digtron Digger"), + _doc_items_longdesc = nil, + _doc_items_usagehelp = nil, + _digtron_disassembled_node = "digtron:digger_static", + drops = "digtron:digger_static", + drawtype = "mesh", + mesh = "digtron_digger.obj", + tiles = { + { name = "digtron_plate.png^digtron_digger_yb_frame.png", backface_culling = true }, + { name = "digtron_plate.png", backface_culling = true }, + { name = "digtron_drill_head_animated.png", backface_culling = true, animation = + { + type = "vertical_frames", + aspect_w = 48, + aspect_h = 12, + length = 1.0, + } + }, + { name = "digtron_plate.png^digtron_motor.png", backface_culling = true }, + }, + collision_box = single_drill_nodebox, + selection_box = single_drill_nodebox, + paramtype2 = "facedir", + paramtype = "light", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 10, not_in_creative_inventory = 1}, + is_ground_content = false, + sounds = default.node_sound_metal_defaults(), + can_dig = digtron.can_dig, + on_blast = digtron.on_blast, + on_rightclick = assembled_digger_on_rightclick, +}) + +minetest.register_node("digtron:digger_static",{ + description = S("Digtron Digger"), + _doc_items_longdesc = nil, + _doc_items_usagehelp = nil, + _digtron_assembled_node = "digtron:digger", + drawtype = "mesh", + mesh = "digtron_digger_static.obj", + tiles = { + { name = "digtron_plate.png^digtron_digger_yb_frame.png", backface_culling = true }, + { name = "digtron_plate.png", backface_culling = true }, + { name = "digtron_drill_head_animated.png", backface_culling = true }, + { name = "digtron_plate.png^digtron_motor.png", backface_culling = true }, + }, + collision_box = single_drill_nodebox, + selection_box = single_drill_nodebox, + paramtype2 = "facedir", + paramtype = "light", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, + is_ground_content = false, + sounds = default.node_sound_metal_defaults(), + on_rightclick = digger_on_rightclick, +}) + +---------------------------------------------------------------------------------------------- +-- Dual regular digger + +minetest.register_node("digtron:dual_digger", { + description = S("Digtron Dual Digger"), + _doc_items_longdesc = nil, + _doc_items_usagehelp = nil, + _digtron_disassembled_node = "digtron:dual_digger_static", + drops = "digtron:dual_digger_static", + drawtype = "mesh", + mesh = "digtron_dual_digger.obj", + tiles = { + { name = "digtron_plate.png^digtron_digger_yb_frame.png", backface_culling = true }, + { name = "digtron_plate.png^digtron_motor.png", backface_culling = true }, + { name = "digtron_drill_head_animated.png", backface_culling = true, animation = + { + type = "vertical_frames", + aspect_w = 48, + aspect_h = 12, + length = 1.0, + } + }, + { name = "digtron_plate.png", backface_culling = true }, + }, + collision_box = dual_drill_nodebox, + selection_box = dual_drill_nodebox, + paramtype2 = "facedir", + paramtype = "light", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 11, not_in_creative_inventory = 1}, + is_ground_content = false, + sounds = default.node_sound_metal_defaults(), + can_dig = digtron.can_dig, + on_blast = digtron.on_blast, + on_rightclick = assembled_digger_on_rightclick, +}) + +minetest.register_node("digtron:dual_digger_static",{ + description = S("Digtron Dual Digger"), + _doc_items_longdesc = nil, + _doc_items_usagehelp = nil, + _digtron_assembled_node = "digtron:dual_digger", + drawtype = "mesh", + mesh = "digtron_dual_digger_static.obj", + tiles = { + { name = "digtron_plate.png^digtron_digger_yb_frame.png", backface_culling = true }, + { name = "digtron_plate.png^digtron_motor.png", backface_culling = true }, + { name = "digtron_drill_head_animated.png", backface_culling = true }, + { name = "digtron_plate.png", backface_culling = true }, + }, + collision_box = dual_drill_nodebox, + selection_box = dual_drill_nodebox, + paramtype2 = "facedir", + paramtype = "light", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, + is_ground_content = false, + sounds = default.node_sound_metal_defaults(), + on_rightclick = digger_on_rightclick, +}) + +-------------------------------------------------------------------------------------------- +-- Soft digger + +minetest.register_node("digtron:soft_digger", { + description = S("Digtron Soft Digger"), + _doc_items_longdesc = nil, + _doc_items_usagehelp = nil, + _digtron_disassembled_node = "digtron:soft_digger_static", + drops = "digtron:soft_digger_static", + drawtype = "mesh", + mesh = "digtron_digger.obj", + tiles = { + { name = "digtron_plate.png^digtron_digger_yb_frame.png", backface_culling = true }, + { name = "digtron_plate.png", backface_culling = true }, + { name = "digtron_drill_head_animated.png^[multiply:#FFCC88", backface_culling = true, animation = + { + type = "vertical_frames", + aspect_w = 48, + aspect_h = 12, + length = 1.0, + } + }, + { name = "digtron_plate.png^digtron_motor.png", backface_culling = true }, + }, + collision_box = single_drill_nodebox, + selection_box = single_drill_nodebox, + paramtype2 = "facedir", + paramtype = "light", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 12, not_in_creative_inventory = 1}, + is_ground_content = false, + sounds = default.node_sound_metal_defaults(), + can_dig = digtron.can_dig, + on_blast = digtron.on_blast, + on_rightclick = assembled_digger_on_rightclick, +}) + +minetest.register_node("digtron:soft_digger_static",{ + description = S("Digtron Soft Digger"), + _doc_items_longdesc = nil, + _doc_items_usagehelp = nil, + _digtron_assembled_node = "digtron:soft_digger", + drawtype = "mesh", + mesh = "digtron_digger_static.obj", + tiles = { + { name = "digtron_plate.png^digtron_digger_yb_frame.png", backface_culling = true }, + { name = "digtron_plate.png", backface_culling = true }, + { name = "digtron_drill_head_animated.png^[multiply:#FFCC88", backface_culling = true }, + { name = "digtron_plate.png^digtron_motor.png", backface_culling = true }, + }, + collision_box = single_drill_nodebox, + selection_box = single_drill_nodebox, + paramtype2 = "facedir", + paramtype = "light", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, + is_ground_content = false, + sounds = default.node_sound_metal_defaults(), + on_rightclick = digger_on_rightclick, +}) + +---------------------------------------------------------------------------------------------- +-- Dual soft digger + +minetest.register_node("digtron:dual_soft_digger", { + description = S("Digtron Dual Soft Digger"), + _doc_items_longdesc = nil, + _doc_items_usagehelp = nil, + _digtron_disassembled_node = "digtron:dual_soft_digger_static", + drops = "digtron:dual_soft_digger_static", + drawtype = "mesh", + mesh = "digtron_dual_digger.obj", + tiles = { + { name = "digtron_plate.png^digtron_digger_yb_frame.png", backface_culling = true }, + { name = "digtron_plate.png^digtron_motor.png", backface_culling = true }, + { name = "digtron_drill_head_animated.png^[multiply:#FFCC88", backface_culling = true, animation = + { + type = "vertical_frames", + aspect_w = 48, + aspect_h = 12, + length = 1.0, + } + }, + { name = "digtron_plate.png", backface_culling = true }, + }, + collision_box = dual_drill_nodebox, + selection_box = dual_drill_nodebox, + paramtype2 = "facedir", + paramtype = "light", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 13, not_in_creative_inventory = 1}, + is_ground_content = false, + sounds = default.node_sound_metal_defaults(), + can_dig = digtron.can_dig, + on_blast = digtron.on_blast, + on_rightclick = assembled_digger_on_rightclick, +}) + +minetest.register_node("digtron:dual_soft_digger_static",{ + description = S("Digtron Dual Soft Digger"), + _doc_items_longdesc = nil, + _doc_items_usagehelp = nil, + _digtron_assembled_node = "digtron:dual_soft_digger", + drawtype = "mesh", + mesh = "digtron_dual_digger_static.obj", + tiles = { + { name = "digtron_plate.png^digtron_digger_yb_frame.png", backface_culling = true }, + { name = "digtron_plate.png^digtron_motor.png", backface_culling = true }, + { name = "digtron_drill_head_animated.png^[multiply:#FFCC88", backface_culling = true }, + { name = "digtron_plate.png", backface_culling = true }, + }, + collision_box = dual_drill_nodebox, + selection_box = dual_drill_nodebox, + paramtype2 = "facedir", + paramtype = "light", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, + is_ground_content = false, + sounds = default.node_sound_metal_defaults(), + on_rightclick = digger_on_rightclick, +}) + diff --git a/nodes/node_diggers.lua b/nodes/node_diggers.lua deleted file mode 100644 index 18f4ba3..0000000 --- a/nodes/node_diggers.lua +++ /dev/null @@ -1,515 +0,0 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - --- Note: diggers go in group 3 and have an execute_dig method. - -local damage_hp = digtron.config.damage_hp -local damage_hp_half = damage_hp/2 - -local digger_nodebox = { - {-0.5, -0.5, 0, 0.5, 0.5, 0.4375}, -- Block - {-0.4375, -0.3125, 0.4375, 0.4375, 0.3125, 0.5}, -- Cutter1 - {-0.3125, -0.4375, 0.4375, 0.3125, 0.4375, 0.5}, -- Cutter2 - {-0.5, -0.125, -0.125, 0.5, 0.125, 0}, -- BackFrame1 - {-0.125, -0.5, -0.125, 0.125, 0.5, 0}, -- BackFrame2 - {-0.25, -0.25, -0.5, 0.25, 0.25, 0}, -- Drive -} - -local dual_digger_nodebox = { - {-0.5, -0.4375, 0, 0.5, 0.5, 0.4375}, -- Block - {-0.4375, -0.3125, 0.4375, 0.4375, 0.3125, 0.5}, -- Cutter1 - {-0.3125, -0.4375, 0.4375, 0.3125, 0.4375, 0.5}, -- Cutter2 - {-0.5, 0, -0.125, 0.5, 0.125, 0}, -- BackFrame1 - {-0.25, 0, -0.5, 0.25, 0.25, 0}, -- Drive - {-0.25, 0.25, -0.25, 0.25, 0.5, 0}, -- Upper_Drive - {-0.5, -0.4375, -0.5, 0.5, 0, 0.4375}, -- Lower_Block - {-0.3125, -0.5, -0.4375, 0.3125, -0.4375, 0.4375}, -- Lower_Cutter_1 - {-0.4375, -0.5, -0.3125, 0.4375, -0.4375, 0.3125}, -- Lower_Cutter_2 -} - -local modpath_doc = minetest.get_modpath("doc") - -local intermittent_formspec_string = default.gui_bg .. - default.gui_bg_img .. - default.gui_slots .. - "field[0.5,0.8;1,0.1;period;" .. S("Periodicity") .. ";${period}]" .. - "tooltip[period;" .. S("Digger will dig once every n steps.\nThese steps are globally aligned, all diggers with\nthe same period and offset will dig on the same location.") .. "]" .. - "field[1.5,0.8;1,0.1;offset;" .. S("Offset") .. ";${offset}]" .. - "tooltip[offset;" .. S("Offsets the start of periodicity counting by this amount.\nFor example, a digger with period 2 and offset 0 digs\nevery even-numbered block and one with period 2 and\noffset 1 digs every odd-numbered block.") .. "]" .. - "button_exit[2.2,0.5;1,0.1;set;" .. S("Save &\nShow") .. "]" .. - "tooltip[set;" .. S("Saves settings") .. "]" - -if modpath_doc then - intermittent_formspec_string = "size[4.5,1]" .. intermittent_formspec_string .. - "button_exit[3.2,0.5;1,0.1;help;" .. S("Help") .. "]" .. - "tooltip[help;" .. S("Show documentation about this block") .. "]" - else - intermittent_formspec_string = "size[3.5,1]" .. intermittent_formspec_string - end - -local intermittent_formspec = function(pos, meta) - return intermittent_formspec_string - :gsub("${period}", meta:get_int("period"), 1) - :gsub("${offset}", meta:get_int("offset"), 1) - end - -local intermittent_on_construct = function(pos) - local meta = minetest.get_meta(pos) - meta:set_int("period", 1) - meta:set_int("offset", 0) -end - -local intermittent_on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - local item_def = itemstack:get_definition() - if item_def.type == "node" and minetest.get_item_group(itemstack:get_name(), "digtron") > 0 then - local returnstack, success = minetest.item_place_node(itemstack, clicker, pointed_thing) - if success and item_def.sounds and item_def.sounds.place and item_def.sounds.place.name then - minetest.sound_play(item_def.sounds.place, {pos = pos}) - end - return returnstack, success - end - local meta = minetest.get_meta(pos) - minetest.show_formspec(clicker:get_player_name(), - "digtron:intermittent_digger"..minetest.pos_to_string(pos), - intermittent_formspec(pos, meta)) -end - -minetest.register_on_player_receive_fields(function(player, formname, fields) - if formname:sub(1, 27) == "digtron:intermittent_digger" then - local pos = minetest.string_to_pos(formname:sub(28, -1)) - local meta = minetest.get_meta(pos) - local period = tonumber(fields.period) - local offset = tonumber(fields.offset) - if period and period > 0 then - meta:set_int("period", math.floor(period)) - end - if offset then - meta:set_int("offset", math.floor(offset)) - end - if fields.help and minetest.get_modpath("doc") then --check for mod in case someone disabled it after this digger was built - local node_name = minetest.get_node(pos).name - minetest.after(0.5, doc.show_entry, player:get_player_name(), "nodes", node_name, true) - end - if fields.set then - digtron.show_offset_markers(pos, offset, period) - end - return true - end -end) - - --- Digs out nodes that are "in front" of the digger head. -minetest.register_node("digtron:digger", { - description = S("Digtron Digger Head"), - _doc_items_longdesc = digtron.doc.digger_longdesc, - _doc_items_usagehelp = digtron.doc.digger_usagehelp, - groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 3}, - drop = "digtron:digger", - sounds = digtron.metal_sounds, - paramtype = "light", - paramtype2= "facedir", - is_ground_content = false, - drawtype="nodebox", - node_box = { - type = "fixed", - fixed = digger_nodebox, - }, - - -- Aims in the +Z direction by default - tiles = { - "digtron_plate.png^[transformR90", - "digtron_plate.png^[transformR270", - "digtron_plate.png", - "digtron_plate.png^[transformR180", - { - name = "digtron_digger_yb.png", - animation = { - type = "vertical_frames", - aspect_w = 16, - aspect_h = 16, - length = 1.0, - }, - }, - "digtron_plate.png^digtron_motor.png", - }, - - -- returns fuel_cost, item_produced - execute_dig = function(pos, protected_nodes, nodes_dug, controlling_coordinate, lateral_dig, player) - local facing = minetest.get_node(pos).param2 - local digpos = digtron.find_new_pos(pos, facing) - - if protected_nodes:get(digpos.x, digpos.y, digpos.z) then - return 0 - end - - return digtron.mark_diggable(digpos, nodes_dug, player) - end, - - damage_creatures = function(player, pos, controlling_coordinate, items_dropped) - local facing = minetest.get_node(pos).param2 - digtron.damage_creatures(player, pos, digtron.find_new_pos(pos, facing), damage_hp, items_dropped) - end, -}) - --- Digs out nodes that are "in front" of the digger head. -minetest.register_node("digtron:intermittent_digger", { - description = S("Digtron Intermittent Digger Head"), - _doc_items_longdesc = digtron.doc.intermittent_digger_longdesc, - _doc_items_usagehelp = digtron.doc.intermittent_digger_usagehelp, - _digtron_formspec = intermittent_formspec, - groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 3}, - drop = "digtron:intermittent_digger", - sounds = digtron.metal_sounds, - paramtype = "light", - paramtype2= "facedir", - is_ground_content = false, - drawtype="nodebox", - node_box = { - type = "fixed", - fixed = digger_nodebox, - }, - - -- Aims in the +Z direction by default - tiles = { - "digtron_plate.png^[transformR90", - "digtron_plate.png^[transformR270", - "digtron_plate.png", - "digtron_plate.png^[transformR180", - { - name = "digtron_digger_yb.png", - animation = { - type = "vertical_frames", - aspect_w = 16, - aspect_h = 16, - length = 1.0, - }, - }, - "digtron_plate.png^digtron_intermittent.png^digtron_motor.png", - }, - - on_construct = intermittent_on_construct, - - on_rightclick = intermittent_on_rightclick, - - -- returns fuel_cost, item_produced (a table or nil) - execute_dig = function(pos, protected_nodes, nodes_dug, controlling_coordinate, lateral_dig, player) - if lateral_dig == true then - return 0 - end - - local facing = minetest.get_node(pos).param2 - local digpos = digtron.find_new_pos(pos, facing) - - if protected_nodes:get(digpos.x, digpos.y, digpos.z) then - return 0 - end - - local meta = minetest.get_meta(pos) - if (digpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") ~= 0 then - return 0 - end - - return digtron.mark_diggable(digpos, nodes_dug, player) - end, - - damage_creatures = function(player, pos, controlling_coordinate, items_dropped) - local facing = minetest.get_node(pos).param2 - local targetpos = digtron.find_new_pos(pos, facing) - local meta = minetest.get_meta(pos) - if (targetpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") == 0 then - digtron.damage_creatures(player, pos, targetpos, damage_hp, items_dropped) - end - end -}) - --- A special-purpose digger to deal with stuff like sand and gravel in the ceiling. It always digs (no periodicity or offset), but it only digs falling_block nodes -minetest.register_node("digtron:soft_digger", { - description = S("Digtron Soft Material Digger Head"), - _doc_items_longdesc = digtron.doc.soft_digger_longdesc, - _doc_items_usagehelp = digtron.doc.soft_digger_usagehelp, - groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 3}, - drop = "digtron:soft_digger", - sounds = digtron.metal_sounds, - paramtype = "light", - paramtype2= "facedir", - is_ground_content = false, - drawtype="nodebox", - node_box = { - type = "fixed", - fixed = digger_nodebox, - }, - - -- Aims in the +Z direction by default - tiles = { - "digtron_plate.png^[transformR90^[colorize:" .. digtron.soft_digger_colorize, - "digtron_plate.png^[transformR270^[colorize:" .. digtron.soft_digger_colorize, - "digtron_plate.png^[colorize:" .. digtron.soft_digger_colorize, - "digtron_plate.png^[transformR180^[colorize:" .. digtron.soft_digger_colorize, - { - name = "digtron_digger_yb.png^[colorize:" .. digtron.soft_digger_colorize, - animation = { - type = "vertical_frames", - aspect_w = 16, - aspect_h = 16, - length = 1.0, - }, - }, - "digtron_plate.png^digtron_motor.png^[colorize:" .. digtron.soft_digger_colorize, - }, - - execute_dig = function(pos, protected_nodes, nodes_dug, controlling_coordinate, lateral_dig, player) - local facing = minetest.get_node(pos).param2 - local digpos = digtron.find_new_pos(pos, facing) - - if protected_nodes:get(digpos.x, digpos.y, digpos.z) then - return 0 - end - - if digtron.is_soft_material(digpos) then - return digtron.mark_diggable(digpos, nodes_dug, player) - end - - return 0 - end, - - damage_creatures = function(player, pos, controlling_coordinate, items_dropped) - local facing = minetest.get_node(pos).param2 - digtron.damage_creatures(player, pos, digtron.find_new_pos(pos, facing), damage_hp_half, items_dropped) - end, -}) - -minetest.register_node("digtron:intermittent_soft_digger", { - description = S("Digtron Intermittent Soft Material Digger Head"), - _doc_items_longdesc = digtron.doc.intermittent_soft_digger_longdesc, - _doc_items_usagehelp = digtron.doc.intermittent_soft_digger_usagehelp, - _digtron_formspec = intermittent_formspec, - groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 3}, - drop = "digtron:intermittent_soft_digger", - sounds = digtron.metal_sounds, - paramtype = "light", - paramtype2= "facedir", - is_ground_content = false, - drawtype="nodebox", - node_box = { - type = "fixed", - fixed = digger_nodebox, - }, - - -- Aims in the +Z direction by default - tiles = { - "digtron_plate.png^[transformR90^[colorize:" .. digtron.soft_digger_colorize, - "digtron_plate.png^[transformR270^[colorize:" .. digtron.soft_digger_colorize, - "digtron_plate.png^[colorize:" .. digtron.soft_digger_colorize, - "digtron_plate.png^[transformR180^[colorize:" .. digtron.soft_digger_colorize, - { - name = "digtron_digger_yb.png^[colorize:" .. digtron.soft_digger_colorize, - animation = { - type = "vertical_frames", - aspect_w = 16, - aspect_h = 16, - length = 1.0, - }, - }, - "digtron_plate.png^digtron_intermittent.png^digtron_motor.png^[colorize:" .. digtron.soft_digger_colorize, - }, - - on_construct = intermittent_on_construct, - - on_rightclick = intermittent_on_rightclick, - - execute_dig = function(pos, protected_nodes, nodes_dug, controlling_coordinate, lateral_dig, player) - if lateral_dig == true then - return 0 - end - - local facing = minetest.get_node(pos).param2 - local digpos = digtron.find_new_pos(pos, facing) - - if protected_nodes:get(digpos.x, digpos.y, digpos.z) then - return 0 - end - - local meta = minetest.get_meta(pos) - if (digpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") ~= 0 then - return 0 - end - - if digtron.is_soft_material(digpos) then - return digtron.mark_diggable(digpos, nodes_dug, player) - end - - return 0 - end, - - damage_creatures = function(player, pos, controlling_coordinate, items_dropped) - local meta = minetest.get_meta(pos) - local facing = minetest.get_node(pos).param2 - local targetpos = digtron.find_new_pos(pos, facing) - if (targetpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") == 0 then - digtron.damage_creatures(player, pos, targetpos, damage_hp_half, items_dropped) - end - end, -}) - --- Digs out nodes that are "in front" of the digger head and "below" the digger head (can be rotated). -minetest.register_node("digtron:dual_digger", { - description = S("Digtron Dual Digger Head"), - _doc_items_longdesc = digtron.doc.dual_digger_longdesc, - _doc_items_usagehelp = digtron.doc.dual_digger_usagehelp, - groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 3}, - drop = "digtron:dual_digger", - sounds = digtron.metal_sounds, - paramtype = "light", - paramtype2= "facedir", - is_ground_content = false, - drawtype="nodebox", - node_box = { - type = "fixed", - fixed = dual_digger_nodebox, - }, - - -- Aims in the +Z and -Y direction by default - tiles = { - "digtron_plate.png^digtron_motor.png", - { - name = "digtron_digger_yb.png", - animation = { - type = "vertical_frames", - aspect_w = 16, - aspect_h = 16, - length = 1.0, - }, - }, - "digtron_plate.png", - "digtron_plate.png^[transformR180", - { - name = "digtron_digger_yb.png", - animation = { - type = "vertical_frames", - aspect_w = 16, - aspect_h = 16, - length = 1.0, - }, - }, - "digtron_plate.png^digtron_motor.png", - }, - - -- returns fuel_cost, items_produced - execute_dig = function(pos, protected_nodes, nodes_dug, controlling_coordinate, lateral_dig, player) - local facing = minetest.get_node(pos).param2 - local digpos = digtron.find_new_pos(pos, facing) - local digdown = digtron.find_new_pos_downward(pos, facing) - - local items = {} - local cost = 0 - - if protected_nodes:get(digpos.x, digpos.y, digpos.z) ~= true then - local forward_cost, forward_items = digtron.mark_diggable(digpos, nodes_dug, player) - if forward_items ~= nil then - for _, item in pairs(forward_items) do - table.insert(items, item) - end - end - cost = cost + forward_cost - end - if protected_nodes:get(digdown.x, digdown.y, digdown.z) ~= true then - local down_cost, down_items = digtron.mark_diggable(digdown, nodes_dug, player) - if down_items ~= nil then - for _, item in pairs(down_items) do - table.insert(items, item) - end - end - cost = cost + down_cost - end - - return cost, items - end, - - damage_creatures = function(player, pos, controlling_coordinate, items_dropped) - local facing = minetest.get_node(pos).param2 - digtron.damage_creatures(player, pos, digtron.find_new_pos(pos, facing), damage_hp, items_dropped) - digtron.damage_creatures(player, pos, digtron.find_new_pos_downward(pos, facing), damage_hp, items_dropped) - end, -}) - --- Digs out soft nodes that are "in front" of the digger head and "below" the digger head (can be rotated). -minetest.register_node("digtron:dual_soft_digger", { - description = S("Digtron Dual Soft Material Digger Head"), - _doc_items_longdesc = digtron.doc.dual_soft_digger_longdesc, - _doc_items_usagehelp = digtron.doc.dual_soft_digger_usagehelp, - groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 3}, - drop = "digtron:dual_soft_digger", - sounds = digtron.metal_sounds, - paramtype = "light", - paramtype2= "facedir", - is_ground_content = false, - drawtype="nodebox", - node_box = { - type = "fixed", - fixed = dual_digger_nodebox, - }, - - -- Aims in the +Z and -Y direction by default - tiles = { - "digtron_plate.png^digtron_motor.png^[colorize:" .. digtron.soft_digger_colorize, - { - name = "digtron_digger_yb.png^[colorize:" .. digtron.soft_digger_colorize, - animation = { - type = "vertical_frames", - aspect_w = 16, - aspect_h = 16, - length = 1.0, - }, - }, - "digtron_plate.png^[colorize:" .. digtron.soft_digger_colorize, - "digtron_plate.png^[transformR180^[colorize:" .. digtron.soft_digger_colorize, - { - name = "digtron_digger_yb.png^[colorize:" .. digtron.soft_digger_colorize, - animation = { - type = "vertical_frames", - aspect_w = 16, - aspect_h = 16, - length = 1.0, - }, - }, - "digtron_plate.png^digtron_motor.png^[colorize:" .. digtron.soft_digger_colorize, - }, - - -- returns fuel_cost, items_produced - execute_dig = function(pos, protected_nodes, nodes_dug, controlling_coordinate, lateral_dig, player) - local facing = minetest.get_node(pos).param2 - local digpos = digtron.find_new_pos(pos, facing) - local digdown = digtron.find_new_pos_downward(pos, facing) - - local items = {} - local cost = 0 - - if protected_nodes:get(digpos.x, digpos.y, digpos.z) ~= true and digtron.is_soft_material(digpos) then - local forward_cost, forward_items = digtron.mark_diggable(digpos, nodes_dug, player) - if forward_items ~= nil then - for _, item in pairs(forward_items) do - table.insert(items, item) - end - end - cost = cost + forward_cost - end - if protected_nodes:get(digdown.x, digdown.y, digdown.z) ~= true and digtron.is_soft_material(digdown) then - local down_cost, down_items = digtron.mark_diggable(digdown, nodes_dug, player) - if down_items ~= nil then - for _, item in pairs(down_items) do - table.insert(items, item) - end - end - cost = cost + down_cost - end - - return cost, items - end, - - damage_creatures = function(player, pos, controlling_coordinate, items_dropped) - local facing = minetest.get_node(pos).param2 - digtron.damage_creatures(player, pos, digtron.find_new_pos(pos, facing), damage_hp_half, items_dropped) - digtron.damage_creatures(player, pos, digtron.find_new_pos_downward(pos, facing), damage_hp_half, items_dropped) - end, -}) \ No newline at end of file diff --git a/nodes/node_duplicator.lua b/nodes/node_duplicator.lua index a4f87b3..2949cb2 100644 --- a/nodes/node_duplicator.lua +++ b/nodes/node_duplicator.lua @@ -1,26 +1,135 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") +local S = digtron.S -local inventory_formspec_string = - "size[9,9.3]" .. - default.gui_bg .. - default.gui_bg_img .. - default.gui_slots .. - "label[0,0;" .. S("Digtron components") .. "]" .. - "list[current_name;main;0,0.6;8,4;]" .. - "list[current_player;main;0,5.15;8,1;]" .. - "list[current_player;main;0,6.38;8,3;8]" .. - "listring[current_name;main]" .. - "listring[current_player;main]" .. - default.get_hotbar_bg(0,5.15).. - "button_exit[8,3.5;1,1;duplicate;"..S("Duplicate").."]" .. - "tooltip[duplicate;" .. S("Puts a copy of the adjacent Digtron into an empty crate\nlocated at the output side of the duplicator,\nusing components from the duplicator's inventory.") .. "]" +-- Determines how many of each type of Digtron node is needed to build another Digtron +local get_manifest = function(pos) + local manifest = {} + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + local template = inv:get_stack("template", 1) + local stack_meta = template:get_meta() + local digtron_id = stack_meta:get_string("digtron_id") + if digtron_id ~= "" then + local layout = digtron.get_layout(digtron_id) + for layout_node_id, data in pairs(layout) do + local item = data.node.name + local item_def = minetest.registered_items[item] + -- Some digtron nodes change into other nodes when they become active. + -- This determines what the original node was in those cases + if item_def._digtron_disassembled_node then + item = item_def._digtron_disassembled_node + item_def = minetest.registered_items[item] + end + local desc = item_def.description + local entry = manifest[desc] + if entry == nil then + entry = {item = item} + manifest[desc] = entry + elseif entry.item ~= item then + minetest.log("error", "[Digtron] Duplicator found two digtron nodes that are defined with the same description: " + .. item .. " and " .. entry.item .. ". File an issue with Digtron's maintainers.") + end + entry.requires = (entry.requires or 0) + 1 + end + end -if minetest.get_modpath("doc") then - inventory_formspec_string = inventory_formspec_string .. - "button_exit[8,4.5;1,1;help;"..S("Help").."]" .. - "tooltip[help;" .. S("Show documentation about this block") .. "]" + local main_list = inv:get_list("main") + for _, itemstack in ipairs(main_list) do + if not itemstack:is_empty() then + local desc = itemstack:get_definition().description + local entry = manifest[desc] + if entry == nil then + entry = {item = item} + manifest[desc] = entry + end + entry.contains = (entry.contains or 0) + itemstack:get_count() + end + end + + local ok = false + if digtron_id ~= "" then + ok = true + for item, entry in pairs(manifest) do + if entry.requires and (entry.contains == nil or entry.contains < entry.requires) then + ok = false + break + end + end + end + + return manifest, ok, digtron_id +end + +local cache = {} + +local get_formspec = function(pos) + local hash = minetest.hash_node_position(pos) + local cache_val = cache[hash] + if cache_val == nil then + cache_val = {} + cache_val.manifest, cache_val.ok = get_manifest(pos) + cache[hash] = cache_val + end + local manifest = cache_val.manifest + local ok = cache_val.ok + + -- Build item table + local manifest_formspec_head = "tablecolumns[color;text,tooltip=" .. S("Digtron component.") + ..";text,align=center,tooltip=" .. S("Amount of this component required to copy the template Digtron.") + ..";text,align=center,tooltip=" .. S("Amount of this component currently available.") + .."]table[0,0;2.9,3;manifest;#FFFFFF,Item,Required,Available" + local manifest_formspec_body = {} + for desc, entry in pairs(manifest) do + local color = "#FFFFFF" + if entry.requires then + if entry.contains == nil or entry.contains < entry.requires then + color = "#FF0000" + else + color = "#00FF00" + end + end + manifest_formspec_body[#manifest_formspec_body + 1] = + ","..color..","..desc..","..(entry.requires or "-")..","..(entry.contains or "-") + end + table.sort(manifest_formspec_body) + local manifest_formspec_tail = ";]" + local manifest_formspec = manifest_formspec_head .. table.concat(manifest_formspec_body) .. manifest_formspec_tail + + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + + -- Duplicate button + local duplicate_button + if ok and inv:is_empty("copy") then + duplicate_button = "button[1,0;1,1;duplicate;"..S("Duplicate").."]tooltip[duplicate;" + .. S("Puts a copy of the template Digtron into the output inventory slot.") .. "]" + else + duplicate_button = "button[1,0;1,1;no_duplicate;X]tooltip[no_duplicate;" + .. S("Duplication cannot proceed at this time.") .. "]" + end + + return "size[8,9.3]" + + .. "container[5,1.5]" + .. manifest_formspec + .. "container_end[]" + + .."label[0,0;" .. S("Digtron components") .. "]" + .."list[current_name;main;0,0.6;5,4;]" + .."tooltip[0,0;5,4.6;".. S("Digtron components in this inventory will be used to create the duplicate.") .."]" + .."container[5,0]" + .."list[current_name;template;0,0;1,1;]" + .."tooltip[0,0;1,1.25;".. S("Place the Digtron you want to make a copy of here.") .."]" + .."label[0.1,0.8;" .. S("Template") .. "]" + ..duplicate_button + .."list[current_name;copy;2,0;1,1;]" + .."tooltip[2,0;1,1.25;".. S("The duplicate Digtron is output here.") .."]" + .."label[2.25,0.8;" .. S("Copy") .. "]" + .."container_end[]" + .."list[current_player;main;0,5.15;8,1;]" + .."list[current_player;main;0,6.38;8,3;8]" + .."listring[current_name;main]" + .."listring[current_player;main]" + ..default.get_hotbar_bg(0,5.15) end minetest.register_node("digtron:duplicator", { @@ -63,126 +172,104 @@ minetest.register_node("digtron:duplicator", { on_construct = function(pos) local meta = minetest.get_meta(pos) - meta:set_string("formspec", inventory_formspec_string) + meta:set_string("formspec", get_formspec(pos)) local inv = meta:get_inventory() - inv:set_size("main", 8*4) + inv:set_size("main", 5*4) + inv:set_size("template", 1) + inv:set_size("copy", 1) + end, + + on_destruct = function(pos) + cache[minetest.hash_node_position(pos)] = nil end, can_dig = function(pos,player) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() - return inv:is_empty("main") + return inv:is_empty("main") and inv:is_empty("template") and inv:is_empty("copy") end, allow_metadata_inventory_put = function(pos, listname, index, stack, player) - if minetest.get_item_group(stack:get_name(), "digtron") > 0 then + local stack_name = stack:get_name() + if listname == "main" and (minetest.get_item_group(stack_name, "digtron") > 0 or stack_name == "digtron:controller_unassembled") then return stack:get_count() - else - return 0 + elseif listname == "template" and stack:get_name() == "digtron:controller" then + return stack:get_count() + end + return 0 + end, + + allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) + if from_list == to_list then + return count end + return 0 + end, + + allow_metadata_inventory_take = function(pos, listname, index, stack, player) + return stack:get_count() + end, + + on_metadata_inventory_put = function(pos, listname, index, stack, player) + local meta = minetest.get_meta(pos) + cache[minetest.hash_node_position(pos)] = nil + meta:set_string("formspec", get_formspec(pos)) + end, + on_metadata_inventory_take = function(pos, listname, index, stack, player) + local meta = minetest.get_meta(pos) + cache[minetest.hash_node_position(pos)] = nil + meta:set_string("formspec", get_formspec(pos)) end, on_receive_fields = function(pos, formname, fields, sender) if fields.help then minetest.after(0.5, doc.show_entry, sender:get_player_name(), "nodes", "digtron:duplicator", true) end + + if fields.no_duplicate then + minetest.sound_play("digtron_error", {gain=0.5, to_player=sender:get_player_name()}) -- Insufficient inventory + end if fields.duplicate then - local node = minetest.get_node(pos) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() - local target_pos = vector.add(pos, minetest.facedir_to_dir(node.param2)) - local target_node = minetest.get_node(target_pos) - - if target_node.name ~= "digtron:empty_crate" then - minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - meta:set_string("infotext", S("Needs an empty crate in output position to store duplicate")) - return - end - - local layout = DigtronLayout.create(pos, sender) - if layout.contains_protected_node then - minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - meta:set_string("infotext", S("Digtron can't be duplicated, it contains protected blocks")) + if not inv:is_empty("copy") then + minetest.log("error", "[Digtron] duplicator was sent a 'duplicate' command by " .. player_name + .. "but there was an item in the output inventory already. This should be impossible.") + minetest.sound_play("digtron_error", {gain=0.5, to_player=sender:get_player_name()}) return end - - if #layout.all == 1 then - minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - meta:set_string("infotext", S("No Digtron components adjacent to duplicate")) - return - end - - layout.all[1] = {node={name="digtron:empty_crate"}, meta={fields = {}, inventory = {}}, pos={x=pos.x, y=pos.y, z=pos.z}} -- replace the duplicator's image with the empty crate image - - -- count required nodes, skipping node 1 since it's the crate and we already know it's present in-world - local required_count = {} - for i = 2, #layout.all do - local nodename = layout.all[i].node.name - required_count[nodename] = (required_count[nodename] or 0) + 1 - end - - -- check that there's enough in the duplicator's inventory - local unsatisfied = {} - for name, count in pairs(required_count) do - if not inv:contains_item("main", ItemStack({name=name, count=count})) then - table.insert(unsatisfied, tostring(count) .. " " .. minetest.registered_nodes[name].description) - end - end - if #unsatisfied > 0 then - minetest.sound_play("dingding", {gain=1.0, pos=pos}) -- Insufficient inventory - meta:set_string("infotext", S("Duplicator requires:\n@1", table.concat(unsatisfied, "\n"))) + + local manifest, ok, digtron_id = get_manifest(pos) -- don't trust formspec fields, that's hackable. Recalculate manifest. + if not ok then + local player_name = sender:get_player_name() + minetest.log("error", "[Digtron] duplicator was sent a 'duplicate' command by " .. player_name + .. "but get_manifest reported insufficent inputs. This should be impossible.") + minetest.sound_play("digtron_error", {gain=0.5, to_player=player_name}) return end - meta:set_string("infotext", "") -- clear infotext, we're good to go. - -- deduct nodes from duplicator inventory - for name, count in pairs(required_count) do - inv:remove_item("main", ItemStack({name=name, count=count})) - end - - -- clear inventories of image's nodes - if layout.inventories ~= nil then - for _, node_image in pairs(layout.inventories) do - local main_inventory = node_image.meta.inventory.main - if type(main_inventory) ~= "table" then - main_inventory = {} - end - for index, _ in pairs(main_inventory) do - main_inventory[index] = ItemStack(nil) + for desc, entry in pairs(manifest) do + if entry.requires then + local count = entry.requires + while count > 0 do + -- We need to do this loop because we may be wanting to remove more items than + -- a single stack of that item can hold. + -- https://github.com/minetest/minetest/issues/8883 + local stack_to_remove = ItemStack({name=entry.item, count=count}) + stack_to_remove:set_count(math.min(count, stack_to_remove:get_stack_max())) + local removed = inv:remove_item("main", stack_to_remove) + count = count - removed:get_count() end end end - if layout.fuelstores ~= nil then - for _, node_image in pairs(layout.fuelstores) do - local fuel_inventory = node_image.meta.inventory.fuel - for index, _ in pairs(fuel_inventory) do - fuel_inventory[index] = ItemStack(nil) - end - end - end - if layout.battery_holders ~= nil then - for _, node_image in pairs(layout.battery_holders) do - local battery_inventory = node_image.meta.inventory.batteries - for index, _ in pairs(battery_inventory) do - battery_inventory[index] = ItemStack(nil) - end - end - end - - -- replace empty crate with loaded crate and write image to its metadata - local layout_string = layout:serialize() - minetest.set_node(target_pos, {name="digtron:loaded_crate", param1=node.param1, param2=node.param2}) - local target_meta = minetest.get_meta(target_pos) - target_meta:set_string("crated_layout", layout_string) + local new_digtron = digtron.duplicate(digtron_id) + inv:set_stack("copy", 1, new_digtron) - local titlestring = S("Crated @1-block Digtron", tostring(#layout.all-1)) - target_meta:set_string("title", titlestring) - target_meta:set_string("infotext", titlestring) - minetest.sound_play("machine1", {gain=1.0, pos=pos}) + minetest.sound_play("digtron_machine_assemble", {gain=1.0, pos=pos}) end end, diff --git a/nodes/node_item_ejector.lua b/nodes/node_item_ejector.lua deleted file mode 100644 index 55d3607..0000000 --- a/nodes/node_item_ejector.lua +++ /dev/null @@ -1,174 +0,0 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - ---Build up the formspec, somewhat complicated due to multiple mod options -local pipeworks_path = minetest.get_modpath("pipeworks") -local doc_path = minetest.get_modpath("doc") -local formspec_width = 1.5 - -local ejector_formspec_string = - default.gui_bg .. - default.gui_bg_img .. - default.gui_slots - -if doc_path then - ejector_formspec_string = ejector_formspec_string .. - "button_exit[".. 0.2 + formspec_width ..",0.5;1,0.1;help;" .. S("Help") .. "]" .. - "tooltip[help;" .. S("Show documentation about this block") .. "]" - formspec_width = formspec_width + 1.5 -end - -local ejector_formspec_string = "size[".. formspec_width .. ",1]" .. ejector_formspec_string - -local ejector_formspec = function(pos, meta) - local return_string = ejector_formspec_string - if pipeworks_path then - return_string = return_string .. "checkbox[0,0.5;nonpipe;"..S("Eject into world")..";"..meta:get_string("nonpipe").."]" .. - "tooltip[nonpipe;" .. S("When checked, will eject items even if there's no pipe to accept it") .. "]" - end - return return_string .. "checkbox[0,0;autoeject;"..S("Automatic")..";"..meta:get_string("autoeject").."]" .. - "tooltip[autoeject;" .. S("When checked, will eject items automatically with every Digtron cycle.\nItem ejectors can always be operated manually by punching them.") .. "]" -end - -local function eject_items(pos, node, player, eject_even_without_pipeworks, layout) - local dir = minetest.facedir_to_dir(node.param2) - local destination_pos = vector.add(pos, dir) - local destination_node_name = minetest.get_node(destination_pos).name - local destination_node_def = minetest.registered_nodes[destination_node_name] - - if not pipeworks_path then eject_even_without_pipeworks = true end -- if pipeworks is not installed, always eject into world (there's no other option) - - local insert_into_pipe = false - local eject_into_world = false - if pipeworks_path and minetest.get_node_group(destination_node_name, "tubedevice") > 0 then - insert_into_pipe = true - elseif eject_even_without_pipeworks then - if destination_node_def and not destination_node_def.walkable then - eject_into_world = true - else - minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - return false - end - else - return false - end - - if layout == nil then - layout = DigtronLayout.create(pos, player) - end - - -- Build a list of all the items that builder nodes want to use. - local filter_items = {} - if layout.builders ~= nil then - for _, node_image in pairs(layout.builders) do - filter_items[node_image.meta.inventory.main[1]:get_name()] = true - end - end - - -- Look through the inventories and find an item that's not on that list. - local source_node = nil - local source_index = nil - local source_stack = nil - for _, node_image in pairs(layout.inventories or {}) do - if type(node_image.meta.inventory.main) ~= "table" then - node_image.meta.inventory.main = {} - end - for index, item_stack in pairs(node_image.meta.inventory.main) do - if item_stack:get_count() > 0 and not filter_items[item_stack:get_name()] then - source_node = node_image - source_index = index - source_stack = item_stack - node_image.meta.inventory.main[index] = nil - break - end - end - if source_node then break end - end - - if source_node then - local meta = minetest.get_meta(source_node.pos) - local inv = meta:get_inventory() - - if insert_into_pipe then - local from_pos = vector.add(pos, vector.multiply(dir, 0.5)) - local start_pos = pos - inv:set_stack("main", source_index, nil) - pipeworks.tube_inject_item(from_pos, start_pos, vector.multiply(dir, 1), source_stack, player:get_player_name()) - minetest.sound_play("steam_puff", {gain=0.5, pos=pos}) - return true - elseif eject_into_world then - minetest.add_item(destination_pos, source_stack) - inv:set_stack("main", source_index, nil) - minetest.sound_play("steam_puff", {gain=0.5, pos=pos}) - return true - end - end - - -- couldn't find an item to eject - return false -end - -minetest.register_node("digtron:inventory_ejector", { - description = S("Digtron Inventory Ejector"), - _doc_items_longdesc = digtron.doc.inventory_ejector_longdesc, - _doc_items_usagehelp = digtron.doc.inventory_ejector_usagehelp, - _digtron_formspec = ejector_formspec, - groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 9, tubedevice = 1}, - tiles = {"digtron_plate.png", "digtron_plate.png", "digtron_plate.png", "digtron_plate.png", "digtron_plate.png^digtron_output.png", "digtron_plate.png^digtron_output_back.png"}, - drawtype = "nodebox", - sounds = digtron.metal_sounds, - paramtype = "light", - paramtype2 = "facedir", - is_ground_content = false, - node_box = { - type = "fixed", - fixed = { - {-0.5, -0.5, -0.5, 0.5, 0.5, 0.1875}, -- NodeBox1 - {-0.3125, -0.3125, 0.1875, 0.3125, 0.3125, 0.3125}, -- NodeBox2 - {-0.1875, -0.1875, 0.3125, 0.1875, 0.1875, 0.5}, -- NodeBox3 - } - }, - - on_construct = function(pos) - local meta = minetest.get_meta(pos) - meta:set_string("autoeject", "true") - meta:set_string("formspec", ejector_formspec(pos, meta)) - end, - - tube = (function() if pipeworks_path then return { - connect_sides = {back = 1} - } end end)(), - - on_punch = function(pos, node, player) - eject_items(pos, node, player, true) - end, - - execute_eject = function(pos, node, player, layout) - local meta = minetest.get_meta(pos) - eject_items(pos, node, player, meta:get_string("nonpipe") == "true", layout) - end, - - on_receive_fields = function(pos, formname, fields, sender) - local meta = minetest.get_meta(pos) - - if fields.help and minetest.get_modpath("doc") then --check for mod in case someone disabled it after this digger was built - local node_name = minetest.get_node(pos).name - minetest.after(0.5, doc.show_entry, sender:get_player_name(), "nodes", node_name, true) - end - - if fields.nonpipe then - meta:set_string("nonpipe", fields.nonpipe) - end - - if fields.autoeject then - meta:set_string("autoeject", fields.autoeject) - end - - meta:set_string("formspec", ejector_formspec(pos, meta)) - - end, - - after_place_node = (function() if pipeworks_path then return pipeworks.after_place end end)(), - after_dig_node = (function() if pipeworks_path then return pipeworks.after_dig end end)() -}) diff --git a/nodes/node_misc.lua b/nodes/node_misc.lua index cea823e..3980e66 100644 --- a/nodes/node_misc.lua +++ b/nodes/node_misc.lua @@ -1,6 +1,4 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") +local S = digtron.S -- A do-nothing "structural" node, to ensure all digtron nodes that are supposed to be connected to each other can be connected to each other. minetest.register_node("digtron:structure", { @@ -8,10 +6,9 @@ minetest.register_node("digtron:structure", { _doc_items_longdesc = digtron.doc.structure_longdesc, _doc_items_usagehelp = digtron.doc.structure_usagehelp, groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 1}, - drop = "digtron:structure", tiles = {"digtron_plate.png"}, drawtype = "nodebox", - sounds = digtron.metal_sounds, + sounds = default.node_sound_metal_defaults(), climbable = true, walkable = false, paramtype = "light", @@ -33,6 +30,8 @@ minetest.register_node("digtron:structure", { {-0.3125, -0.5, -0.5, 0.3125, -0.3125, -0.3125}, } }, + can_dig = digtron.can_dig, + on_blast = digtron.on_blast, }) -- A modest light source that will move with the digtron, handy for working in a tunnel you aren't bothering to install permanent lights in. @@ -41,7 +40,6 @@ minetest.register_node("digtron:light", { _doc_items_longdesc = digtron.doc.light_longdesc, _doc_items_usagehelp = digtron.doc.light_usagehelp, groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 1}, - drop = "digtron:light", tiles = {"digtron_plate.png^digtron_light.png"}, drawtype = "nodebox", paramtype = "light", @@ -55,6 +53,8 @@ minetest.register_node("digtron:light", { wall_bottom = {-0.25, -0.5, -0.25, 0.25, -0.3125, 0.25}, wall_side = {-0.5, -0.25, -0.25, -0.1875, 0.25, 0.25}, }, + can_dig = digtron.can_dig, + on_blast = digtron.on_blast, }) -- A simple structural panel @@ -63,12 +63,11 @@ minetest.register_node("digtron:panel", { _doc_items_longdesc = digtron.doc.panel_longdesc, _doc_items_usagehelp = digtron.doc.panel_usagehelp, groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 1}, - drop = "digtron:panel", tiles = {"digtron_plate.png"}, drawtype = "nodebox", paramtype = "light", is_ground_content = false, - sounds = digtron.metal_sounds, + sounds = default.node_sound_metal_defaults(), paramtype2 = "facedir", node_box = { type = "fixed", @@ -78,6 +77,8 @@ minetest.register_node("digtron:panel", { type = "fixed", fixed = {-0.5, -0.5, -0.5, 0.5, -0.4375, 0.5}, }, + can_dig = digtron.can_dig, + on_blast = digtron.on_blast, }) -- A simple structural panel @@ -86,12 +87,11 @@ minetest.register_node("digtron:edge_panel", { _doc_items_longdesc = digtron.doc.edge_panel_longdesc, _doc_items_usagehelp = digtron.doc.edge_panel_usagehelp, groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 1}, - drop = "digtron:edge_panel", tiles = {"digtron_plate.png"}, drawtype = "nodebox", paramtype = "light", is_ground_content = false, - sounds = digtron.metal_sounds, + sounds = default.node_sound_metal_defaults(), paramtype2 = "facedir", node_box = { type = "fixed", @@ -107,7 +107,8 @@ minetest.register_node("digtron:edge_panel", { {-0.5, -0.5, -0.5, 0.5, -0.4375, 0.4375} }, }, - + can_dig = digtron.can_dig, + on_blast = digtron.on_blast, }) minetest.register_node("digtron:corner_panel", { @@ -115,12 +116,11 @@ minetest.register_node("digtron:corner_panel", { _doc_items_longdesc = digtron.doc.corner_panel_longdesc, _doc_items_usagehelp = digtron.doc.corner_panel_usagehelp, groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 1}, - drop = "digtron:corner_panel", tiles = {"digtron_plate.png"}, drawtype = "nodebox", paramtype = "light", is_ground_content = false, - sounds = digtron.metal_sounds, + sounds = default.node_sound_metal_defaults(), paramtype2 = "facedir", node_box = { type = "fixed", @@ -138,4 +138,6 @@ minetest.register_node("digtron:corner_panel", { {-0.5, -0.4375, -0.5, -0.4375, 0.5, 0.4375}, }, }, + can_dig = digtron.can_dig, + on_blast = digtron.on_blast, }) \ No newline at end of file diff --git a/nodes/node_pipeworks_interface.lua b/nodes/node_pipeworks_interface.lua new file mode 100644 index 0000000..9dfc4ef --- /dev/null +++ b/nodes/node_pipeworks_interface.lua @@ -0,0 +1,134 @@ +local S = digtron.S + +--Build up the formspec, somewhat complicated due to multiple mod options +local pipeworks_path = minetest.get_modpath("pipeworks") + + +local function eject_items(pos, node, player, eject_even_without_pipeworks, layout) + local dir = minetest.facedir_to_dir(node.param2) + local destination_pos = vector.add(pos, dir) + local destination_node_name = minetest.get_node(destination_pos).name + local destination_node_def = minetest.registered_nodes[destination_node_name] + + local insert_into_pipe = false + + -- Build a list of all the items that builder nodes want to use. + local filter_items = {} + if layout.builders ~= nil then + for _, node_image in pairs(layout.builders) do + filter_items[node_image.meta.inventory.main[1]:get_name()] = true + end + end + + -- Look through the inventories and find an item that's not on that list. + local source_node = nil + local source_index = nil + local source_stack = nil + for _, node_image in pairs(layout.inventories or {}) do + if type(node_image.meta.inventory.main) ~= "table" then + node_image.meta.inventory.main = {} + end + for index, item_stack in pairs(node_image.meta.inventory.main) do + if item_stack:get_count() > 0 and not filter_items[item_stack:get_name()] then + source_node = node_image + source_index = index + source_stack = item_stack + node_image.meta.inventory.main[index] = nil + break + end + end + if source_node then break end + end + + if source_node then + local meta = minetest.get_meta(source_node.pos) + local inv = meta:get_inventory() + + if insert_into_pipe then + local from_pos = vector.add(pos, vector.multiply(dir, 0.5)) + local start_pos = pos + inv:set_stack("main", source_index, nil) + pipeworks.tube_inject_item(from_pos, start_pos, dir, source_stack, player:get_player_name()) + minetest.sound_play("steam_puff", {gain=0.5, pos=pos}) + return true + end + end + + -- couldn't find an item to eject + return false +end + + +-- TODO: hoppers need enhancement to support this + +---- Hopper compatibility +--if minetest.get_modpath("hopper") and hopper ~= nil and hopper.add_container ~= nil then +-- hopper:add_container({ +-- {"top", "digtron:inventory", "main"}, +-- {"bottom", "digtron:inventory", "main"}, +-- {"side", "digtron:inventory", "main"}, +-- +-- {"top", "digtron:fuelstore", "fuel"}, +-- {"bottom", "digtron:fuelstore", "fuel"}, +-- {"side", "digtron:fuelstore", "fuel"}, +-- +-- {"top", "digtron:combined_storage", "main"}, +-- {"bottom", "digtron:combined_storage", "main"}, +-- {"side", "digtron:combined_storage", "fuel"}, +-- }) +--end + + +local ejector_def = { + description = S("Digtron Inventory Interface"), + _doc_items_longdesc = digtron.doc.inventory_ejector_longdesc, + _doc_items_usagehelp = digtron.doc.inventory_ejector_usagehelp, + _digtron_formspec = ejector_formspec, + groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 9, tubedevice = 1}, + tiles = {"digtron_plate.png", "digtron_plate.png", "digtron_plate.png", "digtron_plate.png", "digtron_plate.png^digtron_output.png", "digtron_plate.png^digtron_output_back.png"}, + drawtype = "nodebox", + sounds = digtron.metal_sounds, + paramtype = "light", + paramtype2 = "facedir", + is_ground_content = false, + node_box = { + type = "fixed", + fixed = { + {-0.5, -0.5, -0.5, 0.5, 0.5, 0.1875}, -- NodeBox1 + {-0.3125, -0.3125, 0.1875, 0.3125, 0.3125, 0.3125}, -- NodeBox2 + {-0.1875, -0.1875, 0.3125, 0.1875, 0.1875, 0.5}, -- NodeBox3 + } + }, + + tube = (function() if pipeworks_path then return { + insert_object = function(pos, node, stack, direction) + local meta = minetest.get_meta(pos) + local digtron_id = meta:get_string("digtron_id") + local inv = digtron.retrieve_inventory(digtron_id) + if inv == nil then + -- TODO error message + return + end + +-- return inv:add_item("main", stack) + end, + can_insert = function(pos, node, stack, direction) + local meta = minetest.get_meta(pos) + local digtron_id = meta:get_string("digtron_id") + local inv = digtron.retrieve_inventory(digtron_id) + if inv == nil then + -- TODO error message + return + end + +-- return inv:room_for_item("main", stack) + end, + input_inventory = "main", + connect_sides = {back = 1} + } end end)(), + + after_place_node = (function() if pipeworks_path then return pipeworks.after_place end end)(), + after_dig_node = (function() if pipeworks_path then return pipeworks.after_dig end end)() +}) + +minetest.register_node("digtron:pipeworks_interface", ejector_def) diff --git a/nodes/node_power_connector.lua b/nodes/node_power_connector.lua deleted file mode 100644 index 6763b69..0000000 --- a/nodes/node_power_connector.lua +++ /dev/null @@ -1,100 +0,0 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - -local size = 3/16 - -local max_dig_cost = math.max(digtron.config.dig_cost_cracky, digtron.config.dig_cost_crumbly, digtron.config.dig_cost_choppy, digtron.config.dig_cost_default) - -local get_formspec_string = function(current_val, current_max) - return "size[4.5,0.6]" .. - default.gui_bg .. - default.gui_bg_img .. - default.gui_slots .. - "field[0.2,0.3;1,1;value;;".. current_val .. "]" .. - "button[1,0;1,1;maximize;" .. S("Maximize\nPower") .."]" .. - "label[2,0;"..S("Maximum Power\nRequired: @1", current_max) .."]".. - "button[3.5,0;1,1;refresh;" .. S("Refresh\nMax") .."]" -end - -local connector_groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 8, technic_machine=1, technic_hv=1} -if not minetest.get_modpath("technic") then - -- Technic is not installed, hide this away. - connector_groups.not_in_creative_inventory = 1 -end - -minetest.register_node("digtron:power_connector", { - description = S("Digtron HV Power Connector"), - _doc_items_longdesc = digtron.doc.power_connector_longdesc, - _doc_items_usagehelp = digtron.doc.power_connector_usagehelp, - groups = connector_groups, - tiles = {"digtron_plate.png^digtron_power_connector_top.png^digtron_digger_yb_frame.png", "digtron_plate.png^digtron_digger_yb_frame.png", - "digtron_plate.png^digtron_digger_yb_frame.png^digtron_power_connector_side.png", "digtron_plate.png^digtron_digger_yb_frame.png^digtron_power_connector_side.png", - "digtron_plate.png^digtron_digger_yb_frame.png^digtron_power_connector_side.png", "digtron_plate.png^digtron_digger_yb_frame.png^digtron_power_connector_side.png", - }, - connect_sides = {"bottom", "top", "left", "right", "front", "back"}, - drawtype = "nodebox", - sounds = digtron.metal_sounds, - paramtype = "light", - paramtype2 = "facedir", - is_ground_content = false, - - connects_to = {"group:technic_hv_cable"}, - node_box = { - type = "connected", - fixed = { - {-0.5, -0.5, -0.5, 0.5, 0, 0.5}, -- Main body - {-0.1875, 0, -0.1875, 0.1875, 0.5, 0.1875}, -- post - {-0.3125, 0.0625, -0.3125, 0.3125, 0.1875, 0.3125}, -- vane - {-0.3125, 0.25, -0.3125, 0.3125, 0.375, 0.3125}, -- vane - }, - connect_front = {-size, -size, -0.5, size, size, size}, -- z- - connect_back = {-size, -size, size, size, size, 0.5 }, -- z+ - connect_left = {-0.5, -size, -size, size, size, size}, -- x- - connect_right = {-size, -size, -size, 0.5, size, size}, -- x+ - }, - - on_construct = function(pos) - local meta = minetest.get_meta(pos) - meta:set_string("formspec", get_formspec_string(0,0)) - end, - - technic_run = function(pos, node) - local meta = minetest.get_meta(pos) - local eu_input = meta:get_int("HV_EU_input") - local demand = meta:get_int("HV_EU_demand") - meta:set_string("infotext", S("Digtron Power @1/@2", eu_input, demand)) - end, - - on_receive_fields = function(pos, formname, fields, sender) - local layout = DigtronLayout.create(pos, sender) - local max_cost = 0 - if layout.builders ~= nil then - for _, node_image in pairs(layout.builders) do - max_cost = max_cost + (digtron.config.build_cost * (node_image.meta.fields.extrusion or 1)) - end - end - if layout.diggers ~= nil then - for _, node_image in pairs(layout.diggers) do - max_cost = max_cost + max_dig_cost - end - end - local current_max = max_cost * digtron.config.power_ratio - - local meta = minetest.get_meta(pos) - - if fields.maximize then - meta:set_int("HV_EU_demand", current_max) - elseif fields.value ~= nil then - local number = tonumber(fields.value) or 0 - local number = math.min(math.max(number, 0), current_max) - meta:set_int("HV_EU_demand", number) - end - - meta:set_string("formspec", get_formspec_string(meta:get_int("HV_EU_demand"), current_max)) - end, -}) - -if minetest.get_modpath("technic") then - technic.register_machine("HV", "digtron:power_connector", technic.receiver) -end \ No newline at end of file diff --git a/nodes/node_storage.lua b/nodes/node_storage.lua index 6237e66..a0b4ffe 100644 --- a/nodes/node_storage.lua +++ b/nodes/node_storage.lua @@ -1,24 +1,15 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") +local S = digtron.S -local pipeworks_path = minetest.get_modpath("pipeworks") +--local pipeworks_path = minetest.get_modpath("pipeworks") -local inventory_formspec_string = - "size[8,9.3]" .. - default.gui_bg .. - default.gui_bg_img .. - default.gui_slots .. +local get_inventory_formspec = function(pos, player_name) + return "size[8,9.3]" .. "label[0,0;" .. S("Inventory items") .. "]" .. - "list[current_name;main;0,0.6;8,4;]" .. + "list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;0,0.6;8,4;]" .. "list[current_player;main;0,5.15;8,1;]" .. + "listring[]" .. "list[current_player;main;0,6.38;8,3;8]" .. - "listring[current_name;main]" .. - "listring[current_player;main]" .. default.get_hotbar_bg(0,5.15) - -local inventory_formspec = function(pos, meta) - return inventory_formspec_string end -- Storage buffer. Builder nodes draw from this inventory and digger nodes deposit into it. @@ -27,10 +18,8 @@ minetest.register_node("digtron:inventory", { description = S("Digtron Inventory Storage"), _doc_items_longdesc = digtron.doc.inventory_longdesc, _doc_items_usagehelp = digtron.doc.inventory_usagehelp, - _digtron_formspec = inventory_formspec, groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 2, tubedevice = 1, tubedevice_receiver = 1}, - drop = "digtron:inventory", - sounds = digtron.metal_sounds, + sounds = default.node_sound_metal_defaults(), paramtype2= "facedir", drawtype = "nodebox", node_box = { @@ -52,66 +41,78 @@ minetest.register_node("digtron:inventory", { on_construct = function(pos) local meta = minetest.get_meta(pos) - meta:set_string("formspec", inventory_formspec(pos, meta)) local inv = meta:get_inventory() inv:set_size("main", 8*4) end, can_dig = function(pos,player) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:is_empty("main") + local inv = minetest.get_inventory({type="node", pos=pos}) + return inv:is_empty("main") and digtron.can_dig(pos,player) end, + on_blast = digtron.on_blast, - -- Pipeworks compatibility - ---------------------------------------------------------------- + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + local returnstack, success = digtron.on_rightclick(pos, node, clicker, itemstack, pointed_thing) + if returnstack then + return returnstack, success + end - tube = (function() if pipeworks_path then return { - insert_object = function(pos, node, stack, direction) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:add_item("main", stack) - end, - can_insert = function(pos, node, stack, direction) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:room_for_item("main", stack) - end, - input_inventory = "main", - connect_sides = {left = 1, right = 1, back = 1, front = 1, bottom = 1, top = 1} - } end end)(), - - after_place_node = (function() if pipeworks_path then return pipeworks.after_place end end)(), - after_dig_node = (function() if pipeworks_path then return pipeworks.after_dig end end)() + if clicker == nil then return end + + local meta = minetest.get_meta(pos) + local digtron_id = meta:get("digtron_id") + local player_name = clicker:get_player_name() + if digtron_id == nil then + minetest.show_formspec(player_name, + "digtron_inventory:"..minetest.pos_to_string(pos)..":"..player_name, + get_inventory_formspec(pos, player_name)) + else + minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=player_name}) + minetest.chat_send_player(clicker:get_player_name(), S("This Digtron is active, interact with it via the controller node.")) + end + end, + + +-- -- Pipeworks compatibility +-- ---------------------------------------------------------------- +-- +-- tube = (function() if pipeworks_path then return { +-- insert_object = function(pos, node, stack, direction) +-- local meta = minetest.get_meta(pos) +-- local inv = meta:get_inventory() +-- return inv:add_item("main", stack) +-- end, +-- can_insert = function(pos, node, stack, direction) +-- local meta = minetest.get_meta(pos) +-- local inv = meta:get_inventory() +-- return inv:room_for_item("main", stack) +-- end, +-- input_inventory = "main", +-- connect_sides = {left = 1, right = 1, back = 1, front = 1, bottom = 1, top = 1} +-- } end end)(), +-- +-- after_place_node = (function() if pipeworks_path then return pipeworks.after_place end end)(), +-- after_dig_node = (function() if pipeworks_path then return pipeworks.after_dig end end)() }) -local fuelstore_formspec_string = - "size[8,9.3]" .. - default.gui_bg .. - default.gui_bg_img .. - default.gui_slots .. +local get_fuelstore_formspec = function(pos, player_name) + return "size[8,9.3]" .. "label[0,0;" .. S("Fuel items") .. "]" .. - "list[current_name;fuel;0,0.6;8,4;]" .. + "list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";fuel;0,0.6;8,4;]" .. "list[current_player;main;0,5.15;8,1;]" .. + "listring[]" .. "list[current_player;main;0,6.38;8,3;8]" .. - "listring[current_name;fuel]" .. - "listring[current_player;main]" .. default.get_hotbar_bg(0,5.15) - -local fuelstore_formspec = function(pos, meta) - return fuelstore_formspec_string end - + -- Fuel storage. Controller node draws fuel from here. -- Note that fuel stores are digtron group 5. minetest.register_node("digtron:fuelstore", { description = S("Digtron Fuel Storage"), _doc_items_longdesc = digtron.doc.fuelstore_longdesc, _doc_items_usagehelp = digtron.doc.fuelstore_usagehelp, - _digtron_formspec = fuelstore_formspec, groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 5, tubedevice = 1, tubedevice_receiver = 1}, - drop = "digtron:fuelstore", - sounds = digtron.metal_sounds, + sounds = default.node_sound_metal_defaults(), paramtype2= "facedir", drawtype = "nodebox", node_box = { @@ -133,7 +134,6 @@ minetest.register_node("digtron:fuelstore", { on_construct = function(pos) local meta = minetest.get_meta(pos) - meta:set_string("formspec", fuelstore_formspec(pos, meta)) local inv = meta:get_inventory() inv:set_size("fuel", 8*4) end, @@ -151,56 +151,72 @@ minetest.register_node("digtron:fuelstore", { end, can_dig = function(pos,player) + local inv = minetest.get_inventory({type="node", pos=pos}) + return inv:is_empty("fuel") and digtron.can_dig(pos,player) + end, + on_blast = digtron.on_blast, + + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + local returnstack, success = digtron.on_rightclick(pos, node, clicker, itemstack, pointed_thing) + if returnstack then + return returnstack, success + end + + if clicker == nil then return end + local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:is_empty("fuel") + local digtron_id = meta:get("digtron_id") + local player_name = clicker:get_player_name() + if digtron_id == nil then + minetest.show_formspec(player_name, + "digtron_fuelstore:"..minetest.pos_to_string(pos)..":"..player_name, + get_fuelstore_formspec(pos, player_name)) + else + minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=player_name}) + minetest.chat_send_player(clicker:get_player_name(), S("This Digtron is active, interact with it via the controller node.")) + end end, - - -- Pipeworks compatibility - ---------------------------------------------------------------- - tube = (function() if pipeworks_path then return { - insert_object = function(pos, node, stack, direction) - if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:add_item("fuel", stack) - end - return stack - end, - can_insert = function(pos, node, stack, direction) - if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:room_for_item("fuel", stack) - end - return false - end, - input_inventory = "fuel", - connect_sides = {left = 1, right = 1, back = 1, front = 1, bottom = 1, top = 1} - } end end)(), - - after_place_node = (function() if pipeworks_path then return pipeworks.after_place end end)(), - after_dig_node = (function() if pipeworks_path then return pipeworks.after_dig end end)() +-- +-- -- Pipeworks compatibility +-- ---------------------------------------------------------------- +-- +-- tube = (function() if pipeworks_path then return { +-- insert_object = function(pos, node, stack, direction) +-- if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then +-- local meta = minetest.get_meta(pos) +-- local inv = meta:get_inventory() +-- return inv:add_item("fuel", stack) +-- end +-- return stack +-- end, +-- can_insert = function(pos, node, stack, direction) +-- if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then +-- local meta = minetest.get_meta(pos) +-- local inv = meta:get_inventory() +-- return inv:room_for_item("fuel", stack) +-- end +-- return false +-- end, +-- input_inventory = "fuel", +-- connect_sides = {left = 1, right = 1, back = 1, front = 1, bottom = 1, top = 1} +-- } end end)(), +-- +-- after_place_node = (function() if pipeworks_path then return pipeworks.after_place end end)(), +-- after_dig_node = (function() if pipeworks_path then return pipeworks.after_dig end end)() }) - -local combined_storage_formspec_string = - "size[8,9.9]" .. - default.gui_bg .. - default.gui_bg_img .. - default.gui_slots .. +-- +local get_combined_formspec = function(pos, player_name) + return "size[8,9.9]" .. "label[0,0;" .. S("Inventory items") .. "]" .. - "list[current_name;main;0,0.6;8,3;]" .. + "list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;0,0.6;8,3;]" .. "label[0,3.5;" .. S("Fuel items") .. "]" .. - "list[current_name;fuel;0,4.1;8,1;]" .. + "list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";fuel;0,4.1;8,1;]" .. "list[current_player;main;0,5.75;8,1;]" .. "list[current_player;main;0,6.98;8,3;8]" .. - "listring[current_name;main]" .. + "listring[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main]" .. "listring[current_player;main]" .. default.get_hotbar_bg(0,5.75) - -local combined_storage_formspec = function(pos, meta) - return combined_storage_formspec_string end -- Combined storage. Group 6 has both an inventory and a fuel store @@ -208,10 +224,8 @@ minetest.register_node("digtron:combined_storage", { description = S("Digtron Combined Storage"), _doc_items_longdesc = digtron.doc.combined_storage_longdesc, _doc_items_usagehelp = digtron.doc.combined_storage_usagehelp, - _digtron_formspec = combined_storage_formspec, groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 6, tubedevice = 1, tubedevice_receiver = 1}, - drop = "digtron:combined_storage", - sounds = digtron.metal_sounds, + sounds = default.node_sound_metal_defaults(), paramtype2= "facedir", drawtype = "nodebox", node_box = { @@ -231,14 +245,14 @@ minetest.register_node("digtron:combined_storage", { }, on_construct = function(pos) local meta = minetest.get_meta(pos) - meta:set_string("formspec", combined_storage_formspec(pos, meta)) local inv = meta:get_inventory() inv:set_size("main", 8*3) inv:set_size("fuel", 8*1) end, - -- Only allow fuel items to be placed in fuel + allow_metadata_inventory_put = function(pos, listname, index, stack, player) + -- Only allow fuel items to be placed in fuel if listname == "fuel" then if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then return stack:get_count() @@ -264,51 +278,72 @@ minetest.register_node("digtron:combined_storage", { end, can_dig = function(pos,player) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:is_empty("fuel") and inv:is_empty("main") + local inv = minetest.get_inventory({type="node", pos=pos}) + return inv:is_empty("fuel") and inv:is_empty("main") and digtron.can_dig(pos,player) end, - - -- Pipeworks compatibility - ---------------------------------------------------------------- - tube = (function() if pipeworks_path then return { - insert_object = function(pos, node, stack, direction) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 and direction.y == 1 then - return inv:add_item("fuel", stack) - end - return inv:add_item("main", stack) - end, - can_insert = function(pos, node, stack, direction) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 and direction.y == 1 then - return inv:room_for_item("fuel", stack) - end - return inv:room_for_item("main", stack) - end, - input_inventory = "main", - connect_sides = {left = 1, right = 1, back = 1, front = 1, bottom = 1, top = 1} - } end end)(), + on_blast = digtron.on_blast, - after_place_node = (function() if pipeworks_path then return pipeworks.after_place end end)(), - after_dig_node = (function() if pipeworks_path then return pipeworks.after_dig end end)() -}) + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + local returnstack, success = digtron.on_rightclick(pos, node, clicker, itemstack, pointed_thing) + if returnstack then + return returnstack, success + end --- Hopper compatibility -if minetest.get_modpath("hopper") and hopper ~= nil and hopper.add_container ~= nil then - hopper:add_container({ - {"top", "digtron:inventory", "main"}, - {"bottom", "digtron:inventory", "main"}, - {"side", "digtron:inventory", "main"}, + if clicker == nil then return end - {"top", "digtron:fuelstore", "fuel"}, - {"bottom", "digtron:fuelstore", "fuel"}, - {"side", "digtron:fuelstore", "fuel"}, - - {"top", "digtron:combined_storage", "main"}, - {"bottom", "digtron:combined_storage", "main"}, - {"side", "digtron:combined_storage", "fuel"}, - }) -end + local meta = minetest.get_meta(pos) + local digtron_id = meta:get("digtron_id") + local player_name = clicker:get_player_name() + if digtron_id == nil then + minetest.show_formspec(player_name, + "digtron_combined_storage:"..minetest.pos_to_string(pos)..":"..player_name, + get_combined_formspec(pos, player_name)) + else + minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=player_name}) + minetest.chat_send_player(clicker:get_player_name(), S("This Digtron is active, interact with it via the controller node.")) + end + end, +-- +-- -- Pipeworks compatibility +-- ---------------------------------------------------------------- +-- tube = (function() if pipeworks_path then return { +-- insert_object = function(pos, node, stack, direction) +-- local meta = minetest.get_meta(pos) +-- local inv = meta:get_inventory() +-- if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 and direction.y == 1 then +-- return inv:add_item("fuel", stack) +-- end +-- return inv:add_item("main", stack) +-- end, +-- can_insert = function(pos, node, stack, direction) +-- local meta = minetest.get_meta(pos) +-- local inv = meta:get_inventory() +-- if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 and direction.y == 1 then +-- return inv:room_for_item("fuel", stack) +-- end +-- return inv:room_for_item("main", stack) +-- end, +-- input_inventory = "main", +-- connect_sides = {left = 1, right = 1, back = 1, front = 1, bottom = 1, top = 1} +-- } end end)(), +-- +-- after_place_node = (function() if pipeworks_path then return pipeworks.after_place end end)(), +-- after_dig_node = (function() if pipeworks_path then return pipeworks.after_dig end end)() +}) +-- +---- Hopper compatibility +--if minetest.get_modpath("hopper") and hopper ~= nil and hopper.add_container ~= nil then +-- hopper:add_container({ +-- {"top", "digtron:inventory", "main"}, +-- {"bottom", "digtron:inventory", "main"}, +-- {"side", "digtron:inventory", "main"}, +-- +-- {"top", "digtron:fuelstore", "fuel"}, +-- {"bottom", "digtron:fuelstore", "fuel"}, +-- {"side", "digtron:fuelstore", "fuel"}, +-- +-- {"top", "digtron:combined_storage", "main"}, +-- {"bottom", "digtron:combined_storage", "main"}, +-- {"side", "digtron:combined_storage", "fuel"}, +-- }) +--end diff --git a/nodes/recipes.lua b/nodes/recipes.lua index 313c4a9..d876c7a 100644 --- a/nodes/recipes.lua +++ b/nodes/recipes.lua @@ -1,6 +1,4 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") +local S = digtron.S minetest.register_craftitem("digtron:digtron_core", { description = S("Digtron Core"), @@ -12,323 +10,233 @@ minetest.register_craftitem("digtron:digtron_core", { minetest.register_craft({ output = "digtron:digtron_core", recipe = { - {"","default:steel_ingot",""}, - {"default:steel_ingot","default:mese_crystal_fragment","default:steel_ingot"}, - {"","default:steel_ingot",""} - } + {"","default:steel_ingot",""}, + {"default:steel_ingot","default:mese_crystal_fragment","default:steel_ingot"}, + {"","default:steel_ingot",""} + } }) minetest.register_craft({ output = "digtron:controller", recipe = { - {"","default:mese_crystal",""}, - {"default:mese_crystal","digtron:digtron_core","default:mese_crystal"}, - {"","default:mese_crystal",""} - } -}) - -minetest.register_craft({ - output = "digtron:auto_controller", - recipe = { - {"default:mese_crystal","default:mese_crystal","default:mese_crystal"}, - {"default:mese_crystal","digtron:digtron_core","default:mese_crystal"}, - {"default:mese_crystal","default:mese_crystal","default:mese_crystal"} - } + {"","default:mese_crystal",""}, + {"default:mese_crystal","digtron:digtron_core","default:mese_crystal"}, + {"","default:mese_crystal",""} + } }) minetest.register_craft({ output = "digtron:builder", recipe = { - {"","default:mese_crystal_fragment",""}, - {"default:mese_crystal_fragment","digtron:digtron_core","default:mese_crystal_fragment"}, - {"","default:mese_crystal_fragment",""} - } + {"","default:mese_crystal_fragment",""}, + {"default:mese_crystal_fragment","digtron:digtron_core","default:mese_crystal_fragment"}, + {"","default:mese_crystal_fragment",""} + } }) minetest.register_craft({ output = "digtron:light", recipe = { - {"","default:torch",""}, - {"","digtron:digtron_core",""}, - {"","",""} - } + {"","default:torch",""}, + {"","digtron:digtron_core",""}, + {"","",""} + } }) minetest.register_craft({ - output = "digtron:digger", + output = "digtron:digger_static", recipe = { - {"","default:diamond",""}, - {"default:diamond","digtron:digtron_core","default:diamond"}, - {"","default:diamond",""} - } + {"","default:diamond",""}, + {"default:diamond","digtron:digtron_core","default:diamond"}, + {"","default:diamond",""} + } }) minetest.register_craft({ - output = "digtron:soft_digger", + output = "digtron:soft_digger_static", recipe = { - {"","default:steel_ingot",""}, - {"default:steel_ingot","digtron:digtron_core","default:steel_ingot"}, - {"","default:steel_ingot",""} - } + {"","default:steel_ingot",""}, + {"default:steel_ingot","digtron:digtron_core","default:steel_ingot"}, + {"","default:steel_ingot",""} + } }) minetest.register_craft({ output = "digtron:inventory", recipe = { - {"","default:chest",""}, - {"","digtron:digtron_core",""}, - {"","",""} - } + {"","default:chest",""}, + {"","digtron:digtron_core",""}, + {"","",""} + } }) minetest.register_craft({ output = "digtron:fuelstore", recipe = { - {"","default:furnace",""}, - {"","digtron:digtron_core",""}, - {"","",""} - } -}) - -if minetest.get_modpath("technic") then - -- no need for this recipe if technic is not installed, avoid cluttering crafting guides - minetest.register_craft({ - output = "digtron:battery_holder", - recipe = { - {"","default:chest",""}, - {"","digtron:digtron_core",""}, - {"","default:steel_ingot",""} - } - }) - - minetest.register_craft({ - output = "digtron:power_connector", - recipe = { - {"","technic:hv_cable",""}, - {"technic:hv_cable","digtron:digtron_core","technic:hv_cable"}, - {"","technic:hv_cable",""} - } - }) -end + {"","default:furnace",""}, + {"","digtron:digtron_core",""}, + {"","",""} + } +}) + +--if minetest.get_modpath("technic") then +-- -- no need for this recipe if technic is not installed, avoid cluttering crafting guides +-- minetest.register_craft({ +-- output = "digtron:battery_holder", +-- recipe = { +-- {"","default:chest",""}, +-- {"","digtron:digtron_core",""}, +-- {"","default:steel_ingot",""} +-- } +-- }) +-- +-- minetest.register_craft({ +-- output = "digtron:power_connector", +-- recipe = { +-- {"","technic:hv_cable",""}, +-- {"technic:hv_cable","digtron:digtron_core","technic:hv_cable"}, +-- {"","technic:hv_cable",""} +-- } +-- }) +--end minetest.register_craft({ output = "digtron:combined_storage", recipe = { - {"","default:furnace",""}, - {"","digtron:digtron_core",""}, - {"","default:chest",""} - } -}) - -minetest.register_craft({ - output = "digtron:pusher", - recipe = { - {"","default:coal_lump",""}, - {"default:coal_lump","digtron:digtron_core","default:coal_lump"}, - {"","default:coal_lump",""} - } -}) - -minetest.register_craft({ - output = "digtron:axle", - recipe = { - {"default:coal_lump","default:coal_lump","default:coal_lump"}, - {"default:coal_lump","digtron:digtron_core","default:coal_lump"}, - {"default:coal_lump","default:coal_lump","default:coal_lump"} - } -}) - -minetest.register_craft({ - output = "digtron:empty_crate", - recipe = { - {"","default:chest",""}, - {"","digtron:digtron_core",""}, - {"","default:mese_crystal",""} - } -}) - -minetest.register_craft({ - output = "digtron:empty_locked_crate", - type = "shapeless", - recipe = {"default:steel_ingot", "digtron:empty_crate"}, -}) - -minetest.register_craft({ - output = "digtron:empty_crate", - type = "shapeless", - recipe = {"digtron:empty_locked_crate"}, + {"","default:furnace",""}, + {"","digtron:digtron_core",""}, + {"","default:chest",""} + } }) minetest.register_craft({ output = "digtron:duplicator", recipe = { - {"default:mese_crystal","default:mese_crystal","default:mese_crystal"}, - {"default:chest","digtron:digtron_core","default:chest"}, - {"default:mese_crystal","default:mese_crystal","default:mese_crystal"} - } + {"default:mese_crystal","default:mese_crystal","default:mese_crystal"}, + {"default:chest","digtron:digtron_core","default:chest"}, + {"default:mese_crystal","default:mese_crystal","default:mese_crystal"} + } }) -minetest.register_craft({ - output = "digtron:inventory_ejector", - recipe = { - {"default:steel_ingot","default:steel_ingot","default:steel_ingot"}, - {"","digtron:digtron_core",""}, - {"","default:steel_ingot",""} - } -}) +--minetest.register_craft({ +-- output = "digtron:inventory_ejector", +-- recipe = { +-- {"default:steel_ingot","default:steel_ingot","default:steel_ingot"}, +-- {"","digtron:digtron_core",""}, +-- {"","default:steel_ingot",""} +-- } +--}) -- Structural minetest.register_craft({ output = "digtron:structure", recipe = { - {"group:stick","","group:stick"}, - {"","digtron:digtron_core",""}, - {"group:stick","","group:stick"} - } + {"group:stick","","group:stick"}, + {"","digtron:digtron_core",""}, + {"group:stick","","group:stick"} + } }) minetest.register_craft({ output = "digtron:panel", recipe = { - {"","",""}, - {"","digtron:digtron_core",""}, - {"","default:steel_ingot",""} - } + {"","",""}, + {"","digtron:digtron_core",""}, + {"","default:steel_ingot",""} + } }) minetest.register_craft({ output = "digtron:edge_panel", recipe = { - {"","",""}, - {"","digtron:digtron_core","default:steel_ingot"}, - {"","default:steel_ingot",""} - } + {"","",""}, + {"","digtron:digtron_core","default:steel_ingot"}, + {"","default:steel_ingot",""} + } }) minetest.register_craft({ output = "digtron:corner_panel", recipe = { - {"","",""}, - {"","digtron:digtron_core","default:steel_ingot"}, - {"","default:steel_ingot","default:steel_ingot"} - } + {"","",""}, + {"","digtron:digtron_core","default:steel_ingot"}, + {"","default:steel_ingot","default:steel_ingot"} + } }) -- For swapping digger types -minetest.register_craft({ - output = "digtron:digger", - recipe = { - {"digtron:intermittent_digger"}, - } -}) -minetest.register_craft({ - output = "digtron:intermittent_digger", - recipe = { - {"digtron:digger"}, - } -}) -minetest.register_craft({ - output = "digtron:soft_digger", - recipe = { - {"digtron:intermittent_soft_digger"}, - } -}) -minetest.register_craft({ - output = "digtron:intermittent_soft_digger", - recipe = { - {"digtron:soft_digger"}, - } -}) minetest.register_craft({ - output = "digtron:dual_soft_digger", + output = "digtron:dual_soft_digger_static", type = "shapeless", - recipe = {"digtron:soft_digger", "digtron:soft_digger"}, + recipe = {"digtron:soft_digger_static", "digtron:soft_digger_static"}, }) + minetest.register_craft({ - output = "digtron:dual_digger", + output = "digtron:dual_digger_static", type = "shapeless", - recipe = {"digtron:digger", "digtron:digger"}, + recipe = {"digtron:digger_static", "digtron:digger_static"}, }) + minetest.register_craft({ - output = "digtron:soft_digger 2", - recipe = { - {"digtron:dual_soft_digger"}, - } + output = "digtron:soft_digger_static 2", + type = "shapeless", + recipe = {"digtron:dual_soft_digger_static"}, }) + minetest.register_craft({ - output = "digtron:digger 2", - recipe = { - {"digtron:dual_digger"}, - } + output = "digtron:digger_static 2", + type = "shapeless", + recipe = {"digtron:dual_digger_static"}, }) -- And some recycling reactions to get digtron cores out of the "cheap" parts: minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:structure"}, - } -}) -minetest.register_craft({ - output = "digtron:digtron_core", - recipe = { - {"digtron:panel"}, - } -}) -minetest.register_craft({ - output = "digtron:digtron_core", - recipe = { - {"digtron:corner_panel"}, - } + type = "shapeless", + recipe = {"digtron:structure"}, }) + minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:edge_panel"}, - } + type = "shapeless", + recipe = {"digtron:panel"}, }) minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:inventory"}, - } + type = "shapeless", + recipe = {"digtron:corner_panel"}, }) minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:fuelstore"}, - } + type = "shapeless", + recipe = {"digtron:edge_panel"}, }) minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:combined_storage"}, - } + type = "shapeless", + recipe = {"digtron:inventory"}, }) minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:light"}, - } + type = "shapeless", + recipe = {"digtron:fuelstore"}, }) minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:pusher"}, - } + type = "shapeless", + recipe = {"digtron:combined_storage"}, }) minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:axle"}, - } + type = "shapeless", + recipe = {"digtron:light"}, }) \ No newline at end of file diff --git a/screenshot.png b/screenshot.png deleted file mode 100644 index 1226a6b..0000000 Binary files a/screenshot.png and /dev/null differ diff --git a/sounds/buzzer.ogg b/sounds/digtron_buzzer.ogg similarity index 100% rename from sounds/buzzer.ogg rename to sounds/digtron_buzzer.ogg diff --git a/sounds/construction.ogg b/sounds/digtron_construction.ogg similarity index 100% rename from sounds/construction.ogg rename to sounds/digtron_construction.ogg diff --git a/sounds/dingding.ogg b/sounds/digtron_dingding.ogg similarity index 100% rename from sounds/dingding.ogg rename to sounds/digtron_dingding.ogg diff --git a/sounds/digtron_engine.ogg b/sounds/digtron_engine.ogg new file mode 100644 index 0000000..996cc8c Binary files /dev/null and b/sounds/digtron_engine.ogg differ diff --git a/sounds/digtron_error.ogg b/sounds/digtron_error.ogg new file mode 100644 index 0000000..d1ae7c9 Binary files /dev/null and b/sounds/digtron_error.ogg differ diff --git a/sounds/honk.ogg b/sounds/digtron_honk.ogg similarity index 100% rename from sounds/honk.ogg rename to sounds/digtron_honk.ogg diff --git a/sounds/digtron_hydraulic.ogg b/sounds/digtron_hydraulic.ogg new file mode 100644 index 0000000..995d06a Binary files /dev/null and b/sounds/digtron_hydraulic.ogg differ diff --git a/sounds/machine1.ogg b/sounds/digtron_machine_assemble.ogg similarity index 100% rename from sounds/machine1.ogg rename to sounds/digtron_machine_assemble.ogg diff --git a/sounds/machine2.ogg b/sounds/digtron_machine_disassemble.ogg similarity index 100% rename from sounds/machine2.ogg rename to sounds/digtron_machine_disassemble.ogg diff --git a/sounds/sploosh.ogg b/sounds/digtron_sploosh.ogg similarity index 100% rename from sounds/sploosh.ogg rename to sounds/digtron_sploosh.ogg diff --git a/sounds/squeal.ogg b/sounds/digtron_squeal.ogg similarity index 100% rename from sounds/squeal.ogg rename to sounds/digtron_squeal.ogg diff --git a/sounds/steam_puff.ogg b/sounds/digtron_steam_puff.ogg similarity index 100% rename from sounds/steam_puff.ogg rename to sounds/digtron_steam_puff.ogg diff --git a/sounds/truck.ogg b/sounds/digtron_truck.ogg similarity index 100% rename from sounds/truck.ogg rename to sounds/digtron_truck.ogg diff --git a/sounds/whirr.ogg b/sounds/digtron_whirr.ogg similarity index 100% rename from sounds/whirr.ogg rename to sounds/digtron_whirr.ogg diff --git a/sounds/woopwoopwoop.ogg b/sounds/digtron_woopwoopwoop.ogg similarity index 100% rename from sounds/woopwoopwoop.ogg rename to sounds/digtron_woopwoopwoop.ogg diff --git a/sounds/license.txt b/sounds/license.txt index 1de8114..fd4df56 100644 --- a/sounds/license.txt +++ b/sounds/license.txt @@ -1,17 +1,18 @@ The sounds in this folder were sampled from source .wavs from Freesound.org. Specifically: -buzzer.ogg - https://freesound.org/people/hypocore/sounds/164090/ - public domain via CC 1.0 by hypocore -construction.ogg - https://www.freesound.org/people/mediapetros/sounds/109117/ - under the CC BY 3.0 license by mediapetros -dingding.ogg - https://www.freesound.org/people/JohnsonBrandEditing/sounds/173932/ public domain via CC 1.0 by JohnsonBrandEditing -honk.ogg - https://freesound.org/people/bigmanjoe/sounds/349922/ public domain via CC 1.0 by bigmanjoe -machine1.ogg and machine2.ogg - https://www.freesound.org/people/nuckan/sounds/212941/ public domain via CC 1.0 by nuckan -sploosh.ogg - https://www.freesound.org/people/mr_marcello/sounds/257609/ public domain via CC 1.0 by mr_marcello -squeal.ogg - https://www.freesound.org/people/RutgerMuller/sounds/104026/ public domain via CC 1.0 by RutgerMuller -truck.ogg - https://www.freesound.org/people/jberkuta14/sounds/134898/ public domain via CC 1.0 by jberkuta14 -whirr.ogg - https://www.freesound.org/people/daveincamas/sounds/25034/ - under the CC BY 3.0 license by daveincamas -woopwoopwoop.ogg - https://www.freesound.org/people/gregconquest/sounds/188012/ public domain via CC 1.0 by gregconquest -steam_puff.ogg - https://freesound.org/people/Aiwha/sounds/250703/ under the CC BY 3.0 license by Aiwha - +digtron_buzzer.ogg - https://freesound.org/people/hypocore/sounds/164090/ - public domain via CC 1.0 by hypocore +digtron_construction.ogg - https://www.freesound.org/people/mediapetros/sounds/109117/ - under the CC BY 3.0 license by mediapetros +digtron_dingding.ogg - https://www.freesound.org/people/JohnsonBrandEditing/sounds/173932/ public domain via CC 1.0 by JohnsonBrandEditing +digtron_honk.ogg - https://freesound.org/people/bigmanjoe/sounds/349922/ public domain via CC 1.0 by bigmanjoe +digtron_machine_assemble.ogg and digtron_machine_disassemble.ogg - https://www.freesound.org/people/nuckan/sounds/212941/ public domain via CC 1.0 by nuckan +digtron_sploosh.ogg - https://www.freesound.org/people/mr_marcello/sounds/257609/ public domain via CC 1.0 by mr_marcello +digtron_squeal.ogg - https://www.freesound.org/people/RutgerMuller/sounds/104026/ public domain via CC 1.0 by RutgerMuller +digtron_truck.ogg - https://www.freesound.org/people/jberkuta14/sounds/134898/ public domain via CC 1.0 by jberkuta14 +digtron_whirr.ogg - https://www.freesound.org/people/daveincamas/sounds/25034/ - under the CC BY 3.0 license by daveincamas +digtron_woopwoopwoop.ogg - https://www.freesound.org/people/gregconquest/sounds/188012/ public domain via CC 1.0 by gregconquest +digtron_steam_puff.ogg - https://freesound.org/people/Aiwha/sounds/250703/ under the CC BY 3.0 license by Aiwha +digtron_hydraulic.ogg - https://freesound.org/people/jesabat/sounds/119745/ under the CC BY 3.0 license by jesabat +digtron_engine.ogg - https://freesound.org/people/NoiseCollector/sounds/55093/ under the CC BY 3.0 license by NoiseCollector Creative Commons Attribution 3.0 license: diff --git a/textures/digtron_axel_side.png b/textures/digtron_axel_side.png index aaf91b3..81694fc 100644 Binary files a/textures/digtron_axel_side.png and b/textures/digtron_axel_side.png differ diff --git a/textures/digtron_control.png b/textures/digtron_control.png index 2701b21..99d489d 100644 Binary files a/textures/digtron_control.png and b/textures/digtron_control.png differ diff --git a/textures/digtron_core.png b/textures/digtron_core.png index 2e63f80..cdd6016 100644 Binary files a/textures/digtron_core.png and b/textures/digtron_core.png differ diff --git a/textures/digtron_digger_yb_frame.png b/textures/digtron_digger_yb_frame.png index 2a849f0..ad67626 100644 Binary files a/textures/digtron_digger_yb_frame.png and b/textures/digtron_digger_yb_frame.png differ diff --git a/textures/digtron_drill_head_animated.png b/textures/digtron_drill_head_animated.png new file mode 100644 index 0000000..83be605 Binary files /dev/null and b/textures/digtron_drill_head_animated.png differ diff --git a/textures/digtron_marker.png b/textures/digtron_marker.png index cbd42d5..41bc193 100644 Binary files a/textures/digtron_marker.png and b/textures/digtron_marker.png differ diff --git a/textures/digtron_motor.png b/textures/digtron_motor.png index d3c3f78..e1f7205 100644 Binary files a/textures/digtron_motor.png and b/textures/digtron_motor.png differ diff --git a/textures/digtron_output.png b/textures/digtron_output.png index a34f7f5..4e5e9a7 100644 Binary files a/textures/digtron_output.png and b/textures/digtron_output.png differ diff --git a/upgrades.lua b/upgrades.lua deleted file mode 100644 index dc19053..0000000 --- a/upgrades.lua +++ /dev/null @@ -1,59 +0,0 @@ --- re-applies the "_digtron_formspec" property from all digtron node defs to the digtron node's metadata. -minetest.register_lbm({ - name = "digtron:generic_formspec_sanitizer", - nodenames = {"group:digtron"}, - action = function(pos, node) - local node_def = minetest.registered_nodes[node.name] - if node_def._digtron_formspec then - local meta = minetest.get_meta(pos) - meta:set_string("formspec", node_def._digtron_formspec(pos, meta)) - end - end -}) - -minetest.register_lbm({ - name = "digtron:sand_digger_upgrade", - nodenames = {"digtron:sand_digger"}, - action = function(pos, node) - local meta = minetest.get_meta(pos) - local offset = meta:get_string("offset") - local period = meta:get_string("period") - minetest.set_node(pos, {name = "digtron:soft_digger", - param2 = node.param2}) - meta:set_string("offset", offset) - meta:set_string("period", period) - end -}) - -minetest.register_lbm({ - name = "digtron:fuelstore_upgrade", - nodenames = {"digtron:fuelstore"}, - action = function(pos, node) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - local list = inv:get_list("main") - inv:set_list("main", {}) - inv:set_list("fuel", list) - end -}) - -minetest.register_lbm({ - name = "digtron:autocontroller_lateral_upgrade", - nodenames = {"digtron:auto_controller"}, - action = function(pos, node) - local meta = minetest.get_meta(pos) - local cycles = meta:get_int("offset") - meta:set_int("cycles", cycles) - meta:set_int("offset", 0) - meta:set_int("slope", 0) - end -}) - -minetest.register_lbm({ - name = "digtron:builder_extrusion_upgrade", - nodenames = {"digtron:builder"}, - action = function(pos, node) - local meta = minetest.get_meta(pos) - meta:set_int("extrusion", 1) - end -}) \ No newline at end of file diff --git a/util.lua b/util.lua deleted file mode 100644 index 327c7e7..0000000 --- a/util.lua +++ /dev/null @@ -1,435 +0,0 @@ --- A random assortment of methods used in various places in this mod. - -dofile( minetest.get_modpath( "digtron" ) .. "/util_item_place_node.lua" ) -- separated out to avoid potential for license complexity -dofile( minetest.get_modpath( "digtron" ) .. "/util_execute_cycle.lua" ) -- separated out simply for tidiness, there's some big code in there - -local node_inventory_table = {type="node"} -- a reusable parameter for get_inventory calls, set the pos parameter before using. - --- Apparently node_sound_metal_defaults is a newer thing, I ran into games using an older version of the default mod without it. -if default.node_sound_metal_defaults ~= nil then - digtron.metal_sounds = default.node_sound_metal_defaults() -else - digtron.metal_sounds = default.node_sound_stone_defaults() -end - - -digtron.find_new_pos = function(pos, facing) - -- finds the point one node "forward", based on facing - local dir = minetest.facedir_to_dir(facing) - return vector.add(pos, dir) -end - -local facedir_to_down_dir_table = { - [0]={x=0, y=-1, z=0}, - {x=0, y=0, z=-1}, - {x=0, y=0, z=1}, - {x=-1, y=0, z=0}, - {x=1, y=0, z=0}, - {x=0, y=1, z=0} -} -digtron.facedir_to_down_dir = function(facing) - return facedir_to_down_dir_table[math.floor(facing/4)] -end - -digtron.find_new_pos_downward = function(pos, facing) - return vector.add(pos, digtron.facedir_to_down_dir(facing)) -end - -digtron.mark_diggable = function(pos, nodes_dug, player) - -- mark the node as dug, if the player provided would have been able to dig it. - -- Don't *actually* dig the node yet, though, because if we dig a node with sand over it the sand will start falling - -- and then destroy whatever node we place there subsequently (either by a builder head or by moving a digtron node) - -- I don't like sand. It's coarse and rough and irritating and it gets everywhere. And it necessitates complicated dig routines. - -- returns fuel cost and what will be dropped by digging these nodes. - - local target = minetest.get_node(pos) - - -- prevent digtrons from being marked for digging. - if minetest.get_item_group(target.name, "digtron") ~= 0 or - minetest.get_item_group(target.name, "digtron_protected") ~= 0 or - minetest.get_item_group(target.name, "immortal") ~= 0 then - return 0 - end - - local targetdef = minetest.registered_nodes[target.name] - if targetdef == nil or targetdef.can_dig == nil or targetdef.can_dig(pos, player) then - nodes_dug:set(pos.x, pos.y, pos.z, true) - if target.name ~= "air" then - local in_known_group = false - local material_cost = 0 - - if digtron.config.uses_resources then - if minetest.get_item_group(target.name, "cracky") ~= 0 then - in_known_group = true - material_cost = math.max(material_cost, digtron.config.dig_cost_cracky) - end - if minetest.get_item_group(target.name, "crumbly") ~= 0 then - in_known_group = true - material_cost = math.max(material_cost, digtron.config.dig_cost_crumbly) - end - if minetest.get_item_group(target.name, "choppy") ~= 0 then - in_known_group = true - material_cost = math.max(material_cost, digtron.config.dig_cost_choppy) - end - if not in_known_group then - material_cost = digtron.config.dig_cost_default - end - end - - return material_cost, minetest.get_node_drops(target.name, "") - end - end - return 0 -end - -digtron.can_build_to = function(pos, protected_nodes, dug_nodes) - -- Returns whether a space is clear to have something put into it - - if protected_nodes:get(pos.x, pos.y, pos.z) then - return false - end - - -- tests if the location pointed to is clear to move something into - local target = minetest.get_node(pos) - if target.name == "air" or - dug_nodes:get(pos.x, pos.y, pos.z) == true or - minetest.registered_nodes[target.name].buildable_to == true - then - return true - end - return false -end - -digtron.can_move_to = function(pos, protected_nodes, dug_nodes) - -- Same as can_build_to, but also checks if the current node is part of the digtron. - -- this allows us to disregard obstructions that *will* move out of the way. - if digtron.can_build_to(pos, protected_nodes, dug_nodes) == true or - minetest.get_item_group(minetest.get_node(pos).name, "digtron") ~= 0 then - return true - end - return false -end - - -digtron.place_in_inventory = function(itemname, inventory_positions, fallback_pos) - --tries placing the item in each inventory node in turn. If there's no room, drop it at fallback_pos - local itemstack = ItemStack(itemname) - if inventory_positions ~= nil then - for k, location in pairs(inventory_positions) do - node_inventory_table.pos = location.pos - local inv = minetest.get_inventory(node_inventory_table) - itemstack = inv:add_item("main", itemstack) - if itemstack:is_empty() then - return nil - end - end - end - minetest.add_item(fallback_pos, itemstack) -end - -digtron.place_in_specific_inventory = function(itemname, pos, inventory_positions, fallback_pos) - --tries placing the item in a specific inventory. Other parameters are used as fallbacks on failure - --Use this method for putting stuff back after testing and failed builds so that if the player - --is trying to keep various inventories organized manually stuff will go back where it came from, - --probably. - local itemstack = ItemStack(itemname) - node_inventory_table.pos = pos - local inv = minetest.get_inventory(node_inventory_table) - local returned_stack = inv:add_item("main", itemstack) - if not returned_stack:is_empty() then - -- we weren't able to put the item back into that particular inventory for some reason. - -- try putting it *anywhere.* - digtron.place_in_inventory(returned_stack, inventory_positions, fallback_pos) - end -end - -digtron.take_from_inventory = function(itemname, inventory_positions) - if inventory_positions == nil then return nil end - --tries to take an item from each inventory node in turn. Returns location of inventory item was taken from on success, nil on failure - local itemstack = ItemStack(itemname) - for k, location in pairs(inventory_positions) do - node_inventory_table.pos = location.pos - local inv = minetest.get_inventory(node_inventory_table) - local output = inv:remove_item("main", itemstack) - if not output:is_empty() then - return location.pos - end - end - return nil -end - --- Used to determine which coordinate is being checked for periodicity. eg, if the digtron is moving in the z direction, then periodicity is checked for every n nodes in the z axis. -digtron.get_controlling_coordinate = function(pos, facedir) - -- used for determining builder period and offset - local dir = digtron.facedir_to_dir_map[facedir] - if dir == 1 or dir == 3 then - return "z" - elseif dir == 2 or dir == 4 then - return "x" - else - return "y" - end -end - -local fuel_craft = {method="fuel", width=1, items={}} -- reusable crafting recipe table for get_craft_result calls below --- Searches fuel store inventories for burnable items and burns them until target is reached or surpassed --- (or there's nothing left to burn). Returns the total fuel value burned --- if the "test" parameter is set to true, doesn't actually take anything out of inventories. --- We can get away with this sort of thing for fuel but not for builder inventory because there's just one --- controller node burning stuff, not multiple build heads drawing from inventories in turn. Much simpler. -digtron.burn = function(fuelstore_positions, target, test) - if fuelstore_positions == nil then - return 0 - end - - local current_burned = 0 - for k, location in pairs(fuelstore_positions) do - if current_burned > target then - break - end - node_inventory_table.pos = location.pos - local inv = minetest.get_inventory(node_inventory_table) - local invlist = inv:get_list("fuel") - - if invlist == nil then -- This check shouldn't be needed, it's yet another guard against https://github.com/minetest/minetest/issues/8067 - break - end - - for i, itemstack in pairs(invlist) do - fuel_craft.items[1] = itemstack:peek_item(1) - local fuel_per_item = minetest.get_craft_result(fuel_craft).time - if fuel_per_item ~= 0 then - local actual_burned = math.min( - math.ceil((target - current_burned)/fuel_per_item), -- burn this many, if we can. - itemstack:get_count() -- how many we have at most. - ) - if test ~= true then - -- don't bother recording the items if we're just testing, nothing is actually being removed. - itemstack:set_count(itemstack:get_count() - actual_burned) - end - current_burned = current_burned + actual_burned * fuel_per_item - end - if current_burned > target then - break - end - end - if test ~= true then - -- only update the list if we're doing this for real. - inv:set_list("fuel", invlist) - end - end - return current_burned -end - --- Consume energy from the batteries --- The same as burning coal, except that instead of destroying the items in the inventory, we merely drain --- the charge in them, leaving them empty. The charge is converted into "coal heat units" by a downscaling --- factor, since if taken at face value (10000 EU), the batteries would be the ultimate power source barely --- ever needing replacement. -digtron.tap_batteries = function(battery_positions, target, test) - if (battery_positions == nil) then - return 0 - end - - local current_burned = 0 - -- 1 coal block is 370 PU - -- 1 coal lump is 40 PU - -- An RE battery holds 10000 EU of charge - -- local power_ratio = 100 -- How much charge equals 1 unit of PU from coal - -- setting Moved to digtron.config.power_ratio - - for k, location in pairs(battery_positions) do - if current_burned > target then - break - end - node_inventory_table.pos = location.pos - local inv = minetest.get_inventory(node_inventory_table) - local invlist = inv:get_list("batteries") - - if (invlist == nil) then -- This check shouldn't be needed, it's yet another guard against https://github.com/minetest/minetest/issues/8067 - break - end - - for i, itemstack in pairs(invlist) do - local meta = minetest.deserialize(itemstack:get_metadata()) - if (meta ~= nil) then - local power_available = math.floor(meta.charge / digtron.config.power_ratio) - if power_available ~= 0 then - local actual_burned = power_available -- we just take all we have from the battery, since they aren't stackable - if test ~= true then - -- don't bother recording the items if we're just testing, nothing is actually being removed. - local charge_left = meta.charge - power_available * digtron.config.power_ratio - local properties = itemstack:get_tool_capabilities() - -- itemstack = technic.set_RE_wear(itemstack, charge_left, properties.groupcaps.fleshy.uses) - -- we only need half the function, so why bother using it in the first place - - -- Charge is stored separately, but shown as wear level - -- This calls for recalculating the value. - local charge_level - if charge_left == 0 then - charge_level = 0 - else - charge_level = 65536 - math.floor(charge_left / properties.groupcaps.fleshy.uses * 65535) - if charge_level > 65535 then charge_level = 65535 end - if charge_level < 1 then charge_level = 1 end - end - itemstack:set_wear(charge_level) - - meta.charge = charge_left - itemstack:set_metadata(minetest.serialize(meta)) - - end - current_burned = current_burned + actual_burned - end - - end - - if current_burned > target then - break - end - end - - if test ~= true then - -- only update the list if we're doing this for real. - inv:set_list("batteries", invlist) - end - end - return current_burned -end - -digtron.remove_builder_item = function(pos) - local objects = minetest.env:get_objects_inside_radius(pos, 0.5) - if objects ~= nil then - for _, obj in ipairs(objects) do - if obj and obj:get_luaentity() and obj:get_luaentity().name == "digtron:builder_item" then - obj:remove() - end - end - end -end - -digtron.update_builder_item = function(pos) - digtron.remove_builder_item(pos) - node_inventory_table.pos = pos - local inv = minetest.get_inventory(node_inventory_table) - local item_stack = inv:get_stack("main", 1) - if not item_stack:is_empty() then - digtron.create_builder_item = item_stack:get_name() - minetest.add_entity(pos,"digtron:builder_item") - end -end - -local damage_def = { - full_punch_interval = 1.0, - damage_groups = {}, -} -digtron.damage_creatures = function(player, source_pos, target_pos, amount, items_dropped) - local objects = minetest.env:get_objects_inside_radius(target_pos, 1.0) - if objects ~= nil then - damage_def.damage_groups.fleshy = amount - local velocity = { - x = target_pos.x-source_pos.x, - y = target_pos.y-source_pos.y + 0.2, - z = target_pos.z-source_pos.z, - } - for _, obj in ipairs(objects) do - if obj:is_player() then - -- See issue #2960 for status of a "set player velocity" method - -- instead, knock the player back - local newpos = { - x = target_pos.x + velocity.x, - y = target_pos.y + velocity.y, - z = target_pos.z + velocity.z, - } - obj:set_pos(newpos) - obj:punch(player, 1.0, damage_def, nil) - else - local lua_entity = obj:get_luaentity() - if lua_entity ~= nil then - if lua_entity.name == "__builtin:item" then - table.insert(items_dropped, lua_entity.itemstring) - lua_entity.itemstring = "" - obj:remove() - else - if obj.add_velocity ~= nil then - obj:add_velocity(velocity) - else - local vel = obj:get_velocity() - obj:set_velocity(vector.add(vel, velocity)) - end - obj:punch(player, 1.0, damage_def, nil) - end - end - end - end - end - -- If we killed any mobs they might have dropped some stuff, vacuum that up now too. - objects = minetest.env:get_objects_inside_radius(target_pos, 1.0) - if objects ~= nil then - for _, obj in ipairs(objects) do - if not obj:is_player() then - local lua_entity = obj:get_luaentity() - if lua_entity ~= nil and lua_entity.name == "__builtin:item" then - table.insert(items_dropped, lua_entity.itemstring) - lua_entity.itemstring = "" - obj:remove() - end - end - end - end -end - -digtron.is_soft_material = function(target) - local target_node = minetest.get_node(target) - if minetest.get_item_group(target_node.name, "crumbly") ~= 0 or - minetest.get_item_group(target_node.name, "choppy") ~= 0 or - minetest.get_item_group(target_node.name, "snappy") ~= 0 or - minetest.get_item_group(target_node.name, "oddly_breakable_by_hand") ~= 0 or - minetest.get_item_group(target_node.name, "fleshy") ~= 0 then - return true - end - return false -end - --- If someone sets very large offsets or intervals for the offset markers they might be added too far --- away. safe_add_entity causes these attempts to be ignored rather than crashing the game. --- returns the entity if successful, nil otherwise -function safe_add_entity(pos, name) - success, ret = pcall(minetest.add_entity, pos, name) - if success then return ret else return nil end -end - -digtron.show_offset_markers = function(pos, offset, period) - local buildpos = digtron.find_new_pos(pos, minetest.get_node(pos).param2) - local x_pos = math.floor((buildpos.x+offset)/period)*period - offset - safe_add_entity({x=x_pos, y=buildpos.y, z=buildpos.z}, "digtron:marker") - if x_pos >= buildpos.x then - safe_add_entity({x=x_pos - period, y=buildpos.y, z=buildpos.z}, "digtron:marker") - end - if x_pos <= buildpos.x then - safe_add_entity({x=x_pos + period, y=buildpos.y, z=buildpos.z}, "digtron:marker") - end - - local y_pos = math.floor((buildpos.y+offset)/period)*period - offset - safe_add_entity({x=buildpos.x, y=y_pos, z=buildpos.z}, "digtron:marker_vertical") - if y_pos >= buildpos.y then - safe_add_entity({x=buildpos.x, y=y_pos - period, z=buildpos.z}, "digtron:marker_vertical") - end - if y_pos <= buildpos.y then - safe_add_entity({x=buildpos.x, y=y_pos + period, z=buildpos.z}, "digtron:marker_vertical") - end - - local z_pos = math.floor((buildpos.z+offset)/period)*period - offset - - local entity = safe_add_entity({x=buildpos.x, y=buildpos.y, z=z_pos}, "digtron:marker") - if entity ~= nil then entity:setyaw(1.5708) end - - if z_pos >= buildpos.z then - local entity = safe_add_entity({x=buildpos.x, y=buildpos.y, z=z_pos - period}, "digtron:marker") - if entity ~= nil then entity:setyaw(1.5708) end - end - if z_pos <= buildpos.z then - local entity = safe_add_entity({x=buildpos.x, y=buildpos.y, z=z_pos + period}, "digtron:marker") - if entity ~= nil then entity:setyaw(1.5708) end - end -end \ No newline at end of file diff --git a/util_execute_cycle.lua b/util_execute_cycle.lua deleted file mode 100644 index 900fd0f..0000000 --- a/util_execute_cycle.lua +++ /dev/null @@ -1,617 +0,0 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - -local dig_dust = function(pos, facing) - local direction = minetest.facedir_to_dir(facing) - return { - amount = 10, - time = 1.0, - minpos = vector.subtract(pos, vector.new(0.5,0.5,0.5)), - maxpos = vector.add(pos, vector.new(0.5,0.5,0.5)), - minvel = vector.multiply(direction, -10), - maxvel = vector.multiply(direction, -20), - minacc = {x=0, y=-40, z=0}, - maxacc = {x=0, y=-40, z=0}, - minexptime = 0.25, - maxexptime = 0.5, - minsize = 2, - maxsize = 5, - collisiondetection = false, - vertical = false, - texture = "default_item_smoke.png^[colorize:#9F817080", - } -end - -local burn_smoke = function(pos, amount) - return { - amount = math.min(amount, 40), - time = 1.0, - minpos = vector.subtract(pos, vector.new(0.5,0.5,0.5)), - maxpos = vector.add(pos, vector.new(0.5,0.5,0.5)), - minvel = {x=0, y=2, z=0}, - maxvel = {x=0, y=5, z=0}, - minacc = {x=0, y=0, z=0}, - maxacc = {x=0, y=0, z=0}, - minexptime = 0.5, - maxexptime = 1.5, - minsize = 8, - maxsize = 12, - collisiondetection = false, - vertical = false, - texture = "default_item_smoke.png^[colorize:#000000DD", - } -end - ---Performs various tests on a layout to play warning noises and see if Digtron can move at all. -local function neighbour_test(layout, status_text, dir) - if layout.ignore_touching == true then - -- if the digtron array touches unloaded nodes, too dangerous to do anything in that situation. Abort. - minetest.sound_play("buzzer", {gain=0.25, pos=layout.controller}) - return S("Digtron is adjacent to unloaded nodes.") .. "\n" .. status_text, 1 - end - - if layout.water_touching == true then - minetest.sound_play("sploosh", {gain=1.0, pos=layout.controller}) - end - - if layout.lava_touching == true then - minetest.sound_play("woopwoopwoop", {gain=1.0, pos=layout.controller}) - end - - if dir and dir.y ~= -1 and layout.traction * digtron.config.traction_factor < table.getn(layout.all) then - -- digtrons can't fly, though they can fall - minetest.sound_play("squeal", {gain=1.0, pos=layout.controller}) - return S("Digtron has @1 blocks but only enough traction to move @2 blocks.\n", table.getn(layout.all), layout.traction * digtron.config.traction_factor) - .. status_text, 2 - end - - return status_text, 0 -end - --- Checks if a player is within a layout's extents. -local function move_player_test(layout, player) - local player_pos = player:getpos() - if player_pos.x >= layout.extents_min_x - 1 and player_pos.x <= layout.extents_max_x + 1 and - player_pos.y >= layout.extents_min_y - 1 and player_pos.y <= layout.extents_max_y + 1 and - player_pos.z >= layout.extents_min_z - 1 and player_pos.z <= layout.extents_max_z + 1 then - return true - end - return false -end - -local node_inventory_table = {type="node"} -- a reusable parameter for get_inventory calls, set the pos parameter before using. -local function test_stop_block(pos, items) - node_inventory_table.pos = pos - local inv = minetest.get_inventory(node_inventory_table) - local item_stack = inv:get_stack("stop", 1) - if not item_stack:is_empty() then - for _, item in pairs(items) do - if item == item_stack:get_name() then - return true - end - end - end - return false -end - -local function check_digtron_size(layout) - if #layout.all > digtron.config.size_limit then - return S("Size limit of @1 reached with @2 nodes!", digtron.config.size_limit, #layout.all) - end -end - --- returns newpos, status string, and a return code indicating why the method returned (so the auto-controller can keep trying if it's due to unloaded nodes) --- 0 - success --- 1 - failed due to unloaded nodes --- 2 - failed due to insufficient traction --- 3 - obstructed by undiggable node --- 4 - insufficient fuel --- 5 - unknown builder error during testing --- 6 - builder with unset output --- 7 - insufficient builder materials in inventory --- 8 - size/node limit reached -digtron.execute_dig_cycle = function(pos, clicker) - local meta = minetest.get_meta(pos) - local facing = minetest.get_node(pos).param2 - local dir = minetest.facedir_to_dir(facing) - local fuel_burning = meta:get_float("fuel_burning") -- get amount of burned fuel left over from last cycle - local status_text = S("Heat remaining in controller furnace: @1", math.floor(math.max(0, fuel_burning))) - local exhaust = meta:get_int("on_coal") - - local layout = DigtronLayout.create(pos, clicker) - - local status_text, return_code = neighbour_test(layout, status_text, dir) - if return_code ~= 0 then - return pos, status_text, return_code - end - - local size_check_error = check_digtron_size(layout) - if size_check_error then - return pos, size_check_error, 8 - end - - local controlling_coordinate = digtron.get_controlling_coordinate(pos, facing) - - ---------------------------------------------------------------------------------------------------------------------- - - local items_dropped = {} - local digging_fuel_cost = 0 - local particle_systems = {} - - -- execute the execute_dig method on all digtron components that have one - -- This builds a set of nodes that will be dug and returns a list of products that will be generated - -- but doesn't actually dig the nodes yet. That comes later. - -- If we dug them now, sand would fall and some digtron nodes would die. - if layout.diggers ~= nil then - for k, location in pairs(layout.diggers) do - local target = minetest.get_node(location.pos) - local targetdef = minetest.registered_nodes[target.name] - if targetdef.execute_dig ~= nil then - local fuel_cost, dropped = targetdef.execute_dig(location.pos, layout.protected, layout.nodes_dug, controlling_coordinate, false, clicker) - if dropped ~= nil then - for _, itemname in pairs(dropped) do - table.insert(items_dropped, itemname) - end - if digtron.config.particle_effects then - table.insert(particle_systems, dig_dust(vector.add(location.pos, dir), target.param2)) - end - end - digging_fuel_cost = digging_fuel_cost + fuel_cost - else - minetest.log(string.format("%s has digger group but is missing execute_dig method! This is an error in mod programming, file a bug.", targetdef.name)) - end - end - end - - ---------------------------------------------------------------------------------------------------------------------- - - -- test if any digtrons are obstructed by non-digtron nodes that haven't been marked - -- as having been dug. - local can_move = true - for _, location in pairs(layout.all) do - local newpos = vector.add(location.pos, dir) - if not digtron.can_move_to(newpos, layout.protected, layout.nodes_dug) then - can_move = false - end - end - - if test_stop_block(pos, items_dropped) then - can_move = false - end - - if not can_move then - -- mark this node as waiting, will clear this flag in digtron.config.cycle_time seconds - minetest.get_meta(pos):set_string("waiting", "true") - minetest.get_node_timer(pos):start(digtron.config.cycle_time) - minetest.sound_play("squeal", {gain=1.0, pos=pos}) - minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - return pos, S("Digtron is obstructed.") .. "\n" .. status_text, 3 --Abort, don't dig and don't build. - end - - ---------------------------------------------------------------------------------------------------------------------- - - -- ask each builder node if it can get what it needs from inventory to build this cycle. - -- This is a complicated test because each builder needs to actually *take* the item it'll - -- need from inventory, and then we put it all back afterward. - -- Note that this test may overestimate the amount of work that will actually need to be done so don't treat its fuel cost as authoritative. - local can_build = true - local test_build_return_code = nil - local test_build_return_items = nil - local failed_to_find = nil - local test_items = {} - local test_fuel_items = {} - local test_build_fuel_cost = 0 - if layout.builders ~= nil then - for k, location in pairs(layout.builders) do - local target = minetest.get_node(location.pos) - local targetdef = minetest.registered_nodes[target.name] - local test_location = vector.add(location.pos, dir) - if targetdef.test_build ~= nil then - test_build_return_code, test_build_return_items, failed_to_find = targetdef.test_build(location.pos, test_location, layout.inventories, layout.protected, layout.nodes_dug, controlling_coordinate, layout.controller) - for k, return_item in pairs(test_build_return_items) do - table.insert(test_items, return_item) - test_build_fuel_cost = test_build_fuel_cost + digtron.config.build_cost - end - if test_build_return_code > 1 then - can_build = false - break - end - else - minetest.log(string.format("%s has builder group but is missing test_build method! This is an error in mod programming, file a bug.", targetdef.name)) - end - end - end - - local test_fuel_needed = test_build_fuel_cost + digging_fuel_cost - fuel_burning - local test_fuel_burned = 0 - - local power_from_cables = 0 - if minetest.get_modpath("technic") then - if layout.power_connectors ~= nil then - local power_inputs = {} - for _, power_connector in pairs(layout.power_connectors) do - if power_connector.meta.fields.HV_network and power_connector.meta.fields.HV_EU_input then - power_inputs[power_connector.meta.fields.HV_network] = tonumber(power_connector.meta.fields.HV_EU_input) - end - end - for _, power in pairs(power_inputs) do - power_from_cables = power_from_cables + power - end - power_from_cables = power_from_cables / digtron.config.power_ratio - test_fuel_burned = power_from_cables - end - - if test_fuel_needed - test_fuel_burned > 0 then - -- check for the available electrical power - test_fuel_burned = test_fuel_burned + digtron.tap_batteries(layout.battery_holders, test_fuel_needed, true) - end - end - if (test_fuel_needed < test_fuel_burned) then - exhaust = 0 -- all power needs met by electricity, don't blow smoke - else - -- burn combustible fuel if not enough power - test_fuel_burned = test_fuel_burned + digtron.burn(layout.fuelstores, test_fuel_needed - test_fuel_burned, true) - exhaust = 1 -- burning fuel produces smoke - end - - --Put everything back where it came from - for k, item_return in pairs(test_items) do - digtron.place_in_specific_inventory(item_return.item, item_return.location, layout.inventories, layout.controller) - end - - if test_fuel_needed > fuel_burning + test_fuel_burned then - minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - return pos, S("Digtron needs more fuel."), 4 -- abort, don't dig and don't build. - end - - if not can_build then - minetest.get_meta(pos):set_string("waiting", "true") - minetest.get_node_timer(pos):start(digtron.config.cycle_time) - local return_string = nil - local return_code = 5 - if test_build_return_code == 3 then - minetest.sound_play("honk", {gain=0.5, pos=pos}) -- A builder is not configured - return_string = S("Digtron connected to at least one builder with no output material assigned.") .. "\n" - return_code = 6 - elseif test_build_return_code == 2 then - minetest.sound_play("dingding", {gain=1.0, pos=pos}) -- Insufficient inventory - return_string = S("Digtron has insufficient building materials. Needed: @1", failed_to_find:get_name()) .. "\n" - return_code = 7 - end - return pos, return_string .. status_text, return_code --Abort, don't dig and don't build. - end - - ---------------------------------------------------------------------------------------------------------------------- - - -- All tests passed, ready to go for real! - minetest.sound_play("construction", {gain=1.0, pos=pos}) - - -- if the player is standing within the array or next to it, move him too. - local move_player = move_player_test(layout, clicker) - - -- damage the weak flesh - if digtron.config.damage_hp > 0 and layout.diggers ~= nil then - for k, location in pairs(layout.diggers) do - local target = minetest.get_node(location.pos) - local targetdef = minetest.registered_nodes[target.name] - if targetdef.damage_creatures ~= nil then - targetdef.damage_creatures(clicker, location.pos, controlling_coordinate, items_dropped) - end - end - end - - --move the array - layout:move_layout_image(dir) - if not layout:write_layout_image(clicker) then - return pos, "unrecoverable write_layout_image error", 1 - end - local oldpos = {x=pos.x, y=pos.y, z=pos.z} - pos = vector.add(pos, dir) - meta = minetest.get_meta(pos) - if move_player then - clicker:moveto(vector.add(dir, clicker:getpos()), true) - end - - -- store or drop the products of the digger heads - for _, itemname in pairs(items_dropped) do - digtron.place_in_inventory(itemname, layout.inventories, oldpos) - end - digtron.award_item_dug(items_dropped, clicker) -- Achievements mod hook - - local building_fuel_cost = 0 - local strange_failure = false - -- execute_build on all digtron components that have one - if layout.builders ~= nil then - for k, location in pairs(layout.builders) do - local target = minetest.get_node(location.pos) - local targetdef = minetest.registered_nodes[target.name] - if targetdef.execute_build ~= nil then - --using the old location of the controller as fallback so that any leftovers land with the rest of the digger output. Not that there should be any. - local build_return = targetdef.execute_build(location.pos, clicker, layout.inventories, layout.protected, layout.nodes_dug, controlling_coordinate, oldpos) - if build_return < 0 then - -- This happens if there's insufficient inventory, but we should have confirmed there was sufficient inventory during test phase. - -- So this should never happen. However, "should never happens" happen sometimes. So - -- don't interrupt the build cycle as a whole, we've already moved so might as well try to complete as much as possible. - strange_failure = true - build_return = (build_return * -1) - 1 - elseif digtron.config.uses_resources then - building_fuel_cost = building_fuel_cost + (digtron.config.build_cost * build_return) - end - else - minetest.log(string.format("%s has builder group but is missing execute_build method! This is an error in mod programming, file a bug.", targetdef.name)) - end - end - end - - if layout.auto_ejectors ~= nil then - for k, location in pairs(layout.auto_ejectors) do - local target = minetest.get_node(location.pos) - local targetdef = minetest.registered_nodes[target.name] - if targetdef.execute_eject ~= nil then - targetdef.execute_eject(location.pos, target, clicker, layout) - else - minetest.log(string.format("%s has an ejector group but is missing execute_eject method! This is an error in mod programming, file a bug.", targetdef.name)) - end - end - end - - local status_text = "" - if strange_failure then - -- We weren't able to detect this build failure ahead of time, so make a big noise now. This is strange, shouldn't happen. - minetest.sound_play("dingding", {gain=1.0, pos=pos}) - minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - status_text = S("Digtron unexpectedly failed to execute one or more build operations, likely due to an inventory error.") .. "\n" - end - - local total_fuel_cost = math.max(digging_fuel_cost + building_fuel_cost - power_from_cables, 0) - - -- actually burn the fuel needed - fuel_burning = fuel_burning - total_fuel_cost - if digtron.config.particle_effects and exhaust == 1 then - table.insert(particle_systems, burn_smoke(pos, total_fuel_cost)) - end - if fuel_burning < 0 then - -- we tap into the batteries either way - fuel_burning = fuel_burning + digtron.tap_batteries(layout.battery_holders, -fuel_burning, false) - if exhaust == 1 then - -- but we burn coal only if we must (exhaust = flag) - fuel_burning = fuel_burning + digtron.burn(layout.fuelstores, -fuel_burning, false) - end - end - - meta:set_float("fuel_burning", fuel_burning) - meta:set_int("on_coal", exhaust) - status_text = status_text .. S("Heat remaining in controller furnace: @1", math.floor(math.max(0, fuel_burning))) - - -- Eyecandy - for _, particles in pairs(particle_systems) do - minetest.add_particlespawner(particles) - end - - -- finally, dig out any nodes remaining to be dug. Some of these will have had their flag revoked because - -- a builder put something there or because they're another digtron node. - local node_to_dig, whether_to_dig = layout.nodes_dug:pop() - while node_to_dig ~= nil do - if whether_to_dig == true then - minetest.log("action", string.format("%s uses Digtron to dig %s at (%d, %d, %d)", clicker:get_player_name(), minetest.get_node(node_to_dig).name, node_to_dig.x, node_to_dig.y, node_to_dig.z)) - minetest.remove_node(node_to_dig) - end - -- all of the digtron's nodes wind up in nodes_dug, so this is an ideal place to stick - -- a check to make sand fall after the digtron has passed. - minetest.check_for_falling({x=node_to_dig.x, y=node_to_dig.y+1, z=node_to_dig.z}) - node_to_dig, whether_to_dig = layout.nodes_dug:pop() - end - return pos, status_text, 0 -end - - --- Simplified version of the above method that only moves, and doesn't execute diggers or builders. -digtron.execute_move_cycle = function(pos, clicker) - local meta = minetest.get_meta(pos) - local layout = DigtronLayout.create(pos, clicker) - - local status_text = "" - local status_text, return_code = neighbour_test(layout, status_text, nil) -- skip traction check for pusher by passing nil for direction - if return_code ~= 0 then - return pos, status_text, return_code - end - - local size_check_error = check_digtron_size(layout) - if size_check_error then - return pos, size_check_error, 8 - end - - local facing = minetest.get_node(pos).param2 - local dir = minetest.facedir_to_dir(facing) - local controlling_coordinate = digtron.get_controlling_coordinate(pos, facing) - - -- if the player is standing within the array or next to it, move him too. - local move_player = move_player_test(layout, clicker) - - -- test if any digtrons are obstructed by non-digtron nodes - layout:move_layout_image(dir) - if not layout:can_write_layout_image() then - -- mark this node as waiting, will clear this flag in digtron.config.cycle_time seconds - minetest.get_meta(pos):set_string("waiting", "true") - minetest.get_node_timer(pos):start(digtron.config.cycle_time) - minetest.sound_play("squeal", {gain=1.0, pos=pos}) - minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - return pos, S("Digtron is obstructed.") .. "\n" .. status_text, 3 --Abort, don't dig and don't build. - end - - minetest.sound_play("truck", {gain=1.0, pos=pos}) - - --move the array - if not layout:write_layout_image(clicker) then - return pos, "unrecoverable write_layout_image error", 1 - end - - pos = vector.add(pos, dir) - if move_player then - clicker:moveto(vector.add(clicker:getpos(), dir), true) - end - return pos, "", 0 -end - --- Simplified version of the dig cycle that moves laterally relative to the controller's orientation ("downward") --- Does the dig portion of the cycle, but skips the build portion. --- returns newpos, status string, and a return code indicating why the method returned (so the auto-controller can keep trying if it's due to unloaded nodes) --- 0 - success --- 1 - failed due to unloaded nodes --- 2 - failed due to insufficient traction --- 3 - obstructed by undiggable node --- 4 - insufficient fuel -digtron.execute_downward_dig_cycle = function(pos, clicker) - local meta = minetest.get_meta(pos) - local facing = minetest.get_node(pos).param2 - local dir = digtron.facedir_to_down_dir(facing) - local fuel_burning = meta:get_float("fuel_burning") -- get amount of burned fuel left over from last cycle - local status_text = S("Heat remaining in controller furnace: @1", math.floor(math.max(0, fuel_burning))) - local exhaust = meta:get_int("on_coal") - - local layout = DigtronLayout.create(pos, clicker) - - local status_text, return_code = neighbour_test(layout, status_text, dir) - if return_code ~= 0 then - return pos, status_text, return_code - end - - local size_check_error = check_digtron_size(layout) - if size_check_error then - return pos, size_check_error, 8 - end - - - local controlling_coordinate = digtron.get_controlling_coordinate(pos, facing) - - ---------------------------------------------------------------------------------------------------------------------- - - local items_dropped = {} - local digging_fuel_cost = 0 - local particle_systems = {} - - -- execute the execute_dig method on all digtron components that have one - -- This builds a set of nodes that will be dug and returns a list of products that will be generated - -- but doesn't actually dig the nodes yet. That comes later. - -- If we dug them now, sand would fall and some digtron nodes would die. - if layout.diggers ~= nil then - for k, location in pairs(layout.diggers) do - local target = minetest.get_node(location.pos) - local targetdef = minetest.registered_nodes[target.name] - if targetdef.execute_dig ~= nil then - local fuel_cost, dropped = targetdef.execute_dig(location.pos, layout.protected, layout.nodes_dug, controlling_coordinate, true, clicker) - if dropped ~= nil then - for _, itemname in pairs(dropped) do - table.insert(items_dropped, itemname) - end - if digtron.config.particle_effects then - table.insert(particle_systems, dig_dust(vector.add(location.pos, dir), target.param2)) - end - end - digging_fuel_cost = digging_fuel_cost + fuel_cost - else - minetest.log(string.format("%s has digger group but is missing execute_dig method! This is an error in mod programming, file a bug.", targetdef.name)) - end - end - end - - ---------------------------------------------------------------------------------------------------------------------- - - -- test if any digtrons are obstructed by non-digtron nodes that haven't been marked - -- as having been dug. - local can_move = true - for _, location in pairs(layout.all) do - local newpos = vector.add(location.pos, dir) - if not digtron.can_move_to(newpos, layout.protected, layout.nodes_dug) then - can_move = false - end - end - - if test_stop_block(pos, items_dropped) then - can_move = false - end - - if not can_move then - -- mark this node as waiting, will clear this flag in digtron.config.cycle_time seconds - minetest.get_meta(pos):set_string("waiting", "true") - minetest.get_node_timer(pos):start(digtron.config.cycle_time) - minetest.sound_play("squeal", {gain=1.0, pos=pos}) - minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - return pos, S("Digtron is obstructed.") .. "\n" .. status_text, 3 --Abort, don't dig and don't build. - end - - ---------------------------------------------------------------------------------------------------------------------- - - -- All tests passed, ready to go for real! - minetest.sound_play("construction", {gain=1.0, pos=pos}) - - -- if the player is standing within the array or next to it, move him too. - local move_player = move_player_test(layout, clicker) - - -- damage the weak flesh - if digtron.config.damage_hp > 0 and layout.diggers ~= nil then - for k, location in pairs(layout.diggers) do - local target = minetest.get_node(location.pos) - local targetdef = minetest.registered_nodes[target.name] - if targetdef.damage_creatures ~= nil then - targetdef.damage_creatures(clicker, location.pos, controlling_coordinate, items_dropped) - end - end - end - - --move the array - layout:move_layout_image(digtron.facedir_to_down_dir(facing)) - if not layout:write_layout_image(clicker) then - return pos, "unrecoverable write_layout_image error", 1 - end - local oldpos = {x=pos.x, y=pos.y, z=pos.z} - pos = vector.add(pos, dir) - meta = minetest.get_meta(pos) - if move_player then - clicker:moveto(vector.add(clicker:getpos(), dir), true) - end - - -- store or drop the products of the digger heads - for _, itemname in pairs(items_dropped) do - digtron.place_in_inventory(itemname, layout.inventories, oldpos) - end - digtron.award_item_dug(items_dropped, clicker) -- Achievements mod hook - - local status_text = "" - - -- actually burn the fuel needed - fuel_burning = fuel_burning - digging_fuel_cost - if digtron.config.particle_effects and exhaust == 1 then - table.insert(particle_systems, burn_smoke(pos, digging_fuel_cost)) - end - if fuel_burning < 0 then - -- we tap into the batteries either way - fuel_burning = fuel_burning + digtron.tap_batteries(layout.battery_holders, -fuel_burning, false) - if exhaust == 1 then - -- but we burn coal only if we must (exhaust = flag) - fuel_burning = fuel_burning + digtron.burn(layout.fuelstores, -fuel_burning, false) - end - end - - meta:set_float("fuel_burning", fuel_burning) - meta:set_int("on_coal", exhaust) - status_text = status_text .. S("Heat remaining in controller furnace: @1", math.floor(math.max(0, fuel_burning))) - - -- Eyecandy - for _, particles in pairs(particle_systems) do - minetest.add_particlespawner(particles) - end - - -- finally, dig out any nodes remaining to be dug. Some of these will have had their flag revoked because - -- a builder put something there or because they're another digtron node. - local node_to_dig, whether_to_dig = layout.nodes_dug:pop() - while node_to_dig ~= nil do - if whether_to_dig == true then - minetest.log("action", string.format("%s uses Digtron to dig %s at (%d, %d, %d)", clicker:get_player_name(), minetest.get_node(node_to_dig).name, node_to_dig.x, node_to_dig.y, node_to_dig.z)) - minetest.remove_node(node_to_dig) - end - node_to_dig, whether_to_dig = layout.nodes_dug:pop() - end - return pos, status_text, 0 -end diff --git a/util_item_place_node.lua b/util_item_place_node.lua index ba6ff37..16cbb3b 100644 --- a/util_item_place_node.lua +++ b/util_item_place_node.lua @@ -18,16 +18,6 @@ --See the GNU Lesser General Public License for more details: --https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html --- Mapping from facedir value to index in facedir_to_dir. -digtron.facedir_to_dir_map = { - [0]=1, 2, 3, 4, - 5, 2, 6, 4, - 6, 2, 5, 4, - 1, 5, 3, 6, - 1, 6, 3, 5, - 1, 4, 3, 2, -} - local function has_prefix(str, prefix) return str:sub(1, string.len(prefix)) == prefix end