From 6a38e6843319d21e8522e5cfb396981b5bba62bb Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sat, 17 Aug 2019 01:47:16 -0600 Subject: [PATCH 01/63] clean the slate! Old code will be brought back as it's needed. --- README.md | 38 - awards.lua | 436 ---------- class_fakeplayer.lua | 145 ---- class_layout.lua | 532 ------------ class_pointset.lua | 77 -- config.lua | 69 -- controller.lua | 200 +++++ depends.txt | 8 +- description.txt | 5 - doc.lua | 311 ------- entities.lua | 146 ---- init.lua | 118 +-- locale/template.pot | 1494 -------------------------------- locale/update.bat | 6 - mod.conf | 7 - nodes/node_axle.lua | 69 -- nodes/node_battery_holder.lua | 125 --- nodes/node_builders.lua | 357 -------- nodes/node_controllers.lua | 350 -------- nodes/node_crate.lua | 385 -------- nodes/node_diggers.lua | 515 ----------- nodes/node_duplicator.lua | 189 ---- nodes/node_item_ejector.lua | 174 ---- nodes/node_misc.lua | 5 - nodes/node_power_connector.lua | 100 --- nodes/node_storage.lua | 267 +++--- nodes/recipes.lua | 334 ------- screenshot.png | Bin 59062 -> 0 bytes settingtypes.txt | 51 -- upgrades.lua | 59 -- util.lua | 435 ---------- util_execute_cycle.lua | 617 ------------- util_item_place_node.lua | 181 ---- 33 files changed, 350 insertions(+), 7455 deletions(-) delete mode 100644 README.md delete mode 100644 awards.lua delete mode 100644 class_fakeplayer.lua delete mode 100644 class_layout.lua delete mode 100644 class_pointset.lua delete mode 100644 config.lua create mode 100644 controller.lua delete mode 100644 description.txt delete mode 100644 doc.lua delete mode 100644 entities.lua delete mode 100644 locale/template.pot delete mode 100644 locale/update.bat delete mode 100644 mod.conf delete mode 100644 nodes/node_axle.lua delete mode 100644 nodes/node_battery_holder.lua delete mode 100644 nodes/node_builders.lua delete mode 100644 nodes/node_controllers.lua delete mode 100644 nodes/node_crate.lua delete mode 100644 nodes/node_diggers.lua delete mode 100644 nodes/node_duplicator.lua delete mode 100644 nodes/node_item_ejector.lua delete mode 100644 nodes/node_power_connector.lua delete mode 100644 nodes/recipes.lua delete mode 100644 screenshot.png delete mode 100644 settingtypes.txt delete mode 100644 upgrades.lua delete mode 100644 util.lua delete mode 100644 util_execute_cycle.lua delete mode 100644 util_item_place_node.lua 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_fakeplayer.lua b/class_fakeplayer.lua deleted file mode 100644 index 7976cb4..0000000 --- a/class_fakeplayer.lua +++ /dev/null @@ -1,145 +0,0 @@ --- The purpose of this class is to have something that can be passed into callbacks that --- demand a "Player" object as a parameter and hopefully prevent the mods that have --- registered with those callbacks from crashing on a nil dereference or bad function --- call. This is not supposed to be a remotely functional thing, it's just supposed --- to provide dummy methods and return values of the correct data type for anything that --- might ignore the false "is_player()" return and go ahead and try to use this thing --- anyway. - --- I'm trying to patch holes in bad mod programming, essentially. If a mod is so badly --- programmed that it crashes anyway there's not a lot else I can do on my end of things. - -DigtronFakePlayer = {} -DigtronFakePlayer.__index = DigtronFakePlayer - -local function return_value(x) - return (function() return x end) -end - -local function return_nil() - return nil -end - -local function return_empty_string() - return "" -end - -local function return_zero() - return 0 -end - -local function return_empty_table() - return {} -end - -function DigtronFakePlayer.update(self, pos, player_name) - self.is_fake_player = ":digtron " .. player_name - self.get_pos = return_value(pos) -end - -function DigtronFakePlayer.create(pos, player_name) - local self = {} - setmetatable(self, DigtronFakePlayer) - - self.is_fake_player = ":digtron " .. player_name - - -- ObjectRef - self.get_pos = return_value(pos) - self.set_pos = return_nil - self.move_to = return_nil - self.punch = return_nil - self.right_click = return_nil - self.get_hp = return_value(10) - self.set_hp = return_nil - self.get_inventory = return_nil -- returns an `InvRef` - self.get_wield_list = return_empty_string - self.get_wield_index = return_value(1) - self.get_wielded_item = return_value(ItemStack(nil)) - self.set_wielded_item = return_value(false) - self.set_armor_groups = return_nil - self.get_armor_groups = return_empty_table - self.set_animation = return_nil - self.get_animation = return_nil -- a set of values, maybe important? - self.set_attach = return_nil - self.get_attach = return_nil - self.set_detach = return_nil - self.set_bone_position = return_nil - self.get_bone_position = return_nil - self.set_properties = return_nil - self.get_properties = return_empty_table - - self.is_player = return_value(false) - - self.get_nametag_attributes = return_empty_table - self.set_nametag_attributes = return_nil - - --LuaEntitySAO - self.set_velocity = return_nil - self.get_velocity = return_value({x=0,y=0,z=0}) - self.set_acceleration = return_nil - self.get_acceleration = return_value({x=0,y=0,z=0}) - self.set_yaw = return_nil - self.get_yaw = return_zero - self.set_texture_mod = return_nil - self.get_texture_mod = return_nil -- maybe important? - self.set_sprite = return_nil - --self.get_entity_name` (**Deprecated**: Will be removed in a future version) - self.get_luaentity = return_nil - - -- Player object - - self.get_player_name = return_empty_string - self.get_player_velocity = return_nil - self.get_look_dir = return_value({x=0,y=1,z=0}) - self.get_look_horizontal = return_zero - self.set_look_horizontal = return_nil - self.get_look_vertical = return_zero - self.set_look_vertical = return_nil - - --self.get_look_pitch`: pitch in radians - Deprecated as broken. Use `get_look_vertical` - --self.get_look_yaw`: yaw in radians - Deprecated as broken. Use `get_look_horizontal` - --self.set_look_pitch(radians)`: sets look pitch - Deprecated. Use `set_look_vertical`. - --self.set_look_yaw(radians)`: sets look yaw - Deprecated. Use `set_look_horizontal`. - self.get_breath = return_value(10) - self.set_breath = return_nil - self.get_attribute = return_nil - self.set_attribute = return_nil - - self.set_inventory_formspec = return_nil - self.get_inventory_formspec = return_empty_string - self.get_player_control = return_value({jump=false, right=false, left=false, LMB=false, RMB=false, sneak=false, aux1=false, down=false, up=false} ) - self.get_player_control_bits = return_zero - - self.set_physics_override = return_nil - self.get_physics_override = return_value({speed = 1, jump = 1, gravity = 1, sneak = true, sneak_glitch = false, new_move = true,}) - - - self.hud_add = return_nil - self.hud_remove = return_nil - self.hud_change = return_nil - self.hud_get = return_nil -- possibly important return value? - self.hud_set_flags = return_nil - self.hud_get_flags = return_value({ hotbar=true, healthbar=true, crosshair=true, wielditem=true, breathbar=true, minimap=true }) - self.hud_set_hotbar_itemcount = return_nil - self.hud_get_hotbar_itemcount = return_zero - self.hud_set_hotbar_image = return_nil - self.hud_get_hotbar_image = return_empty_string - self.hud_set_hotbar_selected_image = return_nil - self.hud_get_hotbar_selected_image = return_empty_string - self.set_sky = return_nil - self.get_sky = return_empty_table -- may need members on this table - - self.set_clouds = return_nil - self.get_clouds = return_value({density = 0, color = "#fff0f0e5", ambient = "#000000", height = 120, thickness = 16, speed = {x=0, y=-2}}) - - self.override_day_night_ratio = return_nil - self.get_day_night_ratio = return_nil - - self.set_local_animation = return_nil - self.get_local_animation = return_empty_table - - self.set_eye_offset = return_nil - self.get_eye_offset = return_value({x=0,y=0,z=0},{x=0,y=0,z=0}) - - return self -end \ No newline at end of file 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 deleted file mode 100644 index 47026fb..0000000 --- a/config.lua +++ /dev/null @@ -1,69 +0,0 @@ -local CONFIG_FILE_PREFIX = "digtron_" - -digtron.config = {} - -local print_settingtypes = false - -local function setting(stype, name, default, description) - local value - if stype == "bool" then - value = minetest.settings:get_bool(CONFIG_FILE_PREFIX..name) - elseif stype == "string" then - value = minetest.settings:get(CONFIG_FILE_PREFIX..name) - elseif stype == "int" or stype == "float" then - value = tonumber(minetest.settings:get(CONFIG_FILE_PREFIX..name)) - end - if value == nil then - value = default - end - digtron.config[name] = value - - if print_settingtypes then - minetest.debug(CONFIG_FILE_PREFIX..name.." ("..description..") "..stype.." "..tostring(default)) - end -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") -digtron.config.particle_effects = particle_effects or particle_effects == nil -- default true - - -setting("int", "maximum_extrusion", 25, "Maximum builder extrusion distance") -setting("float", "cycle_time", 1.0, "Minimum Digtron cycle time") -setting("float", "traction_factor", 3.0, "Traction factor") - --- fuel costs. For comparison, in the default game: --- one default tree block is 30 units --- one coal lump is 40 units --- one coal block is 370 units (apparently it's slightly more productive making your coal lumps into blocks before burning) --- 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") --- eg, stone -setting("float", "dig_cost_cracky", 1.0, "Cracky dig cost") --- eg, dirt, sand -setting("float", "dig_cost_crumbly", 0.5, "Crumbly dig cost") --- eg, wood -setting("float", "dig_cost_choppy", 0.75, "Choppy dig cost") --- how much fuel is required to build a node -setting("float", "build_cost", 1.0, "Build cost") - --- How much charge (an RE battery holds 10000 EU) is equivalent to 1 unit of heat produced by burning coal --- With 100, the battery is 2.5 better than a coal lump, yet 3.7 less powerful than a coal block --- Being rechargeable should pay off for this "average" performance. -setting("int", "power_ratio", 100, "The electrical charge to 1 coal heat unit conversion ratio") - - -setting("float", "marker_crate_good_duration", 3.0, "Duration that 'good' crate markers last") -setting("float", "marker_crate_bad_duration", 9.0, "Duration that 'bad' crate markers last") - -setting("bool", "emerge_unloaded_mapblocks", true, "When Digtron encounters unloaded map blocks, emerge them.") diff --git a/controller.lua b/controller.lua new file mode 100644 index 0000000..3c7c966 --- /dev/null +++ b/controller.lua @@ -0,0 +1,200 @@ +-- internationalization boilerplate +local MP = minetest.get_modpath(minetest.get_current_modname()) +local S, NS = dofile(MP.."/intllib.lua") + +local controller_nodebox = { + 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 + }, +} + +local get_controller_unconstructed_formspec = function(pos, player_name) + return "size[8,8]button[1,1;1,1;construct;Construct]" +end + +local get_controller_constructed_formspec = function(pos, digtron_id, player_name) + return "size[9,9]button[1,1;1,1;deconstruct;Deconstruct]" +end + +minetest.register_node("digtron:controller", { + description = S("Digtron Control Module"), + _doc_items_longdesc = nil, + _doc_items_usagehelp = nil, + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, + 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 = controller_nodebox, + +-- on_construct = function(pos) +-- end, + + on_dig = function(pos, node, digger) + local meta = minetest.get_meta(pos) + if meta:get("digtron_id") ~= nil then + return + else + return minetest.node_dig(pos, node, digger) + end + end, + + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + local meta = minetest.get_meta(pos) + local digtron_id = meta:get("digtron_id") + local player_name = clicker:get_player_name() + if digtron_id ~= "" then + minetest.show_formspec(player_name, + "digtron_controller_unconstructed:"..minetest.pos_to_string(pos)..":"..player_name, + get_controller_unconstructed_formspec(pos, player_name)) + else + -- initialized + minetest.show_formspec(player_name, + "digtron_controller_constructed:"..minetest.pos_to_string(pos)..":"..player_name..":"..digtron_id, + get_controller_construted_formspec(pos, digtron_id, player_name)) + end + end, + + on_timer = function(pos, elapsed) + end, +}) + +local cardinal_directions = { + {x=1,y=0,z=0}, + {x=-1,y=0,z=0}, + {x=0,y=1,z=0}, + {x=0,y=-1,z=0}, + {x=0,y=0,z=1}, + {x=0,y=0,z=-1}, +} +local origin_hash = minetest.hash_node_position({x=0,y=0,z=0}) + +local get_all_adjacent_digtron_nodes +get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, not_digtron) + for _, dir in ipairs(cardinal_directions) do + local test_pos = vector.add(pos, dir) + local test_hash = minetest.hash_node_position(test_pos) + if not (digtron_nodes[test_hash] or not_digtron[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 + digtron_nodes[test_hash] = test_node + get_all_adjacent_digtron_nodes(test_pos, digtron_nodes, not_digtron) -- recurse + else + not_digtron[test_hash] = test_node + end + end + end +end + + +digtron.construct = function(pos, player_name) + local node = minetest.get_node(pos) + if node.name ~= "digtron:controller" then + -- Called on an incorrect node + minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(pos) .. " but the node at this location was " .. node.name) + return nil + end + local meta = minetest.get_meta(pos) + if meta:get("digtron_id") ~= nil then + -- Already constructed. TODO: validate that the digtron_id actually exists as well + minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(pos) .. " but the controller at this location was already part of a constructed Digtron.") + return nil + end + local root_hash = minetest.hash_node_position(pos) + local digtron_nodes = {[root_hash] = node} + local not_digtron = {} + get_all_adjacent_digtron_nodes(pos, digtron_nodes, not_digtron) + for hash, node in pairs(digtron_nodes) do + local relative_hash = hash - root_hash + origin_hash + minetest.chat_send_all("constructing " .. minetest.pos_to_string(minetest.get_position_from_hash(relative_hash))) + local digtron_meta + if hash == root_hash then + digtron_meta = meta -- we're processing the controller, we already have a reference to its meta + else + digtron_meta = minetest.get_meta(minetest.get_position_from_hash(hash)) + end + + local meta_table = digtron_meta:to_table() + meta_table.node = node + -- Process inventories specially + -- Builder inventory gets turned into an itemname in a special key in the builder's meta + -- fuel and main get added to corresponding detached inventory lists + -- then wipe them from the meta_table. They'll be re-added in digtron.deconstruct. + --meta_table.inventory = nil + node.param1 = nil -- we don't care about param1, wipe it to save space + minetest.chat_send_all(dump(meta_table)) + end + + +end + + +-- Dealing with an unconstructed Digtron controller +minetest.register_on_player_receive_fields(function(player, formname, fields) + local formname_split = formname:split(":") + if #formname_split ~= 3 or formname_split[1] ~= "digtron_controller_unconstructed" then + return + end + local pos = minetest.string_to_pos(formname_split[2]) + if pos == nil then + minetest.log("error", "[Digtron] Unable to parse position from formspec name " .. formname) + return + end + local name = formname_split[3] + if player:get_player_name() ~= name then + return + end + + if fields.construct then + local digtron_id = digtron.construct(pos, name) + if digtron_id then + minetest.show_formspec(name, + "digtron_controller_constructed:"..minetest.pos_to_string(pos)..":"..name..":"..digtron_id, + get_controller_construted_formspec(pos, digtron_id, name)) + end + end + +end) + +-- Controlling a fully armed and operational Digtron +minetest.register_on_player_receive_fields(function(player, formname, fields) + local formname_split = formname:split(":") + if #formname_split ~= 4 or formname_split[1] ~= "digtron_controller_constructed" then + return + end + local pos = minetest.string_to_pos(formname_split[2]) + if pos == nil then + minetest.log("error", "[Digtron] Unable to parse position from formspec name " .. formname) + return + end + local name = formname_split[3] + if player:get_player_name() ~= name then + return + end + local digtron_id = formname_splot[4] + + if fields.deconstruct then + minetest.chat_send_all("Deconstructing " .. digtron_id) + end + +end) diff --git a/depends.txt b/depends.txt index c482b27..cad5160 100644 --- a/depends.txt +++ b/depends.txt @@ -1,8 +1,2 @@ default -pipeworks? -doc? -hopper? -awards? -catacomb? -intllib? -technic? \ No newline at end of file +intllib? \ 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 deleted file mode 100644 index 1a95cf6..0000000 --- a/entities.lua +++ /dev/null @@ -1,146 +0,0 @@ -minetest.register_entity("digtron:marker", { - initial_properties = { - visual = "cube", - visual_size = {x=1.05, y=1.05}, - textures = {"digtron_marker_side.png","digtron_marker_side.png","digtron_marker.png","digtron_marker.png","digtron_marker_side.png","digtron_marker_side.png"}, - collisionbox = {-0.525, -0.525, -0.525, 0.525, 0.525, 0.525}, - physical = false, - }, - - 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_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, - }, - - 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 = { - visual = "cube", - visual_size = {x=1.05, y=1.05}, - 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, - }, - - on_activate = function(self, staticdata) - minetest.after(digtron.config.marker_crate_good_duration, - 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_bad", { - initial_properties = { - visual = "cube", - visual_size = {x=1.05, y=1.05}, - 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, - }, - - on_activate = function(self, staticdata) - minetest.after(digtron.config.marker_crate_bad_duration, - 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:builder_item", { - - initial_properties = { - hp_max = 1, - is_visible = true, - visual = "wielditem", - visual_size = {x=0.25, y=0.25}, - collisionbox = {0,0,0,0,0,0}, - physical = false, - textures = {""}, - automatic_rotate = math.pi * 0.25, - }, - - 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 - self.object:remove() - return - end - props.textures = {staticdata} - self.object:set_properties(props) - elseif digtron.create_builder_item ~= nil then - props.textures = {digtron.create_builder_item} - self.object:set_properties(props) - digtron.create_builder_item = nil - else - self.object:remove() - end - end, - - get_staticdata = function(self) - local props = self.object:get_properties() - if props ~= nil and props.textures ~= nil and props.textures[1] ~= nil then - return props.textures[1] - end - return "" - end, -}) diff --git a/init.lua b/init.lua index 82de725..f543b1c 100644 --- a/init.lua +++ b/init.lua @@ -1,116 +1,8 @@ digtron = {} +digtron.doc = {} -digtron.auto_controller_colorize = "#88000030" -digtron.pusher_controller_colorize = "#00880030" -digtron.soft_digger_colorize = "#88880030" +local modpath = minetest.get_modpath(minetest.get_current_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 = { - ["default:torch_ceiling"] = "default:torch", - ["default:torch_wall"] = "default:torch", - ["default:dirt_with_grass"] = "default:dirt", - ["default:dirt_with_grass_footsteps"] = "default:dirt", - ["default:dirt_with_dry_grass"] = "default:dirt", - ["default:dirt_with_rainforest_litter"] = "default:dirt", - ["default:dirt_with_snow"] = "default:dirt", - ["default:furnace_active"] = "default:furnace", - ["farming:soil"] = "default:dirt", - ["farming:soil_wet"] = "default:dirt", - ["farming:desert_sand_soil"] = "default:desert_sand", - ["farming:desert_sand_soil_wet"] = "default:desert_sand", -} - --- 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. - -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 - ---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( digtron_modpath .. "/upgrades.lua" ) -- various LBMs for upgrading older versions of Digtron. - --- 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 --- 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 +dofile(modpath.."/controller.lua") +dofile(modpath.."/nodes/node_misc.lua") +dofile(modpath.."/nodes/node_storage.lua") \ 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/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 deleted file mode 100644 index 03cac2f..0000000 --- a/mod.conf +++ /dev/null @@ -1,7 +0,0 @@ -name = digtron -title = Digtron -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 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_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_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 deleted file mode 100644 index a4f87b3..0000000 --- a/nodes/node_duplicator.lua +++ /dev/null @@ -1,189 +0,0 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - -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.") .. "]" - -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") .. "]" -end - -minetest.register_node("digtron:duplicator", { - description = S("Digtron Duplicator"), - _doc_items_longdesc = digtron.doc.duplicator_longdesc, - _doc_items_usagehelp = digtron.doc.duplicator_usagehelp, - groups = {cracky = 3, oddly_breakable_by_hand=3}, - sounds = digtron.metal_sounds, - tiles = {"digtron_plate.png^(digtron_axel_side.png^[transformR90)", - "digtron_plate.png^(digtron_axel_side.png^[transformR270)", - "digtron_plate.png^digtron_axel_side.png", - "digtron_plate.png^(digtron_axel_side.png^[transformR180)", - "digtron_plate.png^digtron_builder.png", - "digtron_plate.png", - }, - paramtype = "light", - paramtype2= "facedir", - is_ground_content = false, - drawtype="nodebox", - node_box = { - type = "fixed", - fixed = { - {-0.5, 0.3125, 0.3125, 0.5, 0.5, 0.5}, -- FrontFrame_top - {-0.5, -0.5, 0.3125, 0.5, -0.3125, 0.5}, -- FrontFrame_bottom - {0.3125, -0.3125, 0.3125, 0.5, 0.3125, 0.5}, -- FrontFrame_right - {-0.5, -0.3125, 0.3125, -0.3125, 0.3125, 0.5}, -- FrontFrame_left - {-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 - {-0.4375, -0.4375, -0.4375, 0.4375, 0.4375, 0.3125}, -- Body - {-0.5, -0.3125, -0.5, -0.3125, 0.3125, -0.3125}, -- backframe_vertical - {0.3125, -0.3125, -0.5, 0.5, 0.3125, -0.3125}, -- backframe_left - {-0.5, 0.3125, -0.5, 0.5, 0.5, -0.3125}, -- backframe_top - {-0.5, -0.5, -0.5, 0.5, -0.3125, -0.3125}, -- backframe_bottom - {-0.0625, -0.0625, -0.5625, 0.0625, 0.0625, -0.4375}, -- back_probe - }, - }, - selection_box = { - type = "regular" - }, - - on_construct = function(pos) - local meta = minetest.get_meta(pos) - meta:set_string("formspec", inventory_formspec_string) - 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") - end, - - allow_metadata_inventory_put = function(pos, listname, index, stack, player) - if minetest.get_item_group(stack:get_name(), "digtron") > 0 then - return stack:get_count() - else - return 0 - end - 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.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")) - 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"))) - 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) - 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 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}) - 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..99608c7 100644 --- a/nodes/node_misc.lua +++ b/nodes/node_misc.lua @@ -8,7 +8,6 @@ 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, @@ -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", @@ -63,7 +61,6 @@ 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", @@ -86,7 +83,6 @@ 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", @@ -115,7 +111,6 @@ 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", 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..6aca684 100644 --- a/nodes/node_storage.lua +++ b/nodes/node_storage.lua @@ -2,23 +2,19 @@ local MP = minetest.get_modpath(minetest.get_current_modname()) local S, NS = dofile(MP.."/intllib.lua") -local pipeworks_path = minetest.get_modpath("pipeworks") +--local pipeworks_path = minetest.get_modpath("pipeworks") -local inventory_formspec_string = - "size[8,9.3]" .. +local get_inventory_formspec = function(pos, player_name) + return "size[8,9.3]" .. default.gui_bg .. default.gui_bg_img .. default.gui_slots .. "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,9 +23,7 @@ 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, paramtype2= "facedir", drawtype = "nodebox", @@ -52,7 +46,6 @@ 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, @@ -63,54 +56,62 @@ minetest.register_node("digtron:inventory", { return inv:is_empty("main") end, - -- Pipeworks compatibility - ---------------------------------------------------------------- + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + 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.chat_send_player(clicker:get_player_name(), "This Digtron is active, interact with its inventory via the controller node.") + end + 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)() + +-- -- 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]" .. +local get_fuelstore_formspec = function(pos, player_name) + return "size[8,9.3]" .. default.gui_bg .. default.gui_bg_img .. default.gui_slots .. "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, paramtype2= "facedir", drawtype = "nodebox", @@ -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, @@ -155,52 +155,63 @@ minetest.register_node("digtron:fuelstore", { local inv = meta:get_inventory() return inv:is_empty("fuel") 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)() -}) + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + 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_fuelstore:"..minetest.pos_to_string(pos)..":"..player_name, + get_fuelstore_formspec(pos, player_name)) + else + minetest.chat_send_player(clicker:get_player_name(), "This Digtron is active, interact with its inventory via the controller node.") + end + end, -local combined_storage_formspec_string = - "size[8,9.9]" .. +-- +-- -- 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 get_combined_formspec = function(pos, player_name) + return "size[8,9.9]" .. default.gui_bg .. default.gui_bg_img .. default.gui_slots .. "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,9 +219,7 @@ 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, paramtype2= "facedir", drawtype = "nodebox", @@ -231,7 +240,6 @@ 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) @@ -268,47 +276,60 @@ minetest.register_node("digtron:combined_storage", { local inv = meta:get_inventory() return inv:is_empty("fuel") and inv:is_empty("main") 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)() + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + 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.chat_send_player(clicker:get_player_name(), "This Digtron is active, interact with its inventory 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 +-- +---- 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 deleted file mode 100644 index 313c4a9..0000000 --- a/nodes/recipes.lua +++ /dev/null @@ -1,334 +0,0 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - -minetest.register_craftitem("digtron:digtron_core", { - description = S("Digtron Core"), - inventory_image = "digtron_core.png", - _doc_items_longdesc = digtron.doc.core_longdesc, - _doc_items_usagehelp = digtron.doc.core_usagehelp, -}) - -minetest.register_craft({ - output = "digtron:digtron_core", - recipe = { - {"","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"} - } -}) - -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",""} - } -}) - -minetest.register_craft({ - output = "digtron:light", - recipe = { - {"","default:torch",""}, - {"","digtron:digtron_core",""}, - {"","",""} - } -}) - -minetest.register_craft({ - output = "digtron:digger", - recipe = { - {"","default:diamond",""}, - {"default:diamond","digtron:digtron_core","default:diamond"}, - {"","default:diamond",""} - } -}) - -minetest.register_craft({ - output = "digtron:soft_digger", - recipe = { - {"","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",""}, - {"","",""} - } -}) - -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 - -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"}, -}) - -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"} - } -}) - -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"} - } -}) - -minetest.register_craft({ - output = "digtron:panel", - recipe = { - {"","",""}, - {"","digtron:digtron_core",""}, - {"","default:steel_ingot",""} - } -}) - -minetest.register_craft({ - output = "digtron:edge_panel", - recipe = { - {"","",""}, - {"","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"} - } -}) - --- 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", - type = "shapeless", - recipe = {"digtron:soft_digger", "digtron:soft_digger"}, -}) -minetest.register_craft({ - output = "digtron:dual_digger", - type = "shapeless", - recipe = {"digtron:digger", "digtron:digger"}, -}) -minetest.register_craft({ - output = "digtron:soft_digger 2", - recipe = { - {"digtron:dual_soft_digger"}, - } -}) -minetest.register_craft({ - output = "digtron:digger 2", - recipe = { - {"digtron:dual_digger"}, - } -}) - --- 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"}, - } -}) -minetest.register_craft({ - output = "digtron:digtron_core", - recipe = { - {"digtron:edge_panel"}, - } -}) - -minetest.register_craft({ - output = "digtron:digtron_core", - recipe = { - {"digtron:inventory"}, - } -}) - -minetest.register_craft({ - output = "digtron:digtron_core", - recipe = { - {"digtron:fuelstore"}, - } -}) - -minetest.register_craft({ - output = "digtron:digtron_core", - recipe = { - {"digtron:combined_storage"}, - } -}) - -minetest.register_craft({ - output = "digtron:digtron_core", - recipe = { - {"digtron:light"}, - } -}) - -minetest.register_craft({ - output = "digtron:digtron_core", - recipe = { - {"digtron:pusher"}, - } -}) - -minetest.register_craft({ - output = "digtron:digtron_core", - recipe = { - {"digtron:axle"}, - } -}) \ No newline at end of file diff --git a/screenshot.png b/screenshot.png deleted file mode 100644 index 1226a6bb4942ae8cd49d20566ade12039636b572..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59062 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 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 deleted file mode 100644 index ba6ff37..0000000 --- a/util_item_place_node.lua +++ /dev/null @@ -1,181 +0,0 @@ --- The default minetest.item_place_node from item.lua was hard to work with given some of the details --- of how it handled pointed_thing. It also didn't work right with default:torch and seeds. It was simpler to --- just copy it here and chop out the special cases that were causing problems, and add some special handling. --- for nodes that define on_place - --- This specific file is therefore licensed under the LGPL 2.1 - ---GNU Lesser General Public License, version 2.1 ---Copyright (C) 2011-2016 celeron55, Perttu Ahola ---Copyright (C) 2011-2016 Various Minetest developers and contributors - ---This program is free software; you can redistribute it and/or modify it under the terms ---of the GNU Lesser General Public License as published by the Free Software Foundation; ---either version 2.1 of the License, or (at your option) any later version. - ---This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; ---without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ---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 - -digtron.whitelisted_on_place = function (item_name) - for listed_item, value in pairs(digtron.builder_on_place_items) do - if item_name == listed_item then return value end - end - - for prefix, value in pairs(digtron.builder_on_place_prefixes) do - if has_prefix(item_name, prefix) then return value end - end - - if minetest.get_item_group(item_name, "digtron_on_place") > 0 then return true end - - return false -end - -local function copy_pointed_thing(pointed_thing) - return { - type = pointed_thing.type, - above = vector.new(pointed_thing.above), - under = vector.new(pointed_thing.under), - } -end - -local function check_attached_node(p, n) - local def = minetest.registered_nodes[n.name] - local d = {x = 0, y = 0, z = 0} - if def.paramtype2 == "wallmounted" then - -- The fallback vector here is in case 'wallmounted to dir' is nil due - -- to voxelmanip placing a wallmounted node without resetting a - -- pre-existing param2 value that is out-of-range for wallmounted. - -- The fallback vector corresponds to param2 = 0. - d = minetest.wallmounted_to_dir(n.param2) or {x = 0, y = 1, z = 0} - else - d.y = -1 - end - local p2 = vector.add(p, d) - local nn = minetest.get_node(p2).name - local def2 = minetest.registered_nodes[nn] - if def2 and not def2.walkable then - return false - end - return true -end - -digtron.item_place_node = function(itemstack, placer, place_to, param2) - local item_name = itemstack:get_name() - local def = itemstack:get_definition() - if (not def) or (param2 < 0) or (def.paramtype2 == "wallmounted" and param2 > 5) or (param2 > 23) then -- validate parameters - return itemstack, false - end - - local pointed_thing = {} - pointed_thing.type = "node" - pointed_thing.above = {x=place_to.x, y=place_to.y, z=place_to.z} - pointed_thing.under = {x=place_to.x, y=place_to.y - 1, z=place_to.z} - - -- Handle node-specific on_place calls as best we can. - if def.on_place and def.on_place ~= minetest.nodedef_default.on_place and digtron.whitelisted_on_place(item_name) then - if def.paramtype2 == "facedir" then - pointed_thing.under = vector.add(place_to, minetest.facedir_to_dir(param2)) - elseif def.paramtype2 == "wallmounted" then - pointed_thing.under = vector.add(place_to, minetest.wallmounted_to_dir(param2)) - end - - -- pass a copy of the item stack parameter because on_place might modify it directly and then we can't tell if we succeeded or not - -- though note that some mods do "creative_mode" handling within their own on_place methods, which makes it impossible for Digtron - -- to know what to do in that case - if you're in creative_mode Digtron will place such items but it will think it failed and not - -- deduct them from inventory no matter what Digtron's settings are. Unfortunate, but not very harmful and I have no workaround. - local returnstack, success = def.on_place(ItemStack(itemstack), placer, pointed_thing) - if returnstack and returnstack:get_count() < itemstack:get_count() then success = true end -- some mods neglect to return a success condition - if success then - -- Override the param2 value to force it to be what Digtron wants - local placed_node = minetest.get_node(place_to) - placed_node.param2 = param2 - minetest.set_node(place_to, placed_node) - end - - return returnstack, success - end - - if minetest.registered_nodes[item_name] == nil then - -- Permitted craft items are handled by the node-specific on_place call, above. - -- if we are a craft item and we get here then we're not whitelisted and we should fail. - -- Note that builder settings should be filtering out craft items like this before we get here, - -- but this will protect us just in case. - return itemstack, false - end - - local oldnode = minetest.get_node_or_nil(place_to) - - --this should never happen, digtron is testing for adjacent unloaded nodes before getting here. - if not oldnode then - minetest.log("info", placer:get_player_name() .. " tried to place" - .. " node in unloaded position " .. minetest.pos_to_string(place_to) - .. " using a digtron.") - return itemstack, false - end - - local newnode = {name = def.name, param1 = 0, param2 = param2} - if def.place_param2 ~= nil then - newnode.param2 = def.place_param2 - end - - -- Check if the node is attached and if it can be placed there - if minetest.get_item_group(def.name, "attached_node") ~= 0 and - not check_attached_node(place_to, newnode) then - minetest.log("action", "attached node " .. def.name .. - " can not be placed at " .. minetest.pos_to_string(place_to)) - return itemstack, false - end - - -- Add node and update - minetest.add_node(place_to, newnode) - - local take_item = true - - -- Run callback, using genuine player for per-node definition. - if def.after_place_node then - -- Deepcopy place_to and pointed_thing because callback can modify it - local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z} - local pointed_thing_copy = copy_pointed_thing(pointed_thing) - if def.after_place_node(place_to_copy, placer, itemstack, - pointed_thing_copy) then - take_item = false - end - end - - -- Run script hook, using fake_player to take the blame. - -- Note that fake_player:update is called in the DigtronLayout class's "create" function, - -- which is called before Digtron does any of this building stuff, so it's not necessary - -- to update it here. - local _, callback - for _, callback in ipairs(minetest.registered_on_placenodes) do - -- Deepcopy pos, node and pointed_thing because callback can modify them - local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z} - local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2} - local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2} - local pointed_thing_copy = copy_pointed_thing(pointed_thing) - if callback(place_to_copy, newnode_copy, digtron.fake_player, oldnode_copy, itemstack, pointed_thing_copy) then - take_item = false - end - end - - if take_item then - itemstack:take_item() - end - return itemstack, true -end \ No newline at end of file From 4e2ac43b2db011252c3c7b8f1280d8cd60391eed Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sat, 17 Aug 2019 23:12:12 -0600 Subject: [PATCH 02/63] can now create detached inventory. May be very buggy yet. --- controller.lua | 98 +++++----------------- functions.lua | 186 +++++++++++++++++++++++++++++++++++++++++ init.lua | 1 + nodes/node_storage.lua | 3 +- 4 files changed, 212 insertions(+), 76 deletions(-) create mode 100644 functions.lua diff --git a/controller.lua b/controller.lua index 3c7c966..8035bbe 100644 --- a/controller.lua +++ b/controller.lua @@ -23,7 +23,10 @@ local get_controller_unconstructed_formspec = function(pos, player_name) end local get_controller_constructed_formspec = function(pos, digtron_id, player_name) + local digtron_id_name = digtron.get_digtron_id_name(digtron_id) return "size[9,9]button[1,1;1,1;deconstruct;Deconstruct]" + .. "list[detached:" .. digtron_id_name .. ";main;1,2;8,2]" -- TODO: paging system for inventory + .. "list[detached:" .. digtron_id_name .. ";fuel;1,7;8,2]" -- TODO: paging system for inventory end minetest.register_node("digtron:controller", { @@ -60,9 +63,9 @@ minetest.register_node("digtron:controller", { on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) local meta = minetest.get_meta(pos) - local digtron_id = meta:get("digtron_id") + local digtron_id = meta:get_int("digtron_id") local player_name = clicker:get_player_name() - if digtron_id ~= "" then + if digtron_id == 0 then minetest.show_formspec(player_name, "digtron_controller_unconstructed:"..minetest.pos_to_string(pos)..":"..player_name, get_controller_unconstructed_formspec(pos, player_name)) @@ -70,7 +73,7 @@ minetest.register_node("digtron:controller", { -- initialized minetest.show_formspec(player_name, "digtron_controller_constructed:"..minetest.pos_to_string(pos)..":"..player_name..":"..digtron_id, - get_controller_construted_formspec(pos, digtron_id, player_name)) + get_controller_constructed_formspec(pos, digtron_id, player_name)) end end, @@ -78,75 +81,6 @@ minetest.register_node("digtron:controller", { end, }) -local cardinal_directions = { - {x=1,y=0,z=0}, - {x=-1,y=0,z=0}, - {x=0,y=1,z=0}, - {x=0,y=-1,z=0}, - {x=0,y=0,z=1}, - {x=0,y=0,z=-1}, -} -local origin_hash = minetest.hash_node_position({x=0,y=0,z=0}) - -local get_all_adjacent_digtron_nodes -get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, not_digtron) - for _, dir in ipairs(cardinal_directions) do - local test_pos = vector.add(pos, dir) - local test_hash = minetest.hash_node_position(test_pos) - if not (digtron_nodes[test_hash] or not_digtron[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 - digtron_nodes[test_hash] = test_node - get_all_adjacent_digtron_nodes(test_pos, digtron_nodes, not_digtron) -- recurse - else - not_digtron[test_hash] = test_node - end - end - end -end - - -digtron.construct = function(pos, player_name) - local node = minetest.get_node(pos) - if node.name ~= "digtron:controller" then - -- Called on an incorrect node - minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(pos) .. " but the node at this location was " .. node.name) - return nil - end - local meta = minetest.get_meta(pos) - if meta:get("digtron_id") ~= nil then - -- Already constructed. TODO: validate that the digtron_id actually exists as well - minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(pos) .. " but the controller at this location was already part of a constructed Digtron.") - return nil - end - local root_hash = minetest.hash_node_position(pos) - local digtron_nodes = {[root_hash] = node} - local not_digtron = {} - get_all_adjacent_digtron_nodes(pos, digtron_nodes, not_digtron) - for hash, node in pairs(digtron_nodes) do - local relative_hash = hash - root_hash + origin_hash - minetest.chat_send_all("constructing " .. minetest.pos_to_string(minetest.get_position_from_hash(relative_hash))) - local digtron_meta - if hash == root_hash then - digtron_meta = meta -- we're processing the controller, we already have a reference to its meta - else - digtron_meta = minetest.get_meta(minetest.get_position_from_hash(hash)) - end - - local meta_table = digtron_meta:to_table() - meta_table.node = node - -- Process inventories specially - -- Builder inventory gets turned into an itemname in a special key in the builder's meta - -- fuel and main get added to corresponding detached inventory lists - -- then wipe them from the meta_table. They'll be re-added in digtron.deconstruct. - --meta_table.inventory = nil - node.param1 = nil -- we don't care about param1, wipe it to save space - minetest.chat_send_all(dump(meta_table)) - end - - -end -- Dealing with an unconstructed Digtron controller @@ -168,12 +102,14 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if fields.construct then local digtron_id = digtron.construct(pos, name) if digtron_id then + local meta = minetest.get_meta(pos) + meta:set_int("digtron_id", digtron_id) minetest.show_formspec(name, "digtron_controller_constructed:"..minetest.pos_to_string(pos)..":"..name..":"..digtron_id, - get_controller_construted_formspec(pos, digtron_id, name)) + get_controller_constructed_formspec(pos, digtron_id, name)) end end - + end) -- Controlling a fully armed and operational Digtron @@ -191,10 +127,22 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if player:get_player_name() ~= name then return end - local digtron_id = formname_splot[4] + local digtron_id = formname_split[4] if fields.deconstruct then minetest.chat_send_all("Deconstructing " .. digtron_id) + + local meta = minetest.get_meta(pos) + local digtron_id = meta:get_int("digtron_id") + if digtron_id == 0 then + minetest.log("error", "[Digtron] tried to deconstruct Digtron at pos " + .. minetest.pos_to_string(pos) .. " but it had no digtron_id in the node's metadata") + else + digtron.deconstruct(digtron_id, pos, name) + minetest.show_formspec(name, + "digtron_controller_unconstructed:"..minetest.pos_to_string(pos)..":"..name, + get_controller_unconstructed_formspec(pos, name)) + end end end) diff --git a/functions.lua b/functions.lua new file mode 100644 index 0000000..fffda03 --- /dev/null +++ b/functions.lua @@ -0,0 +1,186 @@ +local mod_meta = minetest.get_mod_storage() + +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" + local stack = inv:get_stack(from_list, from_index) + if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then + return stack:get_count() + 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), +-- on_put = function(inv, listname, index, stack, player), +-- on_take = function(inv, listname, index, stack, player), + } + +digtron.get_digtron_id_name = function(id) + return "digtron_id_" .. tostring(id) +end + +local create_new_id = function(pos) + local last_id = mod_meta:get_int("last_id") -- returns 0 when uninitialized, so 0 will never be a valid digtron_id. + local new_id = last_id + 1 + mod_meta:set_int("last_id", new_id) -- ensure each call to this method gets a unique number + + local digtron_id_name = digtron.get_digtron_id_name(new_id) + + mod_meta:set_string(digtron_id_name, minetest.pos_to_string(pos)) -- record that this digtron exists + local inv = minetest.create_detached_inventory(digtron_id_name, detached_inventory_callbacks) + + return new_id, inv +end + +-- Deletes a Digtron record. Note: throws everything away, this is not digtron.deconstruct. +local dispose_id = function(id) + local digtron_id_name = digtron.get_digtron_id_name(id) + minetest.remove_detached_inventory(digtron_id_name) + mod_meta:set_string(digtron_id_name, "") +end + + +local cardinal_directions = { + {x=1,y=0,z=0}, + {x=-1,y=0,z=0}, + {x=0,y=1,z=0}, + {x=0,y=-1,z=0}, + {x=0,y=0,z=1}, + {x=0,y=0,z=-1}, +} +local origin_hash = minetest.hash_node_position({x=0,y=0,z=0}) + +-- recursive function searches out all connected unassigned digtron nodes +local get_all_adjacent_digtron_nodes +get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, not_digtron, player_name) + for _, dir in ipairs(cardinal_directions) do + local test_pos = vector.add(pos, dir) + local test_hash = minetest.hash_node_position(test_pos) + if not (digtron_nodes[test_hash] or not_digtron[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 + not_digtron[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_adjacent_digtron_nodes(test_pos, digtron_nodes, not_digtron, player_name) -- recurse + end + else + -- don't record details, the content of this node will change as the digtron moves + not_digtron[test_hash] = true + end + end + end +end + +-- Returns the id of the new Digtron record, or nil on failure +digtron.construct = function(pos, player_name) + local node = minetest.get_node(pos) + -- TODO: a more generic test? Not needed with the more generic controller design, as far as I can tell + if node.name ~= "digtron:controller" then + -- Called on an incorrect node + minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(pos) + .. " but the node at this location was " .. node.name) + return nil + end + local meta = minetest.get_meta(pos) + if meta:contains("digtron_id") then + -- Already constructed. TODO: validate that the digtron_id actually exists as well + minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(pos) + .. " but the controller at this location was already part of a constructed Digtron.") + return nil + end + local root_hash = minetest.hash_node_position(pos) + local digtron_nodes = {[root_hash] = node} -- Nodes that are part of Digtron + local not_digtron = {} -- Nodes that are adjacent to Digtron but not a part of it + get_all_adjacent_digtron_nodes(pos, digtron_nodes, not_digtron, player_name) + + local digtron_id, digtron_inv = create_new_id(pos) + + local layout = {} + + for hash, node in pairs(digtron_nodes) do + local relative_hash = hash - root_hash + origin_hash + minetest.chat_send_all("constructing " .. minetest.pos_to_string(minetest.get_position_from_hash(relative_hash))) + local digtron_meta + if hash == root_hash then + digtron_meta = meta -- we're processing the controller, we already have a reference to its meta + else + digtron_meta = minetest.get_meta(minetest.get_position_from_hash(hash)) + end + + local meta_table = digtron_meta:to_table() + + if meta_table.fields.digtron_id then + -- Trying to incorporate part of an existing digtron, should be impossible. + minetest.log("error", "[Digtron] digtron.construct 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 " .. meta_table.fields.digtron_id) + dispose_id(digtron_id) + return nil + end + -- Process inventories specially + -- TODO Builder inventory gets turned into an itemname in a special key in the builder's meta + -- fuel and main get added to corresponding detached inventory lists + -- then wipe them from the meta_table. They'll be re-added in digtron.deconstruct. + for listname, items in pairs(meta_table.inventory) do + local count = #items + -- increase the corresponding detached inventory size + minetest.chat_send_all("adding " .. count .. " to size of " .. listname) + digtron_inv:set_size(listname, digtron_inv:get_size(listname) + count) + for _, stack in ipairs(items) do + digtron_inv:add_item(listname, stack) + end + end + + node.param1 = nil -- we don't care about param1, wipe it to save space + layout[relative_hash] = {meta = meta_table.fields, node = node} + end + + minetest.debug("constructed id " .. digtron_id .. ": " .. minetest.serialize(layout)) + return digtron_id +end + +-- TODO: skeletal! +digtron.deconstruct = function(digtron_id, pos, name) + dispose_id(digtron_id) + local meta = minetest.get_meta(pos) + meta:set_string("digtron_id", "") +end \ No newline at end of file diff --git a/init.lua b/init.lua index f543b1c..c7c5765 100644 --- a/init.lua +++ b/init.lua @@ -3,6 +3,7 @@ digtron.doc = {} local modpath = minetest.get_modpath(minetest.get_current_modname()) +dofile(modpath.."/functions.lua") dofile(modpath.."/controller.lua") dofile(modpath.."/nodes/node_misc.lua") dofile(modpath.."/nodes/node_storage.lua") \ No newline at end of file diff --git a/nodes/node_storage.lua b/nodes/node_storage.lua index 6aca684..2cb2bbb 100644 --- a/nodes/node_storage.lua +++ b/nodes/node_storage.lua @@ -245,8 +245,9 @@ minetest.register_node("digtron:combined_storage", { 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() From 90c23159d3ebd981ca53e85d09b0efba577abc85 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 18 Aug 2019 02:23:08 -0600 Subject: [PATCH 03/63] persisting inventory between runs --- controller.lua | 17 +++---- functions.lua | 121 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 112 insertions(+), 26 deletions(-) diff --git a/controller.lua b/controller.lua index 8035bbe..0798cd2 100644 --- a/controller.lua +++ b/controller.lua @@ -22,8 +22,9 @@ local get_controller_unconstructed_formspec = function(pos, player_name) return "size[8,8]button[1,1;1,1;construct;Construct]" end -local get_controller_constructed_formspec = function(pos, digtron_id, player_name) - local digtron_id_name = digtron.get_digtron_id_name(digtron_id) +local get_controller_constructed_formspec = function(pos, digtron_id_name, player_name) + digtron.ensure_inventory_exists(digtron_id_name) + return "size[9,9]button[1,1;1,1;deconstruct;Deconstruct]" .. "list[detached:" .. digtron_id_name .. ";main;1,2;8,2]" -- TODO: paging system for inventory .. "list[detached:" .. digtron_id_name .. ";fuel;1,7;8,2]" -- TODO: paging system for inventory @@ -54,7 +55,7 @@ minetest.register_node("digtron:controller", { on_dig = function(pos, node, digger) local meta = minetest.get_meta(pos) - if meta:get("digtron_id") ~= nil then + if meta:get_string("digtron_id") ~= "" then return else return minetest.node_dig(pos, node, digger) @@ -63,9 +64,9 @@ minetest.register_node("digtron:controller", { on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) local meta = minetest.get_meta(pos) - local digtron_id = meta:get_int("digtron_id") + local digtron_id = meta:get_string("digtron_id") local player_name = clicker:get_player_name() - if digtron_id == 0 then + if digtron_id == "" then minetest.show_formspec(player_name, "digtron_controller_unconstructed:"..minetest.pos_to_string(pos)..":"..player_name, get_controller_unconstructed_formspec(pos, player_name)) @@ -103,7 +104,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local digtron_id = digtron.construct(pos, name) if digtron_id then local meta = minetest.get_meta(pos) - meta:set_int("digtron_id", digtron_id) + meta:set_string("digtron_id", digtron_id) minetest.show_formspec(name, "digtron_controller_constructed:"..minetest.pos_to_string(pos)..":"..name..":"..digtron_id, get_controller_constructed_formspec(pos, digtron_id, name)) @@ -133,8 +134,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) minetest.chat_send_all("Deconstructing " .. digtron_id) local meta = minetest.get_meta(pos) - local digtron_id = meta:get_int("digtron_id") - if digtron_id == 0 then + local digtron_id = meta:get_string("digtron_id") + if digtron_id == "" then minetest.log("error", "[Digtron] tried to deconstruct Digtron at pos " .. minetest.pos_to_string(pos) .. " but it had no digtron_id in the node's metadata") else diff --git a/functions.lua b/functions.lua index fffda03..7558a9a 100644 --- a/functions.lua +++ b/functions.lua @@ -1,5 +1,11 @@ local mod_meta = minetest.get_mod_storage() +------------------------------------------------------------------------------------ +-- Inventory + +-- indexed by digtron_id_name, 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. @@ -42,35 +48,96 @@ local detached_inventory_callbacks = { -- 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), --- on_put = function(inv, listname, index, stack, player), --- on_take = function(inv, listname, index, stack, player), + 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, } -digtron.get_digtron_id_name = function(id) - return "digtron_id_" .. tostring(id) +-- 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 +digtron.ensure_inventory_exists = function(digtron_id_name) + local inv = minetest.get_inventory({type="detached", name=digtron_id_name}) + if inv == nil then + inv = minetest.create_detached_inventory(digtron_id_name, detached_inventory_callbacks) + local inv_string = mod_meta:get_string("inv_"..digtron_id_name) + 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 +end + +local persist_inventory = function(digtron_id_name) + local inv = minetest.get_inventory({type="detached", name=digtron_id_name}) + if inv == nil then + minetest.log("error", "[Digtron] persist_inventory attempted to record a nonexistent inventory " + .. digtron_id_name) + 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("inv_"..digtron_id_name, minetest.serialize(persist)) end -local create_new_id = function(pos) +minetest.register_globalstep(function(dtime) + for digtron_id_name, _ in pairs(dirty_inventories) do + persist_inventory(digtron_id_name) + dirty_inventories[digtron_id_name] = nil + end +end) + +-------------------------------------------------------------------------------------- + +local create_new_id = function() local last_id = mod_meta:get_int("last_id") -- returns 0 when uninitialized, so 0 will never be a valid digtron_id. local new_id = last_id + 1 mod_meta:set_int("last_id", new_id) -- ensure each call to this method gets a unique number - local digtron_id_name = digtron.get_digtron_id_name(new_id) - - mod_meta:set_string(digtron_id_name, minetest.pos_to_string(pos)) -- record that this digtron exists + local digtron_id_name = "digtron_id_" .. tostring(new_id) local inv = minetest.create_detached_inventory(digtron_id_name, detached_inventory_callbacks) - return new_id, inv + return digtron_id_name, inv end --- Deletes a Digtron record. Note: throws everything away, this is not digtron.deconstruct. -local dispose_id = function(id) - local digtron_id_name = digtron.get_digtron_id_name(id) +-- Deletes a Digtron record. Note: just throws everything away, this is not digtron.deconstruct. +local dispose_id = function(digtron_id_name) minetest.remove_detached_inventory(digtron_id_name) - mod_meta:set_string(digtron_id_name, "") + mod_meta:set_string("inv_"..digtron_id_name, "") + mod_meta:set_string("layout_"..digtron_id_name, "") +end + +------------------------------------------------------------------------------------------------------- + +local persist_layout = function(digtron_id_name, layout) + mod_meta:set_string("layout_"..digtron_id_name, minetest.serialize(layout)) +end + +local retrieve_layout = function(digtron_id_name) + local layout_string = mod_meta:get_string("layout_"..digtron_id_name) + if layout_string ~= "" then + return minetest.deserialize(layout_string) + end end +-------------------------------------------------------------------------------------------------------- local cardinal_directions = { {x=1,y=0,z=0}, @@ -159,7 +226,6 @@ digtron.construct = function(pos, player_name) -- Process inventories specially -- TODO Builder inventory gets turned into an itemname in a special key in the builder's meta -- fuel and main get added to corresponding detached inventory lists - -- then wipe them from the meta_table. They'll be re-added in digtron.deconstruct. for listname, items in pairs(meta_table.inventory) do local count = #items -- increase the corresponding detached inventory size @@ -174,13 +240,32 @@ digtron.construct = function(pos, player_name) layout[relative_hash] = {meta = meta_table.fields, node = node} end - minetest.debug("constructed id " .. digtron_id .. ": " .. minetest.serialize(layout)) + persist_inventory(digtron_id) + persist_layout(digtron_id, layout) + + -- 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. + for hash, node in pairs(digtron_nodes) do + local digtron_meta + if hash == root_hash then + digtron_meta = meta -- we're processing the controller, we already have a reference to its meta + else + digtron_meta = minetest.get_meta(minetest.get_position_from_hash(hash)) + end + local inv = digtron_meta:get_inventory() + + -- TODO: wipe + end + + minetest.debug("constructed id " .. digtron_id) return digtron_id end -- TODO: skeletal! digtron.deconstruct = function(digtron_id, pos, name) - dispose_id(digtron_id) local meta = minetest.get_meta(pos) - meta:set_string("digtron_id", "") + + --TODO: go through layout and distribute inventory + + dispose_id(digtron_id) end \ No newline at end of file From ff229185b2954c84853e7402daedd9e6ff5244da Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 18 Aug 2019 13:02:42 -0600 Subject: [PATCH 04/63] can now reliably construct and deconstruct --- controller.lua | 13 ++- functions.lua | 227 ++++++++++++++++++++++++++++------------- nodes/node_misc.lua | 6 +- nodes/node_storage.lua | 6 +- 4 files changed, 172 insertions(+), 80 deletions(-) diff --git a/controller.lua b/controller.lua index 0798cd2..cc3cff2 100644 --- a/controller.lua +++ b/controller.lua @@ -23,11 +23,14 @@ local get_controller_unconstructed_formspec = function(pos, player_name) end local get_controller_constructed_formspec = function(pos, digtron_id_name, player_name) - digtron.ensure_inventory_exists(digtron_id_name) + digtron.retrieve_inventory(digtron_id_name) - return "size[9,9]button[1,1;1,1;deconstruct;Deconstruct]" - .. "list[detached:" .. digtron_id_name .. ";main;1,2;8,2]" -- TODO: paging system for inventory - .. "list[detached:" .. digtron_id_name .. ";fuel;1,7;8,2]" -- TODO: paging system for inventory + return "size[9,9]button[1,0;1,1;deconstruct;Deconstruct]" + .. "list[detached:" .. digtron_id_name .. ";main;1,1;8,2]" -- TODO: paging system for inventory + .. "list[detached:" .. digtron_id_name .. ";fuel;1,3.5;8,2]" -- TODO: paging system for inventory + .."container[1,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_name .. ";main]" end minetest.register_node("digtron:controller", { @@ -56,7 +59,7 @@ minetest.register_node("digtron:controller", { on_dig = function(pos, node, digger) local meta = minetest.get_meta(pos) if meta:get_string("digtron_id") ~= "" then - return + return -- TODO: special handling here! else return minetest.node_dig(pos, node, digger) end diff --git a/functions.lua b/functions.lua index 7558a9a..3a78706 100644 --- a/functions.lua +++ b/functions.lua @@ -1,5 +1,8 @@ local mod_meta = minetest.get_mod_storage() +digtron.layout = {} +digtron.adjacent = {} + ------------------------------------------------------------------------------------ -- Inventory @@ -7,61 +10,61 @@ local mod_meta = minetest.get_mod_storage() 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" - local stack = inv:get_stack(from_list, from_index) + -- 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" + local stack = inv:get_stack(from_list, from_index) + if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then + return stack:get_count() + 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 - 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, + 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 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, - } + -- 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 -digtron.ensure_inventory_exists = function(digtron_id_name) +digtron.retrieve_inventory = function(digtron_id_name) local inv = minetest.get_inventory({type="detached", name=digtron_id_name}) if inv == nil then inv = minetest.create_detached_inventory(digtron_id_name, detached_inventory_callbacks) @@ -74,8 +77,10 @@ digtron.ensure_inventory_exists = function(digtron_id_name) end end end + return inv end +-- Stores contents of detached inventory as a metadata string local persist_inventory = function(digtron_id_name) local inv = minetest.get_inventory({type="detached", name=digtron_id_name}) if inv == nil then @@ -120,24 +125,44 @@ end -- Deletes a Digtron record. Note: just throws everything away, this is not digtron.deconstruct. local dispose_id = function(digtron_id_name) minetest.remove_detached_inventory(digtron_id_name) + digtron.layout[digtron_id_name] = nil + digtron.adjacent[digtron_id_name] = nil mod_meta:set_string("inv_"..digtron_id_name, "") mod_meta:set_string("layout_"..digtron_id_name, "") + mod_meta:set_string("adjacent_"..digtron_id_name, "") end ------------------------------------------------------------------------------------------------------- +-- Layout -local persist_layout = function(digtron_id_name, layout) - mod_meta:set_string("layout_"..digtron_id_name, minetest.serialize(layout)) +local get_persist_table_function = function(identifier) + return function(digtron_id_name, tbl) + mod_meta:set_string(identifier .."_"..digtron_id_name, minetest.serialize(tbl)) + digtron[identifier][digtron_id_name] = tbl + end end -local retrieve_layout = function(digtron_id_name) - local layout_string = mod_meta:get_string("layout_"..digtron_id_name) - if layout_string ~= "" then - return minetest.deserialize(layout_string) +local get_retrieve_table_function = function(identifier) + return function(digtron_id_name) + local current = digtron[identifier][digtron_id_name] + if current then + return current + end + local tbl_string = mod_meta:get_string(identifier.."_"..digtron_id_name) + if tbl_string ~= "" then + current = minetest.deserialize(tbl_string) + if current then + digtron[identifier][digtron_id_name] = current + end + return current + end end end --------------------------------------------------------------------------------------------------------- +local persist_layout = get_persist_table_function("layout") +local retrieve_layout = get_retrieve_table_function("layout") +local persist_adjacent = get_persist_table_function("adjacent") +local retrieve_adjacent = get_retrieve_table_function("adjacent") local cardinal_directions = { {x=1,y=0,z=0}, @@ -147,36 +172,40 @@ local cardinal_directions = { {x=0,y=0,z=1}, {x=0,y=0,z=-1}, } -local origin_hash = minetest.hash_node_position({x=0,y=0,z=0}) -- recursive function searches out all connected unassigned digtron nodes local get_all_adjacent_digtron_nodes -get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, not_digtron, player_name) +get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, digtron_adjacent, player_name) for _, dir in ipairs(cardinal_directions) do local test_pos = vector.add(pos, dir) local test_hash = minetest.hash_node_position(test_pos) - if not (digtron_nodes[test_hash] or not_digtron[test_hash]) then -- don't test twice + 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 - not_digtron[test_hash] = true + 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_adjacent_digtron_nodes(test_pos, digtron_nodes, not_digtron, player_name) -- recurse + get_all_adjacent_digtron_nodes(test_pos, digtron_nodes, digtron_adjacent, player_name) -- recurse end else -- don't record details, the content of this node will change as the digtron moves - not_digtron[test_hash] = true + digtron_adjacent[test_hash] = true end end end end +-------------------------------------------------------------------------------------------------------- +-- Construct and deconstruct + +local origin_hash = minetest.hash_node_position({x=0,y=0,z=0}) + -- Returns the id of the new Digtron record, or nil on failure digtron.construct = function(pos, player_name) local node = minetest.get_node(pos) @@ -196,8 +225,8 @@ digtron.construct = function(pos, player_name) end local root_hash = minetest.hash_node_position(pos) local digtron_nodes = {[root_hash] = node} -- Nodes that are part of Digtron - local not_digtron = {} -- Nodes that are adjacent to Digtron but not a part of it - get_all_adjacent_digtron_nodes(pos, digtron_nodes, not_digtron, player_name) + local digtron_adjacent = {} -- Nodes that are adjacent to Digtron but not a part of it + get_all_adjacent_digtron_nodes(pos, digtron_nodes, digtron_adjacent, player_name) local digtron_id, digtron_inv = create_new_id(pos) @@ -205,7 +234,6 @@ digtron.construct = function(pos, player_name) for hash, node in pairs(digtron_nodes) do local relative_hash = hash - root_hash + origin_hash - minetest.chat_send_all("constructing " .. minetest.pos_to_string(minetest.get_position_from_hash(relative_hash))) local digtron_meta if hash == root_hash then digtron_meta = meta -- we're processing the controller, we already have a reference to its meta @@ -229,19 +257,22 @@ digtron.construct = function(pos, player_name) for listname, items in pairs(meta_table.inventory) do local count = #items -- increase the corresponding detached inventory size - minetest.chat_send_all("adding " .. count .. " to size of " .. listname) 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 + meta_table.inventory[listname] = #items end - + node.param1 = nil -- we don't care about param1, wipe it to save space - layout[relative_hash] = {meta = meta_table.fields, node = node} + layout[relative_hash] = {meta = meta_table, node = node} end persist_inventory(digtron_id) persist_layout(digtron_id, layout) + persist_adjacent(digtron_id, digtron_adjacent) -- 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. @@ -254,18 +285,72 @@ digtron.construct = function(pos, player_name) end local inv = digtron_meta:get_inventory() - -- TODO: wipe + for listname, items in pairs(inv:get_lists()) do + for i = 1, #items do + inv:set_stack(listname, i, ItemStack("")) + end + end + + digtron_meta:set_string("digtron_id", digtron_id) end minetest.debug("constructed id " .. digtron_id) return digtron_id end --- TODO: skeletal! digtron.deconstruct = function(digtron_id, pos, name) - local meta = minetest.get_meta(pos) + --local meta = minetest.get_meta(pos) + local layout = retrieve_layout(digtron_id) + local inv = digtron.retrieve_inventory(digtron_id) + local root_hash = minetest.hash_node_position(pos) - --TODO: go through layout and distribute inventory + -- Write metadata and inventory to in-world node at this location + for hash, data in pairs(layout) do + local ipos = minetest.get_position_from_hash(hash + root_hash - origin_hash) + local node = minetest.get_node(ipos) + local imeta = minetest.get_meta(ipos) + + if data.node.name ~= node.name then + minetest.log("error", "[Digtron] digtron.deconstruct tried writing ".. digtron_id .. "'s stored metadata for node " + .. data.node.name .. " at " .. minetest.pos_to_string(pos) .. " but the node at that location was of type " + .. node.name) + elseif imeta:get_string("digtron_id") ~= digtron_id then + minetest.log("error", "[Digtron] digtron.deconstruct tried writing ".. digtron_id .. "'s stored metadata for node " + .. data.node.name .. " at " .. minetest.pos_to_string(pos) .. " but the node at that location had a digtron_id value of \"" + .. imeta:get_string("digtron_id") .. "\"") + + else + local iinv = imeta:get_inventory() + for listname, size in pairs(data.meta.inventory) do + iinv:set_size(listname, size) + for i, itemstack in ipairs(inv:get_list(listname)) do + -- add everything, putting leftovers back in the main inventory + inv:set_stack(listname, i, iinv:add_item(listname, itemstack)) + end + end + + -- TODO: special handling for builder node inventories + + -- Ensure node metadata fields are all set, too + for field, value in pairs(data.meta.fields) do + imeta:set_string(field, value) + end + + -- Clear digtron_id, this node is no longer part of an active digtron + imeta:set_string("digtron_id", "") + end + end dispose_id(digtron_id) -end \ No newline at end of file +end + +--------------------------------------------------------------------------------- +-- Misc + +digtron.can_dig = function(pos, digger) + local meta = minetest.get_meta(pos) + if meta:get_string("digtron_id") ~= "" then + return false + end + return true +end diff --git a/nodes/node_misc.lua b/nodes/node_misc.lua index 99608c7..789152f 100644 --- a/nodes/node_misc.lua +++ b/nodes/node_misc.lua @@ -32,6 +32,7 @@ minetest.register_node("digtron:structure", { {-0.3125, -0.5, -0.5, 0.3125, -0.3125, -0.3125}, } }, + can_dig = digtron.can_dig, }) -- 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. @@ -53,6 +54,7 @@ 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, }) -- A simple structural panel @@ -75,6 +77,7 @@ 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, }) -- A simple structural panel @@ -103,7 +106,7 @@ minetest.register_node("digtron:edge_panel", { {-0.5, -0.5, -0.5, 0.5, -0.4375, 0.4375} }, }, - + can_dig = digtron.can_dig, }) minetest.register_node("digtron:corner_panel", { @@ -133,4 +136,5 @@ minetest.register_node("digtron:corner_panel", { {-0.5, -0.4375, -0.5, -0.4375, 0.5, 0.4375}, }, }, + can_dig = digtron.can_dig, }) \ No newline at end of file diff --git a/nodes/node_storage.lua b/nodes/node_storage.lua index 2cb2bbb..eadcb4f 100644 --- a/nodes/node_storage.lua +++ b/nodes/node_storage.lua @@ -53,7 +53,7 @@ minetest.register_node("digtron:inventory", { 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 meta:get_string("digtron_id") == "" end, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) @@ -153,7 +153,7 @@ minetest.register_node("digtron:fuelstore", { can_dig = function(pos,player) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() - return inv:is_empty("fuel") + return inv:is_empty("fuel") and meta:get_string("digtron_id") == "" end, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) @@ -275,7 +275,7 @@ minetest.register_node("digtron:combined_storage", { 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") + return inv:is_empty("fuel") and inv:is_empty("main") and meta:get_string("digtron_id") == "" end, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) From 27c9d82ea475c80de66f4bd0698396f547b9fc8a Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 18 Aug 2019 15:29:15 -0600 Subject: [PATCH 05/63] a bit of naming work --- controller.lua | 44 ++++++++++++------ functions.lua | 121 +++++++++++++++++++++++++++---------------------- 2 files changed, 99 insertions(+), 66 deletions(-) diff --git a/controller.lua b/controller.lua index cc3cff2..9a8d600 100644 --- a/controller.lua +++ b/controller.lua @@ -19,18 +19,30 @@ local controller_nodebox = { } local get_controller_unconstructed_formspec = function(pos, player_name) - return "size[8,8]button[1,1;1,1;construct;Construct]" + local meta = minetest.get_meta(pos) + return "size[9,9]" + .. "container[0.5,0]" + .. "button[0,0;1,1;construct;Construct]" + .. "field[1.2,0.25;2,1;digtron_name;Digtron name;"..meta:get_string("digtron_name").."]" + .. "field_close_on_enter[digtron_name;false]" + .. "container_end[]" end -local get_controller_constructed_formspec = function(pos, digtron_id_name, player_name) - digtron.retrieve_inventory(digtron_id_name) - - return "size[9,9]button[1,0;1,1;deconstruct;Deconstruct]" - .. "list[detached:" .. digtron_id_name .. ";main;1,1;8,2]" -- TODO: paging system for inventory - .. "list[detached:" .. digtron_id_name .. ";fuel;1,3.5;8,2]" -- TODO: paging system for inventory - .."container[1,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_name .. ";main]" +local get_controller_constructed_formspec = function(pos, digtron_id, player_name) + digtron.retrieve_inventory(digtron_id) -- ensures the detatched inventory exists and is populated + return "size[9,9]" + .. "container[0.5,0]" + .. "button[0,0;1,1;deconstruct;Deconstruct]" + .. "field[1.2,0.25;2,1;digtron_name;Digtron name;"..digtron.get_name(digtron_id).."]" + .. "field_close_on_enter[digtron_name;false]" + .. "container_end[]" + .. "container[0.5,1]" + .. "list[detached:" .. digtron_id .. ";main;0,0;8,2]" -- TODO: paging system for inventory + .. "list[detached:" .. digtron_id .. ";fuel;0,2.5;8,2]" -- TODO: paging system for inventory + .. "container_end[]" + .. "container[0.5,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 .. ";main]" end minetest.register_node("digtron:controller", { @@ -85,8 +97,6 @@ minetest.register_node("digtron:controller", { end, }) - - -- Dealing with an unconstructed Digtron controller minetest.register_on_player_receive_fields(function(player, formname, fields) local formname_split = formname:split(":") @@ -113,7 +123,11 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) get_controller_constructed_formspec(pos, digtron_id, name)) end end - + + if fields.key_enter_field == "digtron_name" or fields.digtron_name then + local meta = minetest.get_meta(pos) + meta:set_string("digtron_name", fields.digtron_name) + end end) -- Controlling a fully armed and operational Digtron @@ -149,4 +163,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end + if fields.key_enter_field == "digtron_name" or fields.digtron_name then + digtron.set_name(digtron_id, fields.digtron_name) + end + end) diff --git a/functions.lua b/functions.lua index 3a78706..3c9b94a 100644 --- a/functions.lua +++ b/functions.lua @@ -6,7 +6,7 @@ digtron.adjacent = {} ------------------------------------------------------------------------------------ -- Inventory --- indexed by digtron_id_name, set to true whenever the detached inventory's contents change +-- indexed by digtron_id, set to true whenever the detached inventory's contents change local dirty_inventories = {} local detached_inventory_callbacks = { @@ -64,11 +64,11 @@ local detached_inventory_callbacks = { -- 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 -digtron.retrieve_inventory = function(digtron_id_name) - local inv = minetest.get_inventory({type="detached", name=digtron_id_name}) +digtron.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_name, detached_inventory_callbacks) - local inv_string = mod_meta:get_string("inv_"..digtron_id_name) + inv = minetest.create_detached_inventory(digtron_id, detached_inventory_callbacks) + local inv_string = mod_meta:get_string("inv_"..digtron_id) if inv_string ~= "" then local inventory_table = minetest.deserialize(inv_string) for listname, invlist in pairs(inventory_table) do @@ -81,11 +81,11 @@ digtron.retrieve_inventory = function(digtron_id_name) end -- Stores contents of detached inventory as a metadata string -local persist_inventory = function(digtron_id_name) - local inv = minetest.get_inventory({type="detached", name=digtron_id_name}) +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_name) + .. digtron_id) return end local lists = inv:get_lists() @@ -99,13 +99,13 @@ local persist_inventory = function(digtron_id_name) persist[listname] = inventory end - mod_meta:set_string("inv_"..digtron_id_name, minetest.serialize(persist)) + mod_meta:set_string("inv_"..digtron_id, minetest.serialize(persist)) end minetest.register_globalstep(function(dtime) - for digtron_id_name, _ in pairs(dirty_inventories) do - persist_inventory(digtron_id_name) - dirty_inventories[digtron_id_name] = nil + for digtron_id, _ in pairs(dirty_inventories) do + persist_inventory(digtron_id) + dirty_inventories[digtron_id] = nil end end) @@ -116,43 +116,55 @@ local create_new_id = function() local new_id = last_id + 1 mod_meta:set_int("last_id", new_id) -- ensure each call to this method gets a unique number - local digtron_id_name = "digtron_id_" .. tostring(new_id) - local inv = minetest.create_detached_inventory(digtron_id_name, detached_inventory_callbacks) + local digtron_id = "digtron_id_" .. tostring(new_id) + local inv = minetest.create_detached_inventory(digtron_id, detached_inventory_callbacks) - return digtron_id_name, inv + return digtron_id, inv end -- Deletes a Digtron record. Note: just throws everything away, this is not digtron.deconstruct. -local dispose_id = function(digtron_id_name) - minetest.remove_detached_inventory(digtron_id_name) - digtron.layout[digtron_id_name] = nil - digtron.adjacent[digtron_id_name] = nil - mod_meta:set_string("inv_"..digtron_id_name, "") - mod_meta:set_string("layout_"..digtron_id_name, "") - mod_meta:set_string("adjacent_"..digtron_id_name, "") +local dispose_id = function(digtron_id) + minetest.remove_detached_inventory(digtron_id) + digtron.layout[digtron_id] = nil + digtron.adjacent[digtron_id] = nil + mod_meta:set_string("inv_"..digtron_id, "") + mod_meta:set_string("layout_"..digtron_id, "") + mod_meta:set_string("adjacent_"..digtron_id, "") + mod_meta:set_string("name_"..digtron_id, "") +end + +-------------------------------------------------------------------------------------------- +-- Name + +digtron.get_name = function(digtron_id) + return mod_meta:get_string("name_"..digtron_id) +end + +digtron.set_name = function(digtron_id, digtron_name) + mod_meta:set_string("name_"..digtron_id, digtron_name) end ------------------------------------------------------------------------------------------------------- -- Layout local get_persist_table_function = function(identifier) - return function(digtron_id_name, tbl) - mod_meta:set_string(identifier .."_"..digtron_id_name, minetest.serialize(tbl)) - digtron[identifier][digtron_id_name] = tbl + return function(digtron_id, tbl) + mod_meta:set_string(identifier .."_"..digtron_id, minetest.serialize(tbl)) + digtron[identifier][digtron_id] = tbl end end local get_retrieve_table_function = function(identifier) - return function(digtron_id_name) - local current = digtron[identifier][digtron_id_name] + return function(digtron_id) + local current = digtron[identifier][digtron_id] if current then return current end - local tbl_string = mod_meta:get_string(identifier.."_"..digtron_id_name) + local tbl_string = mod_meta:get_string(identifier.."_"..digtron_id) if tbl_string ~= "" then current = minetest.deserialize(tbl_string) if current then - digtron[identifier][digtron_id_name] = current + digtron[identifier][digtron_id] = current end return current end @@ -207,54 +219,54 @@ end local origin_hash = minetest.hash_node_position({x=0,y=0,z=0}) -- Returns the id of the new Digtron record, or nil on failure -digtron.construct = function(pos, player_name) - local node = minetest.get_node(pos) +digtron.construct = function(root_pos, player_name) + local node = minetest.get_node(root_pos) -- TODO: a more generic test? Not needed with the more generic controller design, as far as I can tell if node.name ~= "digtron:controller" then -- Called on an incorrect node - minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(pos) + minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(root_pos) .. " but the node at this location was " .. node.name) return nil end - local meta = minetest.get_meta(pos) - if meta:contains("digtron_id") then + local root_meta = minetest.get_meta(root_pos) + if root_meta:contains("digtron_id") then -- Already constructed. TODO: validate that the digtron_id actually exists as well - minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(pos) + minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(root_pos) .. " but the controller at this location was already part of a constructed Digtron.") return nil end - local root_hash = minetest.hash_node_position(pos) + local root_hash = minetest.hash_node_position(root_pos) local digtron_nodes = {[root_hash] = node} -- Nodes that are part of Digtron local digtron_adjacent = {} -- Nodes that are adjacent to Digtron but not a part of it - get_all_adjacent_digtron_nodes(pos, digtron_nodes, digtron_adjacent, player_name) + get_all_adjacent_digtron_nodes(root_pos, digtron_nodes, digtron_adjacent, player_name) - local digtron_id, digtron_inv = create_new_id(pos) + local digtron_id, digtron_inv = create_new_id(root_pos) local layout = {} for hash, node in pairs(digtron_nodes) do local relative_hash = hash - root_hash + origin_hash - local digtron_meta + local current_meta if hash == root_hash then - digtron_meta = meta -- we're processing the controller, we already have a reference to its meta + current_meta = root_meta -- we're processing the controller, we already have a reference to its meta else - digtron_meta = minetest.get_meta(minetest.get_position_from_hash(hash)) + current_meta = minetest.get_meta(minetest.get_position_from_hash(hash)) end - local meta_table = digtron_meta:to_table() + local current_meta_table = current_meta:to_table() - if meta_table.fields.digtron_id then + if current_meta_table.fields.digtron_id then -- Trying to incorporate part of an existing digtron, should be impossible. minetest.log("error", "[Digtron] digtron.construct 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 " .. meta_table.fields.digtron_id) + .. " that was already assigned to digtron id " .. current_meta_table.fields.digtron_id) dispose_id(digtron_id) return nil end -- Process inventories specially -- TODO Builder inventory gets turned into an itemname in a special key in the builder's meta -- fuel and main get added to corresponding detached inventory lists - for listname, items in pairs(meta_table.inventory) do + 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) @@ -263,13 +275,14 @@ digtron.construct = function(pos, player_name) end -- erase actual items from stored layout metadata, the detached inventory is authoritative -- store the inventory size so the inventory can be easily recreated - meta_table.inventory[listname] = #items + current_meta_table.inventory[listname] = #items end node.param1 = nil -- we don't care about param1, wipe it to save space - layout[relative_hash] = {meta = meta_table, node = node} + layout[relative_hash] = {meta = current_meta_table, node = node} end + digtron.set_name(digtron_id, root_meta:get_string("digtron_name")) persist_inventory(digtron_id) persist_layout(digtron_id, layout) persist_adjacent(digtron_id, digtron_adjacent) @@ -279,7 +292,7 @@ digtron.construct = function(pos, player_name) for hash, node in pairs(digtron_nodes) do local digtron_meta if hash == root_hash then - digtron_meta = meta -- we're processing the controller, we already have a reference to its meta + digtron_meta = root_meta -- we're processing the controller, we already have a reference to its meta else digtron_meta = minetest.get_meta(minetest.get_position_from_hash(hash)) end @@ -298,11 +311,13 @@ digtron.construct = function(pos, player_name) return digtron_id end -digtron.deconstruct = function(digtron_id, pos, name) - --local meta = minetest.get_meta(pos) +digtron.deconstruct = function(digtron_id, root_pos, name) + local root_meta = minetest.get_meta(root_pos) + root_meta:set_string("digtron_name", digtron.get_name(digtron_id)) + local layout = retrieve_layout(digtron_id) local inv = digtron.retrieve_inventory(digtron_id) - local root_hash = minetest.hash_node_position(pos) + local root_hash = minetest.hash_node_position(root_pos) -- Write metadata and inventory to in-world node at this location for hash, data in pairs(layout) do @@ -312,11 +327,11 @@ digtron.deconstruct = function(digtron_id, pos, name) if data.node.name ~= node.name then minetest.log("error", "[Digtron] digtron.deconstruct tried writing ".. digtron_id .. "'s stored metadata for node " - .. data.node.name .. " at " .. minetest.pos_to_string(pos) .. " but the node at that location was of type " + .. data.node.name .. " at " .. minetest.pos_to_string(root_pos) .. " but the node at that location was of type " .. node.name) elseif imeta:get_string("digtron_id") ~= digtron_id then minetest.log("error", "[Digtron] digtron.deconstruct tried writing ".. digtron_id .. "'s stored metadata for node " - .. data.node.name .. " at " .. minetest.pos_to_string(pos) .. " but the node at that location had a digtron_id value of \"" + .. data.node.name .. " at " .. minetest.pos_to_string(root_pos) .. " but the node at that location had a digtron_id value of \"" .. imeta:get_string("digtron_id") .. "\"") else From 9d526997fcae0dc6dbdff46f73d732f40b313ea8 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 18 Aug 2019 15:51:25 -0600 Subject: [PATCH 06/63] make constructed controllers pick-uppable --- controller.lua | 53 ++++++++++++++++++++++++++++++++++++++++++++------ functions.lua | 4 ++-- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/controller.lua b/controller.lua index 9a8d600..a0a343b 100644 --- a/controller.lua +++ b/controller.lua @@ -23,7 +23,7 @@ local get_controller_unconstructed_formspec = function(pos, player_name) return "size[9,9]" .. "container[0.5,0]" .. "button[0,0;1,1;construct;Construct]" - .. "field[1.2,0.25;2,1;digtron_name;Digtron name;"..meta:get_string("digtron_name").."]" + .. "field[1.2,0.25;2,1;digtron_name;Digtron name;"..meta:get_string("infotext").."]" .. "field_close_on_enter[digtron_name;false]" .. "container_end[]" end @@ -70,10 +70,49 @@ minetest.register_node("digtron:controller", { on_dig = function(pos, node, digger) local meta = minetest.get_meta(pos) - if meta:get_string("digtron_id") ~= "" then - return -- TODO: special handling here! - else - return minetest.node_dig(pos, node, digger) + local digtron_id = meta:get_string("digtron_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_string("description", meta:get_string("infotext")) + local inv = digger: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) + + if digtron_id ~= "" then + -- TODO destroy other nodes belonging to this digtron + end + end, + + --TODO: this didn't work when I blew up the digtron with TNT, investigate why + 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_string("description", oldmeta:get_string("infotext")) + return + end + end + end, + + after_place_node = function(pos, placer, itemstack, pointed_thing) + local stack_meta = itemstack:get_meta() + local title = stack_meta:get_string("description") + local digtron_id = stack_meta:get_string("digtron_id") + + local meta = minetest.get_meta(pos) + + meta:set_string("infotext", title) + meta:set_string("digtron_id", digtron_id) + + if digtron_id ~= "" then + -- TODO create the other nodes belonging to this digtron end end, @@ -126,7 +165,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if fields.key_enter_field == "digtron_name" or fields.digtron_name then local meta = minetest.get_meta(pos) - meta:set_string("digtron_name", fields.digtron_name) + meta:set_string("infotext", fields.digtron_name) end end) @@ -164,6 +203,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end if fields.key_enter_field == "digtron_name" or fields.digtron_name then + local meta = minetest.get_meta(pos) + meta:set_string("infotext", fields.digtron_name) digtron.set_name(digtron_id, fields.digtron_name) end diff --git a/functions.lua b/functions.lua index 3c9b94a..43e3d9c 100644 --- a/functions.lua +++ b/functions.lua @@ -282,7 +282,7 @@ digtron.construct = function(root_pos, player_name) layout[relative_hash] = {meta = current_meta_table, node = node} end - digtron.set_name(digtron_id, root_meta:get_string("digtron_name")) + digtron.set_name(digtron_id, root_meta:get_string("infotext")) persist_inventory(digtron_id) persist_layout(digtron_id, layout) persist_adjacent(digtron_id, digtron_adjacent) @@ -313,7 +313,7 @@ end digtron.deconstruct = function(digtron_id, root_pos, name) local root_meta = minetest.get_meta(root_pos) - root_meta:set_string("digtron_name", digtron.get_name(digtron_id)) + root_meta:set_string("infotext", digtron.get_name(digtron_id)) local layout = retrieve_layout(digtron_id) local inv = digtron.retrieve_inventory(digtron_id) From fda6aa9a0e2bb789d1b85b2ec0b7f38dc941195c Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 18 Aug 2019 16:24:58 -0600 Subject: [PATCH 07/63] a little more setup for placing constructed Digtrons --- controller.lua | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/controller.lua b/controller.lua index a0a343b..8f5c9fe 100644 --- a/controller.lua +++ b/controller.lua @@ -89,7 +89,7 @@ minetest.register_node("digtron:controller", { end end, - --TODO: this didn't work when I blew up the digtron with TNT, investigate why + --TODO: this didn't work when I blew up a digtron with TNT, investigate why preserve_metadata = function(pos, oldnode, oldmeta, drops) for _, dropped in ipairs(drops) do if dropped:get_name() == "digtron:controller" then @@ -101,6 +101,23 @@ minetest.register_node("digtron:controller", { 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 stack_meta = itemstack:get_meta() + local digtron_id = stack_meta:get_string("digtron_id") + if digtron_id ~= "" then + -- Test if Digtron will fit the surroundings + -- if not, try moving it up so that the lowest y-coordinate on the Digtron is + -- at the y-coordinate of the place clicked on and test again. + -- if that fails, show ghost of Digtron and fail to place. + end + -- + + -- Default: + return minetest.item_place(itemstack, placer, pointed_thing) + end, + after_place_node = function(pos, placer, itemstack, pointed_thing) local stack_meta = itemstack:get_meta() local title = stack_meta:get_string("description") @@ -163,6 +180,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end + --TODO: this isn't recording the field when using ESC to exit the formspec if fields.key_enter_field == "digtron_name" or fields.digtron_name then local meta = minetest.get_meta(pos) meta:set_string("infotext", fields.digtron_name) @@ -202,6 +220,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end + --TODO: this isn't recording the field when using ESC to exit the formspec if fields.key_enter_field == "digtron_name" or fields.digtron_name then local meta = minetest.get_meta(pos) meta:set_string("infotext", fields.digtron_name) From e88a3faae3c2bf41cd063e75e5dc0b86a96d5faf Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 18 Aug 2019 18:23:51 -0600 Subject: [PATCH 08/63] basic ability to pick up and put down a complete digtron --- controller.lua | 21 ++++++++--- functions.lua | 98 ++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 99 insertions(+), 20 deletions(-) diff --git a/controller.lua b/controller.lua index 8f5c9fe..f7b0d08 100644 --- a/controller.lua +++ b/controller.lua @@ -69,6 +69,10 @@ minetest.register_node("digtron:controller", { -- end, 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") @@ -82,10 +86,10 @@ minetest.register_node("digtron:controller", { minetest.add_item(pos, stack) end -- call on_dignodes callback - minetest.remove_node(pos) - if digtron_id ~= "" then - -- TODO destroy other nodes belonging to this digtron + digtron.remove_from_world(digtron_id, pos, player_name) + else + minetest.remove_node(pos) end end, @@ -104,13 +108,18 @@ minetest.register_node("digtron:controller", { 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 -- Test if Digtron will fit the surroundings -- if not, try moving it up so that the lowest y-coordinate on the Digtron is -- at the y-coordinate of the place clicked on and test again. - -- if that fails, show ghost of Digtron and fail to place. + -- if that fails, show ghost of Digtron and fail to place. + local root_pos = minetest.get_pointed_thing_position(pointed_thing, true) + digtron.build_to_world(digtron_id, root_pos, player_name) end -- @@ -136,7 +145,9 @@ minetest.register_node("digtron:controller", { on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) local meta = minetest.get_meta(pos) local digtron_id = meta:get_string("digtron_id") - local player_name = clicker:get_player_name() + local player_name + if clicker then player_name = clicker:get_player_name() end + if digtron_id == "" then minetest.show_formspec(player_name, "digtron_controller_unconstructed:"..minetest.pos_to_string(pos)..":"..player_name, diff --git a/functions.lua b/functions.lua index 43e3d9c..774169d 100644 --- a/functions.lua +++ b/functions.lua @@ -311,7 +311,30 @@ digtron.construct = function(root_pos, player_name) return digtron_id end -digtron.deconstruct = function(digtron_id, root_pos, name) + +-- 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, root_hash, hash, data, function_name) + local ipos = minetest.get_position_from_hash(hash + root_hash - origin_hash) + local node = minetest.get_node(ipos) + local imeta = minetest.get_meta(ipos) + + if data.node.name ~= node.name then + minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with one of ".. digtron_id .. "'s " + .. data.node.name .. "s at " .. minetest.pos_to_string(ipos) .. " but the node at that location was of type " + .. node.name) + elseif imeta:get_string("digtron_id") ~= digtron_id then + minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with ".. digtron_id .. "'s " + .. data.node.name .. " at " .. minetest.pos_to_string(ipos) + .. " but the node at that location had a non-matching digtron_id value of \"" + .. imeta:get_string("digtron_id") .. "\"") + else + return ipos, node, imeta + end +end + +-- Turns the Digtron back into pieces +digtron.deconstruct = function(digtron_id, root_pos, player_name) local root_meta = minetest.get_meta(root_pos) root_meta:set_string("infotext", digtron.get_name(digtron_id)) @@ -321,20 +344,9 @@ digtron.deconstruct = function(digtron_id, root_pos, name) -- Write metadata and inventory to in-world node at this location for hash, data in pairs(layout) do - local ipos = minetest.get_position_from_hash(hash + root_hash - origin_hash) - local node = minetest.get_node(ipos) - local imeta = minetest.get_meta(ipos) - - if data.node.name ~= node.name then - minetest.log("error", "[Digtron] digtron.deconstruct tried writing ".. digtron_id .. "'s stored metadata for node " - .. data.node.name .. " at " .. minetest.pos_to_string(root_pos) .. " but the node at that location was of type " - .. node.name) - elseif imeta:get_string("digtron_id") ~= digtron_id then - minetest.log("error", "[Digtron] digtron.deconstruct tried writing ".. digtron_id .. "'s stored metadata for node " - .. data.node.name .. " at " .. minetest.pos_to_string(root_pos) .. " but the node at that location had a digtron_id value of \"" - .. imeta:get_string("digtron_id") .. "\"") - - else + local ipos, node, imeta = get_valid_data(digtron_id, root_hash, hash, data, "digtron.deconstruct") + + if ipos then local iinv = imeta:get_inventory() for listname, size in pairs(data.meta.inventory) do iinv:set_size(listname, size) @@ -359,6 +371,62 @@ digtron.deconstruct = function(digtron_id, root_pos, name) dispose_id(digtron_id) end +-- Removes the in-world nodes of a digtron +-- Does not destroy its layout info +digtron.remove_from_world = function(digtron_id, root_pos, player_name) + local layout = retrieve_layout(digtron_id) + local root_hash = minetest.hash_node_position(root_pos) + local nodes_to_destroy = {} + for hash, data in pairs(layout) do + local ipos, node, imeta = get_valid_data(digtron_id, root_hash, hash, data, "digtron.destroy") + if ipos then + table.insert(nodes_to_destroy, ipos) + end + end + + -- TODO: voxelmanip might be better here? + minetest.bulk_set_node(nodes_to_destroy, {name="air"}) +end + +digtron.build_to_world = function(digtron_id, root_pos, player_name) + local layout = retrieve_layout(digtron_id) + local root_hash = minetest.hash_node_position(root_pos) + local nodes_to_create = {} + + local permitted = true + for hash, data in pairs(layout) do + local ipos = minetest.get_position_from_hash(hash + root_hash - origin_hash) + local node = minetest.get_node(ipos) + local node_def = minetest.registered_nodes[node.name] + -- TODO: lots of testing needed here + if not (node_def and node_def.buildable_to) then + minetest.chat_send_all("not permitted due to " .. node.name .. " at " .. minetest.pos_to_string(ipos)) + permitted = false + break + end + end + + if permitted then + -- TODO: voxelmanip might be better here, less likely than with destroy though since metadata needs to be written + for hash, data in pairs(layout) do + local ipos = minetest.get_position_from_hash(hash + root_hash - origin_hash) + minetest.set_node(ipos, data.node) + local meta = minetest.get_meta(ipos) + meta:set_string("digtron_id", digtron_id) + for field, value in pairs(data.meta.fields) do + meta:set_string(field, value) + end + -- Not needed - local inventories not used by active digtron, will be restored if deconstructed +-- local inv = meta:get_inventory() +-- for listname, size in pairs(data.meta.inventory) do +-- inv:set_size(listname, size) +-- end + end + end + + +end + --------------------------------------------------------------------------------- -- Misc From 4031ea0484654af670959cd7fc0704c26b40dcf8 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 18 Aug 2019 18:37:47 -0600 Subject: [PATCH 09/63] tidy up aftermath of building a constructed digtron --- controller.lua | 18 +++++++++--------- functions.lua | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/controller.lua b/controller.lua index f7b0d08..100038e 100644 --- a/controller.lua +++ b/controller.lua @@ -119,12 +119,16 @@ minetest.register_node("digtron:controller", { -- at the y-coordinate of the place clicked on and test again. -- if that fails, show ghost of Digtron and fail to place. local root_pos = minetest.get_pointed_thing_position(pointed_thing, true) - digtron.build_to_world(digtron_id, root_pos, player_name) + if digtron.build_to_world(digtron_id, root_pos, player_name) then + -- 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("") + end + return itemstack + else + return minetest.item_place(itemstack, placer, pointed_thing) end - -- - - -- Default: - return minetest.item_place(itemstack, placer, pointed_thing) end, after_place_node = function(pos, placer, itemstack, pointed_thing) @@ -136,10 +140,6 @@ minetest.register_node("digtron:controller", { meta:set_string("infotext", title) meta:set_string("digtron_id", digtron_id) - - if digtron_id ~= "" then - -- TODO create the other nodes belonging to this digtron - end end, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) diff --git a/functions.lua b/functions.lua index 774169d..32ca540 100644 --- a/functions.lua +++ b/functions.lua @@ -424,7 +424,7 @@ digtron.build_to_world = function(digtron_id, root_pos, player_name) end end - + return permitted end --------------------------------------------------------------------------------- From e16035b03709287861b4150f77d19029079e606d Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 18 Aug 2019 22:41:01 -0600 Subject: [PATCH 10/63] save bounding box relative to root. --- functions.lua | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/functions.lua b/functions.lua index 32ca540..bf05cfb 100644 --- a/functions.lua +++ b/functions.lua @@ -2,6 +2,7 @@ local mod_meta = minetest.get_mod_storage() digtron.layout = {} digtron.adjacent = {} +digtron.bounding_box = {} ------------------------------------------------------------------------------------ -- Inventory @@ -131,6 +132,7 @@ local dispose_id = function(digtron_id) mod_meta:set_string("layout_"..digtron_id, "") mod_meta:set_string("adjacent_"..digtron_id, "") mod_meta:set_string("name_"..digtron_id, "") + mod_meta:set_string("bounding_box_"..digtron_id, "") end -------------------------------------------------------------------------------------------- @@ -175,6 +177,8 @@ local persist_layout = get_persist_table_function("layout") local retrieve_layout = get_retrieve_table_function("layout") local persist_adjacent = get_persist_table_function("adjacent") local retrieve_adjacent = get_retrieve_table_function("adjacent") +local persist_bounding_box = get_persist_table_function("bounding_box") +local retrieve_bounding_box = get_retrieve_table_function("bounding_box") local cardinal_directions = { {x=1,y=0,z=0}, @@ -185,9 +189,18 @@ local cardinal_directions = { {x=0,y=0,z=-1}, } +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 + -- recursive function searches out all connected unassigned digtron nodes local get_all_adjacent_digtron_nodes -get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, digtron_adjacent, player_name) +get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, digtron_adjacent, bounding_box, player_name) for _, dir in ipairs(cardinal_directions) do local test_pos = vector.add(pos, dir) local test_hash = minetest.hash_node_position(test_pos) @@ -203,7 +216,8 @@ get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, digtron_adjacent, else --test_node.group_value = group_value -- for later ease of reference digtron_nodes[test_hash] = test_node - get_all_adjacent_digtron_nodes(test_pos, digtron_nodes, digtron_adjacent, player_name) -- recurse + update_bounding_box(bounding_box, test_pos) + get_all_adjacent_digtron_nodes(test_pos, digtron_nodes, digtron_adjacent, bounding_box, player_name) -- recurse end else -- don't record details, the content of this node will change as the digtron moves @@ -238,7 +252,8 @@ digtron.construct = function(root_pos, player_name) local root_hash = minetest.hash_node_position(root_pos) local digtron_nodes = {[root_hash] = node} -- Nodes that are part of Digtron local digtron_adjacent = {} -- Nodes that are adjacent to Digtron but not a part of it - get_all_adjacent_digtron_nodes(root_pos, digtron_nodes, digtron_adjacent, player_name) + local bounding_box = {minp=vector.new(root_pos), maxp=vector.new(root_pos)} + get_all_adjacent_digtron_nodes(root_pos, digtron_nodes, digtron_adjacent, bounding_box, player_name) local digtron_id, digtron_inv = create_new_id(root_pos) @@ -282,10 +297,14 @@ digtron.construct = function(root_pos, player_name) layout[relative_hash] = {meta = current_meta_table, node = node} end + bounding_box.minp = vector.subtract(bounding_box.minp, root_pos) + bounding_box.maxp = vector.subtract(bounding_box.maxp, root_pos) + digtron.set_name(digtron_id, root_meta:get_string("infotext")) persist_inventory(digtron_id) persist_layout(digtron_id, layout) persist_adjacent(digtron_id, digtron_adjacent) + persist_bounding_box(digtron_id, bounding_box) -- 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. From 1c732ae1cd312e23d7c178e9228fb1ab24638a96 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 18 Aug 2019 23:11:24 -0600 Subject: [PATCH 11/63] change metadata naming standard, add some corrupted data checks --- functions.lua | 50 +++++++++++++++++++++++++++++++----------- nodes/node_storage.lua | 15 +++++-------- 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/functions.lua b/functions.lua index bf05cfb..8aa15da 100644 --- a/functions.lua +++ b/functions.lua @@ -4,6 +4,11 @@ digtron.layout = {} digtron.adjacent = {} digtron.bounding_box = {} +-- Wipes mod_meta +--for field, value in pairs(mod_meta:to_table().fields) do +-- mod_meta:set_string(field, "") +--end + ------------------------------------------------------------------------------------ -- Inventory @@ -69,7 +74,7 @@ digtron.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("inv_"..digtron_id) + 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 @@ -100,7 +105,7 @@ local persist_inventory = function(digtron_id) persist[listname] = inventory end - mod_meta:set_string("inv_"..digtron_id, minetest.serialize(persist)) + mod_meta:set_string(digtron_id..":inv", minetest.serialize(persist)) end minetest.register_globalstep(function(dtime) @@ -117,7 +122,7 @@ local create_new_id = function() local new_id = last_id + 1 mod_meta:set_int("last_id", new_id) -- ensure each call to this method gets a unique number - local digtron_id = "digtron_id_" .. tostring(new_id) + local digtron_id = "digtron" .. tostring(new_id) local inv = minetest.create_detached_inventory(digtron_id, detached_inventory_callbacks) return digtron_id, inv @@ -128,22 +133,22 @@ local dispose_id = function(digtron_id) minetest.remove_detached_inventory(digtron_id) digtron.layout[digtron_id] = nil digtron.adjacent[digtron_id] = nil - mod_meta:set_string("inv_"..digtron_id, "") - mod_meta:set_string("layout_"..digtron_id, "") - mod_meta:set_string("adjacent_"..digtron_id, "") - mod_meta:set_string("name_"..digtron_id, "") - mod_meta:set_string("bounding_box_"..digtron_id, "") + mod_meta:set_string(digtron_id..":inv", "") + mod_meta:set_string(digtron_id..":layout", "") + mod_meta:set_string(digtron_id..":adjacent", "") + mod_meta:set_string(digtron_id..":name", "") + mod_meta:set_string(digtron_id..":bounding_box", "") end -------------------------------------------------------------------------------------------- -- Name digtron.get_name = function(digtron_id) - return mod_meta:get_string("name_"..digtron_id) + return mod_meta:get_string(digtron_id..":name") end digtron.set_name = function(digtron_id, digtron_name) - mod_meta:set_string("name_"..digtron_id, digtron_name) + mod_meta:set_string(digtron_id..":name", digtron_name) end ------------------------------------------------------------------------------------------------------- @@ -151,7 +156,7 @@ end local get_persist_table_function = function(identifier) return function(digtron_id, tbl) - mod_meta:set_string(identifier .."_"..digtron_id, minetest.serialize(tbl)) + mod_meta:set_string(digtron_id..":"..identifier, minetest.serialize(tbl)) digtron[identifier][digtron_id] = tbl end end @@ -162,7 +167,7 @@ local get_retrieve_table_function = function(identifier) if current then return current end - local tbl_string = mod_meta:get_string(identifier.."_"..digtron_id) + local tbl_string = mod_meta:get_string(digtron_id..":"..identifier) if tbl_string ~= "" then current = minetest.deserialize(tbl_string) if current then @@ -359,6 +364,14 @@ digtron.deconstruct = function(digtron_id, root_pos, player_name) local layout = retrieve_layout(digtron_id) local inv = digtron.retrieve_inventory(digtron_id) + + if not (layout and inv) then + minetest.log("error", "Unable to find layout or inventory record for " .. digtron_id + .. ", wiping any remaining metadata for this id to prevent corruption. Sorry!") + dispose_id(digtron_id) + return + end + local root_hash = minetest.hash_node_position(root_pos) -- Write metadata and inventory to in-world node at this location @@ -394,6 +407,16 @@ end -- Does not destroy its layout info digtron.remove_from_world = function(digtron_id, root_pos, player_name) local layout = retrieve_layout(digtron_id) + + if not layout then + minetest.log("error", "Unable to find layout record for " .. digtron_id + .. ", wiping any remaining metadata for this id to prevent corruption. Sorry!") + local meta = minetest.get_meta(root_pos) + meta:set_string("digtron_id", "") + dispose_id(digtron_id) + return + end + local root_hash = minetest.hash_node_position(root_pos) local nodes_to_destroy = {} for hash, data in pairs(layout) do @@ -451,7 +474,8 @@ end digtron.can_dig = function(pos, digger) local meta = minetest.get_meta(pos) - if meta:get_string("digtron_id") ~= "" then + local digtron_id = meta:get_string("digtron_id") + if mod_meta:contains(digtron_id..":layout") then return false end return true diff --git a/nodes/node_storage.lua b/nodes/node_storage.lua index eadcb4f..75a7cce 100644 --- a/nodes/node_storage.lua +++ b/nodes/node_storage.lua @@ -51,9 +51,8 @@ minetest.register_node("digtron:inventory", { end, can_dig = function(pos,player) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:is_empty("main") and meta:get_string("digtron_id") == "" + local inv = minetest.get_inventory({type="node", pos=pos}) + return inv:is_empty("main") and digtron.can_dig(pos,player) end, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) @@ -151,9 +150,8 @@ minetest.register_node("digtron:fuelstore", { end, can_dig = function(pos,player) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:is_empty("fuel") and meta:get_string("digtron_id") == "" + local inv = minetest.get_inventory({type="node", pos=pos}) + return inv:is_empty("fuel") and digtron.can_dig(pos,player) end, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) @@ -273,9 +271,8 @@ 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") and meta:get_string("digtron_id") == "" + 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, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) From a9420ea805b6da1a503fecf1cbfb9abd6b103bf7 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Mon, 19 Aug 2019 20:02:16 -0600 Subject: [PATCH 12/63] explosions automatically deconstruct Digtrons. --- controller.lua | 10 +++++-- functions.lua | 65 ++++++++++++++++++++++++++++++++++-------- nodes/node_misc.lua | 5 ++++ nodes/node_storage.lua | 3 ++ 4 files changed, 68 insertions(+), 15 deletions(-) diff --git a/controller.lua b/controller.lua index 100038e..5b5011d 100644 --- a/controller.lua +++ b/controller.lua @@ -37,8 +37,8 @@ local get_controller_constructed_formspec = function(pos, digtron_id, player_nam .. "field_close_on_enter[digtron_name;false]" .. "container_end[]" .. "container[0.5,1]" - .. "list[detached:" .. digtron_id .. ";main;0,0;8,2]" -- TODO: paging system for inventory - .. "list[detached:" .. digtron_id .. ";fuel;0,2.5;8,2]" -- TODO: paging system for inventory + .. "list[detached:" .. digtron_id .. ";main;0,0;8,2]" -- TODO: paging system for inventory, guard against non-existent listname + .. "list[detached:" .. digtron_id .. ";fuel;0,2.5;8,2]" -- TODO: paging system for inventory, guard against non-existent listname .. "container_end[]" .. "container[0.5,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]" @@ -49,7 +49,9 @@ minetest.register_node("digtron:controller", { description = S("Digtron Control Module"), _doc_items_longdesc = nil, _doc_items_usagehelp = nil, - groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, + -- 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, @@ -162,6 +164,8 @@ minetest.register_node("digtron:controller", { on_timer = function(pos, elapsed) end, + + on_blast = digtron.on_blast, }) -- Dealing with an unconstructed Digtron controller diff --git a/functions.lua b/functions.lua index 8aa15da..878d56e 100644 --- a/functions.lua +++ b/functions.lua @@ -4,6 +4,8 @@ digtron.layout = {} digtron.adjacent = {} digtron.bounding_box = {} +--minetest.debug(dump(mod_meta:to_table())) + -- Wipes mod_meta --for field, value in pairs(mod_meta:to_table().fields) do -- mod_meta:set_string(field, "") @@ -240,7 +242,7 @@ local origin_hash = minetest.hash_node_position({x=0,y=0,z=0}) -- Returns the id of the new Digtron record, or nil on failure digtron.construct = function(root_pos, player_name) local node = minetest.get_node(root_pos) - -- TODO: a more generic test? Not needed with the more generic controller design, as far as I can tell + -- TODO: a more generic test? Not needed with the more generic controller design, as far as I can tell. There's only going to be the one type of controller. if node.name ~= "digtron:controller" then -- Called on an incorrect node minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(root_pos) @@ -255,9 +257,10 @@ digtron.construct = function(root_pos, player_name) return nil end local root_hash = minetest.hash_node_position(root_pos) - local digtron_nodes = {[root_hash] = node} -- Nodes that are part of Digtron + local digtron_nodes = {[root_hash] = 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 - local bounding_box = {minp=vector.new(root_pos), maxp=vector.new(root_pos)} + local bounding_box = {minp=vector.new(root_pos), maxp=vector.new(root_pos), root = root_pos} get_all_adjacent_digtron_nodes(root_pos, digtron_nodes, digtron_adjacent, bounding_box, player_name) local digtron_id, digtron_inv = create_new_id(root_pos) @@ -342,19 +345,31 @@ local get_valid_data = function(digtron_id, root_hash, hash, data, function_name local ipos = minetest.get_position_from_hash(hash + root_hash - origin_hash) local node = minetest.get_node(ipos) local imeta = minetest.get_meta(ipos) + local target_digtron_id = imeta:get_string("digtron_id") if data.node.name ~= node.name then minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with one of ".. digtron_id .. "'s " .. data.node.name .. "s at " .. minetest.pos_to_string(ipos) .. " but the node at that location was of type " .. node.name) - elseif imeta:get_string("digtron_id") ~= digtron_id then - minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with ".. digtron_id .. "'s " - .. data.node.name .. " at " .. minetest.pos_to_string(ipos) - .. " but the node at that location had a non-matching digtron_id value of \"" - .. imeta:get_string("digtron_id") .. "\"") - else - return ipos, node, imeta + return + elseif target_digtron_id ~= digtron_id then + if target_digtron_id ~= "" then + minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with ".. digtron_id .. "'s " + .. data.node.name .. " at " .. minetest.pos_to_string(ipos) + .. " 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", "[Digtron] " .. function_name .. " tried interacting with ".. digtron_id .. "'s " + .. data.node.name .. " at " .. minetest.pos_to_string(ipos) + .. " 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.") + return ipos, node, imeta + end end + return ipos, node, imeta end -- Turns the Digtron back into pieces @@ -366,8 +381,8 @@ digtron.deconstruct = function(digtron_id, root_pos, player_name) local inv = digtron.retrieve_inventory(digtron_id) if not (layout and inv) then - minetest.log("error", "Unable to find layout or inventory record for " .. digtron_id - .. ", wiping any remaining metadata for this id to prevent corruption. Sorry!") + minetest.log("error", "digtron.deconstruct was unable to find either layout or inventory record for " .. digtron_id + .. ", deconstruction was impossible. Clearing any other remaining data for this id.") dispose_id(digtron_id) return end @@ -480,3 +495,29 @@ digtron.can_dig = function(pos, digger) end return true end + +digtron.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 + local bbox = retrieve_bounding_box(digtron_id) + if bbox.root then + digtron.deconstruct(digtron_id, bbox.root, "an explosion") + else + 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 deconstructed.") + 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 diff --git a/nodes/node_misc.lua b/nodes/node_misc.lua index 789152f..976b86b 100644 --- a/nodes/node_misc.lua +++ b/nodes/node_misc.lua @@ -33,6 +33,7 @@ minetest.register_node("digtron:structure", { } }, 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. @@ -55,6 +56,7 @@ minetest.register_node("digtron:light", { 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 @@ -78,6 +80,7 @@ minetest.register_node("digtron:panel", { 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 @@ -107,6 +110,7 @@ minetest.register_node("digtron:edge_panel", { }, }, can_dig = digtron.can_dig, + on_blast = digtron.on_blast, }) minetest.register_node("digtron:corner_panel", { @@ -137,4 +141,5 @@ minetest.register_node("digtron:corner_panel", { }, }, can_dig = digtron.can_dig, + on_blast = digtron.on_blast, }) \ No newline at end of file diff --git a/nodes/node_storage.lua b/nodes/node_storage.lua index 75a7cce..586d6da 100644 --- a/nodes/node_storage.lua +++ b/nodes/node_storage.lua @@ -54,6 +54,7 @@ minetest.register_node("digtron:inventory", { 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, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) local meta = minetest.get_meta(pos) @@ -153,6 +154,7 @@ minetest.register_node("digtron:fuelstore", { 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 meta = minetest.get_meta(pos) @@ -274,6 +276,7 @@ minetest.register_node("digtron:combined_storage", { 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, + on_blast = digtron.on_blast, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) local meta = minetest.get_meta(pos) From 03c9a81b6b9688da49f0c963869aa2a7cac95d70 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Mon, 19 Aug 2019 20:56:28 -0600 Subject: [PATCH 13/63] so... much... error checking... --- controller.lua | 1 - functions.lua | 54 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/controller.lua b/controller.lua index 5b5011d..5b729c1 100644 --- a/controller.lua +++ b/controller.lua @@ -95,7 +95,6 @@ minetest.register_node("digtron:controller", { end end, - --TODO: this didn't work when I blew up a digtron with TNT, investigate why preserve_metadata = function(pos, oldnode, oldmeta, drops) for _, dropped in ipairs(drops) do if dropped:get_name() == "digtron:controller" then diff --git a/functions.lua b/functions.lua index 878d56e..7604a99 100644 --- a/functions.lua +++ b/functions.lua @@ -479,6 +479,9 @@ digtron.build_to_world = function(digtron_id, root_pos, player_name) -- inv:set_size(listname, size) -- end end + local bbox = retrieve_bounding_box(digtron_id) + bbox.root = root_pos + persist_bounding_box(digtron_id, bbox) end return permitted @@ -487,15 +490,60 @@ end --------------------------------------------------------------------------------- -- Misc +-- 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. +-- TODO: add protection check? digtron.can_dig = function(pos, digger) local meta = minetest.get_meta(pos) local digtron_id = meta:get_string("digtron_id") - if mod_meta:contains(digtron_id..":layout") then - return false + if digtron_id == "" then + return true end - return true + + local node = minetest.get_node(pos) + + local bbox = retrieve_bounding_box(digtron_id) + local layout = retrieve_layout(digtron_id) + if bbox == nil or bbox.root == nil or layout == nil then + -- Somehow, this belongs to a digtron id that's missing information that should exist in persistence. + local missing = "" + if bbox == nil then missing = missing .. "bounding_box " end + if bbox ~= nil and bbox.root == 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) + -- 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 root_hash = minetest.hash_node_position(bbox.root) + local here_hash = minetest.hash_node_position(pos) + local layout_hash = here_hash - root_hash + origin_hash + 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. digtron.on_blast = function(pos, intensity) if intensity < 1.0 then return end -- The Almighty Digtron ignores weak-ass explosions From 2283cdbbea71e281a49d9d0032052a1ed66053cf Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Tue, 20 Aug 2019 00:29:26 -0600 Subject: [PATCH 14/63] hide digtron_id from clients, change "construct" to "assemble", various other tidbits --- controller.lua | 96 +++++++++++++-------------- depends.txt | 3 +- functions.lua | 174 ++++++++++++++++++++++++++++++++----------------- 3 files changed, 161 insertions(+), 112 deletions(-) diff --git a/controller.lua b/controller.lua index 5b729c1..6768aed 100644 --- a/controller.lua +++ b/controller.lua @@ -2,6 +2,11 @@ local MP = minetest.get_modpath(minetest.get_current_modname()) local S, NS = dofile(MP.."/intllib.lua") +-- 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 player_interacting_with_digtron_pos = {} + local controller_nodebox = { type = "fixed", fixed = { @@ -18,23 +23,24 @@ local controller_nodebox = { }, } -local get_controller_unconstructed_formspec = function(pos, player_name) +local get_controller_unassembled_formspec = function(pos, player_name) local meta = minetest.get_meta(pos) return "size[9,9]" .. "container[0.5,0]" - .. "button[0,0;1,1;construct;Construct]" + .. "button[0,0;1,1;assemble;Assemble]" .. "field[1.2,0.25;2,1;digtron_name;Digtron name;"..meta:get_string("infotext").."]" .. "field_close_on_enter[digtron_name;false]" .. "container_end[]" end -local get_controller_constructed_formspec = function(pos, digtron_id, player_name) +local get_controller_assembled_formspec = function(pos, digtron_id, player_name) digtron.retrieve_inventory(digtron_id) -- ensures the detatched inventory exists and is populated return "size[9,9]" .. "container[0.5,0]" - .. "button[0,0;1,1;deconstruct;Deconstruct]" + .. "button[0,0;1,1;disassemble;Disassemble]" .. "field[1.2,0.25;2,1;digtron_name;Digtron name;"..digtron.get_name(digtron_id).."]" .. "field_close_on_enter[digtron_name;false]" + .. "button[3,0;1,1;move_forward;Move forward]" .. "container_end[]" .. "container[0.5,1]" .. "list[detached:" .. digtron_id .. ";main;0,0;8,2]" -- TODO: paging system for inventory, guard against non-existent listname @@ -141,23 +147,26 @@ minetest.register_node("digtron:controller", { meta:set_string("infotext", title) meta:set_string("digtron_id", digtron_id) + meta:mark_as_private("digtron_id") end, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + if clicker == nil then return end local meta = minetest.get_meta(pos) local digtron_id = meta:get_string("digtron_id") - local player_name - if clicker then player_name = clicker:get_player_name() end + local player_name = clicker:get_player_name() if digtron_id == "" then + player_interacting_with_digtron_pos[player_name] = pos minetest.show_formspec(player_name, - "digtron_controller_unconstructed:"..minetest.pos_to_string(pos)..":"..player_name, - get_controller_unconstructed_formspec(pos, player_name)) + "digtron:controller_unassembled", + get_controller_unassembled_formspec(pos, player_name)) else -- initialized + player_interacting_with_digtron_id[player_name] = digtron_id minetest.show_formspec(player_name, - "digtron_controller_constructed:"..minetest.pos_to_string(pos)..":"..player_name..":"..digtron_id, - get_controller_constructed_formspec(pos, digtron_id, player_name)) + "digtron:controller_assembled", + get_controller_assembled_formspec(pos, digtron_id, player_name)) end end, @@ -167,30 +176,26 @@ minetest.register_node("digtron:controller", { on_blast = digtron.on_blast, }) --- Dealing with an unconstructed Digtron controller +-- Dealing with an unassembled Digtron controller minetest.register_on_player_receive_fields(function(player, formname, fields) - local formname_split = formname:split(":") - if #formname_split ~= 3 or formname_split[1] ~= "digtron_controller_unconstructed" then - return - end - local pos = minetest.string_to_pos(formname_split[2]) - if pos == nil then - minetest.log("error", "[Digtron] Unable to parse position from formspec name " .. formname) - return - end - local name = formname_split[3] - if player:get_player_name() ~= name then + if formname ~= "digtron:controller_unassembled" then return end + local name = player:get_player_name() + local pos = player_interacting_with_digtron_pos[name] + + if pos == nil then return end - if fields.construct then - local digtron_id = digtron.construct(pos, name) + if fields.assemble then + local digtron_id = digtron.assemble(pos, name) if digtron_id then local meta = minetest.get_meta(pos) meta:set_string("digtron_id", digtron_id) + meta:mark_as_private("digtron_id") + player_interacting_with_digtron_id[name] = digtron_id minetest.show_formspec(name, - "digtron_controller_constructed:"..minetest.pos_to_string(pos)..":"..name..":"..digtron_id, - get_controller_constructed_formspec(pos, digtron_id, name)) + "digtron:controller_assembled", + get_controller_assembled_formspec(pos, digtron_id, name)) end end @@ -203,39 +208,30 @@ end) -- Controlling a fully armed and operational Digtron minetest.register_on_player_receive_fields(function(player, formname, fields) - local formname_split = formname:split(":") - if #formname_split ~= 4 or formname_split[1] ~= "digtron_controller_constructed" then + if formname ~= "digtron:controller_assembled" then return end - local pos = minetest.string_to_pos(formname_split[2]) - if pos == nil then - minetest.log("error", "[Digtron] Unable to parse position from formspec name " .. formname) - return - end - local name = formname_split[3] - if player:get_player_name() ~= name then - return - end - local digtron_id = formname_split[4] + local name = player:get_player_name() + local digtron_id = player_interacting_with_digtron_id[name] + if digtron_id == nil then return end - if fields.deconstruct then - minetest.chat_send_all("Deconstructing " .. digtron_id) - - local meta = minetest.get_meta(pos) - local digtron_id = meta:get_string("digtron_id") - if digtron_id == "" then - minetest.log("error", "[Digtron] tried to deconstruct Digtron at pos " - .. minetest.pos_to_string(pos) .. " but it had no digtron_id in the node's metadata") - else - digtron.deconstruct(digtron_id, pos, name) + if fields.disassemble then + local pos = digtron.disassemble(digtron_id, name) + if pos then + player_interacting_with_digtron_pos[name] = pos minetest.show_formspec(name, - "digtron_controller_unconstructed:"..minetest.pos_to_string(pos)..":"..name, - get_controller_unconstructed_formspec(pos, name)) + "digtron:controller_unassembled", + get_controller_unassembled_formspec(pos, name)) end end + if fields.move_forward then + + end + --TODO: this isn't recording the field when using ESC to exit the formspec if fields.key_enter_field == "digtron_name" or fields.digtron_name then + local pos = digtron.get_pos(digtron_id) local meta = minetest.get_meta(pos) meta:set_string("infotext", fields.digtron_name) digtron.set_name(digtron_id, fields.digtron_name) diff --git a/depends.txt b/depends.txt index cad5160..d5c409f 100644 --- a/depends.txt +++ b/depends.txt @@ -1,2 +1,3 @@ default -intllib? \ No newline at end of file +intllib? +creative? \ No newline at end of file diff --git a/functions.lua b/functions.lua index 7604a99..80b1d8e 100644 --- a/functions.lua +++ b/functions.lua @@ -3,6 +3,7 @@ local mod_meta = minetest.get_mod_storage() digtron.layout = {} digtron.adjacent = {} digtron.bounding_box = {} +digtron.pos = {} --minetest.debug(dump(mod_meta:to_table())) @@ -120,17 +121,17 @@ end) -------------------------------------------------------------------------------------- local create_new_id = function() - local last_id = mod_meta:get_int("last_id") -- returns 0 when uninitialized, so 0 will never be a valid digtron_id. - local new_id = last_id + 1 - mod_meta:set_int("last_id", new_id) -- ensure each call to this method gets a unique number - - local digtron_id = "digtron" .. tostring(new_id) + local digtron_id = "digtron" .. tostring(math.random(1, 2^21)) -- TODO: use SecureRandom() + -- 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 local inv = minetest.create_detached_inventory(digtron_id, detached_inventory_callbacks) - return digtron_id, inv end --- Deletes a Digtron record. Note: just throws everything away, this is not digtron.deconstruct. +-- Deletes a Digtron record. Note: just throws everything away, this is not digtron.disassemble. local dispose_id = function(digtron_id) minetest.remove_detached_inventory(digtron_id) digtron.layout[digtron_id] = nil @@ -140,17 +141,22 @@ local dispose_id = function(digtron_id) mod_meta:set_string(digtron_id..":adjacent", "") mod_meta:set_string(digtron_id..":name", "") mod_meta:set_string(digtron_id..":bounding_box", "") + mod_meta:set_string(digtron_id..":pos", "") end -------------------------------------------------------------------------------------------- -- Name +-- Not bothering with a dynamic table store for names, they're just strings with no need for serialization or deserialization digtron.get_name = function(digtron_id) return mod_meta:get_string(digtron_id..":name") end digtron.set_name = function(digtron_id, digtron_name) - mod_meta:set_string(digtron_id..":name", 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 ------------------------------------------------------------------------------------------------------- @@ -186,6 +192,10 @@ local persist_adjacent = get_persist_table_function("adjacent") local retrieve_adjacent = get_retrieve_table_function("adjacent") local persist_bounding_box = get_persist_table_function("bounding_box") local retrieve_bounding_box = get_retrieve_table_function("bounding_box") +local persist_pos = get_persist_table_function("pos") +local retrieve_pos = get_retrieve_table_function("pos") + +digtron.get_pos = retrieve_pos local cardinal_directions = { {x=1,y=0,z=0}, @@ -235,32 +245,32 @@ get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, digtron_adjacent, end -------------------------------------------------------------------------------------------------------- --- Construct and deconstruct +-- assemble and disassemble local origin_hash = minetest.hash_node_position({x=0,y=0,z=0}) -- Returns the id of the new Digtron record, or nil on failure -digtron.construct = function(root_pos, player_name) +digtron.assemble = function(root_pos, player_name) local node = minetest.get_node(root_pos) -- TODO: a more generic test? Not needed with the more generic controller design, as far as I can tell. There's only going to be the one type of controller. if node.name ~= "digtron:controller" then -- Called on an incorrect node - minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(root_pos) + minetest.log("error", "[Digtron] digtron.assemble called with pos " .. minetest.pos_to_string(root_pos) .. " but the node at this location was " .. node.name) return nil end local root_meta = minetest.get_meta(root_pos) if root_meta:contains("digtron_id") then - -- Already constructed. TODO: validate that the digtron_id actually exists as well - minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(root_pos) - .. " but the controller at this location was already part of a constructed Digtron.") + -- 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 root_hash = minetest.hash_node_position(root_pos) local digtron_nodes = {[root_hash] = 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 - local bounding_box = {minp=vector.new(root_pos), maxp=vector.new(root_pos), root = root_pos} + local bounding_box = {minp=vector.new(root_pos), maxp=vector.new(root_pos)} get_all_adjacent_digtron_nodes(root_pos, digtron_nodes, digtron_adjacent, bounding_box, player_name) local digtron_id, digtron_inv = create_new_id(root_pos) @@ -280,7 +290,7 @@ digtron.construct = function(root_pos, player_name) if current_meta_table.fields.digtron_id then -- Trying to incorporate part of an existing digtron, should be impossible. - minetest.log("error", "[Digtron] digtron.construct tried to incorporate a Digtron node of type " + 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) @@ -313,17 +323,18 @@ digtron.construct = function(root_pos, player_name) persist_layout(digtron_id, layout) persist_adjacent(digtron_id, digtron_adjacent) persist_bounding_box(digtron_id, bounding_box) + persist_pos(digtron_id, root_pos) -- 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. for hash, node in pairs(digtron_nodes) do - local digtron_meta + local node_meta if hash == root_hash then - digtron_meta = root_meta -- we're processing the controller, we already have a reference to its meta + node_meta = root_meta -- we're processing the controller, we already have a reference to its meta else - digtron_meta = minetest.get_meta(minetest.get_position_from_hash(hash)) + node_meta = minetest.get_meta(minetest.get_position_from_hash(hash)) end - local inv = digtron_meta:get_inventory() + local inv = node_meta:get_inventory() for listname, items in pairs(inv:get_lists()) do for i = 1, #items do @@ -331,10 +342,13 @@ digtron.construct = function(root_pos, player_name) end end - digtron_meta:set_string("digtron_id", digtron_id) + node_meta:set_string("digtron_id", digtron_id) + node_meta:mark_as_private("digtron_id") end + + minetest.log("action", "Digtron " .. digtron_id .. " assembled at " .. minetest.pos_to_string(root_pos) + .. " by " .. player_name) - minetest.debug("constructed id " .. digtron_id) return digtron_id end @@ -342,38 +356,41 @@ 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, root_hash, hash, data, function_name) - local ipos = minetest.get_position_from_hash(hash + root_hash - origin_hash) - local node = minetest.get_node(ipos) - local imeta = minetest.get_meta(ipos) - local target_digtron_id = imeta:get_string("digtron_id") + local node_pos = minetest.get_position_from_hash(hash + root_hash - origin_hash) + 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") if data.node.name ~= node.name then minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with one of ".. digtron_id .. "'s " - .. data.node.name .. "s at " .. minetest.pos_to_string(ipos) .. " but the node at that location was of type " + .. data.node.name .. "s at " .. minetest.pos_to_string(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", "[Digtron] " .. function_name .. " tried interacting with ".. digtron_id .. "'s " - .. data.node.name .. " at " .. minetest.pos_to_string(ipos) + .. data.node.name .. " at " .. minetest.pos_to_string(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", "[Digtron] " .. function_name .. " tried interacting with ".. digtron_id .. "'s " - .. data.node.name .. " at " .. minetest.pos_to_string(ipos) + .. data.node.name .. " at " .. minetest.pos_to_string(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.") - return ipos, node, imeta + return node_pos, node, node_meta end end - return ipos, node, imeta + return node_pos, node, node_meta end -- Turns the Digtron back into pieces -digtron.deconstruct = function(digtron_id, root_pos, player_name) +digtron.disassemble = function(digtron_id, player_name) + local bbox = retrieve_bounding_box(digtron_id) + local root_pos = retrieve_pos(digtron_id) + local root_meta = minetest.get_meta(root_pos) root_meta:set_string("infotext", digtron.get_name(digtron_id)) @@ -381,8 +398,8 @@ digtron.deconstruct = function(digtron_id, root_pos, player_name) local inv = digtron.retrieve_inventory(digtron_id) if not (layout and inv) then - minetest.log("error", "digtron.deconstruct was unable to find either layout or inventory record for " .. digtron_id - .. ", deconstruction was impossible. Clearing any other remaining data for this id.") + minetest.log("error", "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 @@ -391,15 +408,15 @@ digtron.deconstruct = function(digtron_id, root_pos, player_name) -- Write metadata and inventory to in-world node at this location for hash, data in pairs(layout) do - local ipos, node, imeta = get_valid_data(digtron_id, root_hash, hash, data, "digtron.deconstruct") + local node_pos, node, node_meta = get_valid_data(digtron_id, root_hash, hash, data, "digtron.disassemble") - if ipos then - local iinv = imeta:get_inventory() + if node_pos then + local node_inv = node_meta:get_inventory() for listname, size in pairs(data.meta.inventory) do - iinv:set_size(listname, size) + node_inv:set_size(listname, size) for i, itemstack in ipairs(inv:get_list(listname)) do -- add everything, putting leftovers back in the main inventory - inv:set_stack(listname, i, iinv:add_item(listname, itemstack)) + inv:set_stack(listname, i, node_inv:add_item(listname, itemstack)) end end @@ -407,15 +424,17 @@ digtron.deconstruct = function(digtron_id, root_pos, player_name) -- Ensure node metadata fields are all set, too for field, value in pairs(data.meta.fields) do - imeta:set_string(field, value) + node_meta:set_string(field, value) end -- Clear digtron_id, this node is no longer part of an active digtron - imeta:set_string("digtron_id", "") + node_meta:set_string("digtron_id", "") end end dispose_id(digtron_id) + + return root_pos end -- Removes the in-world nodes of a digtron @@ -435,9 +454,9 @@ digtron.remove_from_world = function(digtron_id, root_pos, player_name) local root_hash = minetest.hash_node_position(root_pos) local nodes_to_destroy = {} for hash, data in pairs(layout) do - local ipos, node, imeta = get_valid_data(digtron_id, root_hash, hash, data, "digtron.destroy") - if ipos then - table.insert(nodes_to_destroy, ipos) + local node_pos, node, node_meta = get_valid_data(digtron_id, root_hash, hash, data, "digtron.destroy") + if node_pos then + table.insert(nodes_to_destroy, node_pos) end end @@ -452,12 +471,12 @@ digtron.build_to_world = function(digtron_id, root_pos, player_name) local permitted = true for hash, data in pairs(layout) do - local ipos = minetest.get_position_from_hash(hash + root_hash - origin_hash) - local node = minetest.get_node(ipos) + local node_pos = minetest.get_position_from_hash(hash + root_hash - origin_hash) + local node = minetest.get_node(node_pos) local node_def = minetest.registered_nodes[node.name] -- TODO: lots of testing needed here if not (node_def and node_def.buildable_to) then - minetest.chat_send_all("not permitted due to " .. node.name .. " at " .. minetest.pos_to_string(ipos)) + minetest.chat_send_all("not permitted due to " .. node.name .. " at " .. minetest.pos_to_string(node_pos)) permitted = false break end @@ -466,22 +485,23 @@ digtron.build_to_world = function(digtron_id, root_pos, player_name) if permitted then -- TODO: voxelmanip might be better here, less likely than with destroy though since metadata needs to be written for hash, data in pairs(layout) do - local ipos = minetest.get_position_from_hash(hash + root_hash - origin_hash) - minetest.set_node(ipos, data.node) - local meta = minetest.get_meta(ipos) - meta:set_string("digtron_id", digtron_id) + local node_pos = minetest.get_position_from_hash(hash + root_hash - origin_hash) + 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 - -- Not needed - local inventories not used by active digtron, will be restored if deconstructed + meta:set_string("digtron_id", digtron_id) + meta:mark_as_private("digtron_id") + -- Not needed - local inventories not used by active digtron, will be restored if disassembled -- local inv = meta:get_inventory() -- for listname, size in pairs(data.meta.inventory) do -- inv:set_size(listname, size) -- end end local bbox = retrieve_bounding_box(digtron_id) - bbox.root = root_pos persist_bounding_box(digtron_id, bbox) + persist_pos(digtron_id, root_pos) end return permitted @@ -503,12 +523,13 @@ digtron.can_dig = function(pos, digger) local node = minetest.get_node(pos) local bbox = retrieve_bounding_box(digtron_id) + local root_pos = retrieve_pos(digtron_id) local layout = retrieve_layout(digtron_id) - if bbox == nil or bbox.root == nil or layout == nil then + if bbox == nil or 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 bbox == nil then missing = missing .. "bounding_box " end - if bbox ~= nil and bbox.root == nil then missing = missing .. "root_pos " end + 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 " @@ -520,7 +541,7 @@ digtron.can_dig = function(pos, digger) return true end - local root_hash = minetest.hash_node_position(bbox.root) + local root_hash = minetest.hash_node_position(root_pos) local here_hash = minetest.hash_node_position(pos) local layout_hash = here_hash - root_hash + origin_hash local layout_data = layout[layout_hash] @@ -550,13 +571,10 @@ digtron.on_blast = function(pos, intensity) local meta = minetest.get_meta(pos) local digtron_id = meta:get_string("digtron_id") if digtron_id ~= "" then - local bbox = retrieve_bounding_box(digtron_id) - if bbox.root then - digtron.deconstruct(digtron_id, bbox.root, "an explosion") - else + 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 deconstructed.") + .. " but didn't have a root position recorded, so it could not be disassembled.") return end end @@ -569,3 +587,37 @@ digtron.on_blast = function(pos, intensity) minetest.remove_node(pos) return drops end + + +------------------------------------------------------------------------------------ +-- Creative trash + +-- This is wrapped in an after() call as a workaround for to https://github.com/minetest/minetest/issues/8827 +if minetest.get_modpath("creative") then + minetest.after(1, function() + if minetest.get_inventory({type="detached", name="creative_trash"}) then + if minetest.remove_detached_inventory("creative_trash") then + -- Create the trash field + local trash = minetest.create_detached_inventory("creative_trash", { + -- Allow the stack to be placed and remove it in on_put() + -- This allows the creative inventory to restore the stack + allow_put = function(inv, listname, index, stack, player) + return stack:get_count() + end, + 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 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 + inv:set_list(listname, {}) + end, + }) + trash:set_size("main", 1) + end + end + end) +end From f14b0a9a42107a3983c11b27fdb2faaf376cf2a3 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Tue, 20 Aug 2019 00:54:18 -0600 Subject: [PATCH 15/63] no need to expose caches globally --- controller.lua | 8 +++--- functions.lua | 67 +++++++++++++++++++++++++++----------------------- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/controller.lua b/controller.lua index 6768aed..bcb869b 100644 --- a/controller.lua +++ b/controller.lua @@ -232,9 +232,11 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) --TODO: this isn't recording the field when using ESC to exit the formspec if fields.key_enter_field == "digtron_name" or fields.digtron_name then local pos = digtron.get_pos(digtron_id) - local meta = minetest.get_meta(pos) - meta:set_string("infotext", fields.digtron_name) - digtron.set_name(digtron_id, fields.digtron_name) + if pos then + local meta = minetest.get_meta(pos) + meta:set_string("infotext", fields.digtron_name) + digtron.set_name(digtron_id, fields.digtron_name) + end end end) diff --git a/functions.lua b/functions.lua index 80b1d8e..0c487a6 100644 --- a/functions.lua +++ b/functions.lua @@ -1,9 +1,6 @@ local mod_meta = minetest.get_mod_storage() -digtron.layout = {} -digtron.adjacent = {} -digtron.bounding_box = {} -digtron.pos = {} +local cache = {} --minetest.debug(dump(mod_meta:to_table())) @@ -132,16 +129,19 @@ local create_new_id = function() end -- Deletes a Digtron record. Note: just throws everything away, this is not digtron.disassemble. +local dispose_callbacks = {} local dispose_id = function(digtron_id) - minetest.remove_detached_inventory(digtron_id) - digtron.layout[digtron_id] = nil - digtron.adjacent[digtron_id] = nil - mod_meta:set_string(digtron_id..":inv", "") - mod_meta:set_string(digtron_id..":layout", "") - mod_meta:set_string(digtron_id..":adjacent", "") + -- name doesn't bother caching mod_meta:set_string(digtron_id..":name", "") - mod_meta:set_string(digtron_id..":bounding_box", "") - mod_meta:set_string(digtron_id..":pos", "") + + -- 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 -------------------------------------------------------------------------------------------- @@ -160,18 +160,24 @@ digtron.set_name = function(digtron_id, digtron_name) end ------------------------------------------------------------------------------------------------------- --- Layout - -local get_persist_table_function = function(identifier) +-- Tables stored to metadata and cached locally + +local get_table_functions = function(identifier) + cache[identifier] = {} + -- add a callback for dispose_id + table.insert(dispose_callbacks, function(digtron_id) + mod_meta:set_string(digtron_id..":"..identifier, "") + cache[identifier][digtron_id] = nil + end) + return function(digtron_id, tbl) + minetest.chat_send_all("persisting " .. identifier .. " " .. digtron_id) mod_meta:set_string(digtron_id..":"..identifier, minetest.serialize(tbl)) - digtron[identifier][digtron_id] = tbl - end -end - -local get_retrieve_table_function = function(identifier) - return function(digtron_id) - local current = digtron[identifier][digtron_id] + cache[identifier][digtron_id] = tbl + end, + function(digtron_id) + minetest.chat_send_all("retrieving " .. identifier .. " " .. digtron_id) + local current = cache[identifier][digtron_id] if current then return current end @@ -179,24 +185,23 @@ local get_retrieve_table_function = function(identifier) if tbl_string ~= "" then current = minetest.deserialize(tbl_string) if current then - digtron[identifier][digtron_id] = current + cache[identifier][digtron_id] = current end return current end end end -local persist_layout = get_persist_table_function("layout") -local retrieve_layout = get_retrieve_table_function("layout") -local persist_adjacent = get_persist_table_function("adjacent") -local retrieve_adjacent = get_retrieve_table_function("adjacent") -local persist_bounding_box = get_persist_table_function("bounding_box") -local retrieve_bounding_box = get_retrieve_table_function("bounding_box") -local persist_pos = get_persist_table_function("pos") -local retrieve_pos = get_retrieve_table_function("pos") +local persist_layout, retrieve_layout = get_table_functions("layout") +local persist_adjacent, retrieve_adjacent = get_table_functions("adjacent") +local persist_bounding_box, retrieve_bounding_box = get_table_functions("bounding_box") +local persist_pos, retrieve_pos = get_table_functions("pos") digtron.get_pos = retrieve_pos +------------------------------------------------------------------------------------------------------- +-- Layout creation helpers + local cardinal_directions = { {x=1,y=0,z=0}, {x=-1,y=0,z=0}, From 292889651cbd38fb36b40923813cb74a766132c9 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Tue, 20 Aug 2019 18:58:32 -0600 Subject: [PATCH 16/63] indicator entities for when Digtron can't be built --- entities.lua | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++ functions.lua | 65 +++++++++------- init.lua | 6 ++ 3 files changed, 247 insertions(+), 25 deletions(-) create mode 100644 entities.lua diff --git a/entities.lua b/entities.lua new file mode 100644 index 0000000..568ad75 --- /dev/null +++ b/entities.lua @@ -0,0 +1,201 @@ +-- 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. +digtron.safe_add_entity = function(pos, name) + success, ret = pcall(minetest.add_entity, pos, name) + if success then return ret else return nil end +end + +local safe_add_entity = digtron.safe_add_entity + +------------------------------------------------------------------------------------------------- +-- For displaying where things get built under which periodicities + +minetest.register_entity("digtron:marker", { + initial_properties = { + visual = "cube", + visual_size = {x=1.05, y=1.05}, + textures = {"digtron_marker_side.png","digtron_marker_side.png","digtron_marker.png","digtron_marker.png","digtron_marker_side.png","digtron_marker_side.png"}, + collisionbox = {-0.525, -0.525, -0.525, 0.525, 0.525, 0.525}, + physical = false, + }, + + 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, +}) + +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 + +minetest.register_entity("digtron:marker_crate_good", { + initial_properties = { + visual = "cube", + visual_size = {x=1.05, y=1.05}, + 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) + 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_bad", { + initial_properties = { + visual = "cube", + visual_size = {x=1.05, y=1.05}, + 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) + self.object:remove() + end, + self) + end, + + on_rightclick=function(self, clicker) + self.object:remove() + end, + + on_punch = function(self, hitter) + self.object:remove() + 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 + +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 + +minetest.register_entity("digtron:builder_item", { + + initial_properties = { + hp_max = 1, + is_visible = true, + visual = "wielditem", + visual_size = {x=0.25, y=0.25}, + collisionbox = {0,0,0,0,0,0}, + physical = false, + textures = {""}, + automatic_rotate = math.pi * 0.25, + }, + + 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 + self.object:remove() + return + end + props.textures = {staticdata} + self.object:set_properties(props) + elseif digtron.create_builder_item ~= nil then + props.textures = {digtron.create_builder_item} + self.object:set_properties(props) + digtron.create_builder_item = nil + else + self.object:remove() + end + end, + + get_staticdata = function(self) + local props = self.object:get_properties() + if props ~= nil and props.textures ~= nil and props.textures[1] ~= nil then + return props.textures[1] + end + return "" + end, +}) diff --git a/functions.lua b/functions.lua index 0c487a6..be8b712 100644 --- a/functions.lua +++ b/functions.lua @@ -469,10 +469,10 @@ digtron.remove_from_world = function(digtron_id, root_pos, player_name) minetest.bulk_set_node(nodes_to_destroy, {name="air"}) end -digtron.build_to_world = function(digtron_id, root_pos, player_name) +-- Tests if a Digtron can be built at the designated location +digtron.is_buildable_to = function(digtron_id, root_pos, player_name, show_visualization) local layout = retrieve_layout(digtron_id) local root_hash = minetest.hash_node_position(root_pos) - local nodes_to_create = {} local permitted = true for hash, data in pairs(layout) do @@ -482,34 +482,49 @@ digtron.build_to_world = function(digtron_id, root_pos, player_name) -- TODO: lots of testing needed here if not (node_def and node_def.buildable_to) then minetest.chat_send_all("not permitted due to " .. node.name .. " at " .. minetest.pos_to_string(node_pos)) - permitted = false - break - end + if not show_visualization then + return false + else + permitted = false + digtron.safe_add_entity(node_pos, "digtron:marker_crate_bad") + end + elseif show_visualization then + digtron.safe_add_entity(node_pos, "digtron:marker_crate_good") + end end + return permitted +end - if permitted then - -- TODO: voxelmanip might be better here, less likely than with destroy though since metadata needs to be written - for hash, data in pairs(layout) do - local node_pos = minetest.get_position_from_hash(hash + root_hash - origin_hash) - 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:mark_as_private("digtron_id") - -- Not needed - local inventories not used by active digtron, will be restored if disassembled --- local inv = meta:get_inventory() --- for listname, size in pairs(data.meta.inventory) do --- inv:set_size(listname, size) --- end +-- Places the Digtron into the world. +digtron.build_to_world = function(digtron_id, root_pos, player_name) + local layout = retrieve_layout(digtron_id) + local root_hash = minetest.hash_node_position(root_pos) + + if not digtron.is_buildable_to(digtron_id, root_pos, player_name, true) then + return false + end + + -- TODO: voxelmanip might be better here, less likely than with destroy though since metadata needs to be written + for hash, data in pairs(layout) do + local node_pos = minetest.get_position_from_hash(hash + root_hash - origin_hash) + 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 - local bbox = retrieve_bounding_box(digtron_id) - persist_bounding_box(digtron_id, bbox) - persist_pos(digtron_id, root_pos) + meta:set_string("digtron_id", digtron_id) + meta:mark_as_private("digtron_id") + -- Not needed - local inventories not used by active digtron, will be restored if disassembled +-- local inv = meta:get_inventory() +-- for listname, size in pairs(data.meta.inventory) do +-- inv:set_size(listname, size) +-- end end + local bbox = retrieve_bounding_box(digtron_id) + persist_bounding_box(digtron_id, bbox) + persist_pos(digtron_id, root_pos) - return permitted + return true end --------------------------------------------------------------------------------- diff --git a/init.lua b/init.lua index c7c5765..9287770 100644 --- a/init.lua +++ b/init.lua @@ -1,8 +1,14 @@ digtron = {} digtron.doc = {} +digtron.config = {} + +digtron.config.marker_crate_bad_duration = 5 +digtron.config.marker_crate_good_duration = 5 + local modpath = minetest.get_modpath(minetest.get_current_modname()) +dofile(modpath.."/entities.lua") dofile(modpath.."/functions.lua") dofile(modpath.."/controller.lua") dofile(modpath.."/nodes/node_misc.lua") From 7f5cf4d0dcedae87fc19bbe5d30deac108cf9f57 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Wed, 21 Aug 2019 01:21:03 -0600 Subject: [PATCH 17/63] add a digger head model --- controller.lua | 37 +- functions.lua | 47 +- init.lua | 3 +- models/digtron_digger.obj | 917 +++++++++++++++++++++++ nodes/node_digger.lua | 42 ++ textures/digtron_drill_head_animated.png | Bin 0 -> 731 bytes 6 files changed, 1024 insertions(+), 22 deletions(-) create mode 100644 models/digtron_digger.obj create mode 100644 nodes/node_digger.lua create mode 100644 textures/digtron_drill_head_animated.png diff --git a/controller.lua b/controller.lua index bcb869b..0560889 100644 --- a/controller.lua +++ b/controller.lua @@ -125,12 +125,27 @@ minetest.register_node("digtron:controller", { -- if not, try moving it up so that the lowest y-coordinate on the Digtron is -- at the y-coordinate of the place clicked on and test again. -- if that fails, show ghost of Digtron and fail to place. - local root_pos = minetest.get_pointed_thing_position(pointed_thing, true) - if digtron.build_to_world(digtron_id, root_pos, player_name) then - -- 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("") + + 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 + + if target_pos then + local success, succeeded, failed = digtron.is_buildable_to(digtron_id, target_pos, player_name) + if success then + digtron.build_to_world(digtron_id, target_pos, player_name) + -- 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 + digtron.show_buildable_nodes(succeeded, failed) + end end return itemstack else @@ -226,7 +241,15 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end if fields.move_forward then - + local pos = digtron.get_pos(digtron_id) + if pos then + local node = minetest.get_node(pos) + if node.name == "digtron:controller" then + local dir = minetest.facedir_to_dir(node.param2) + local dest_pos = vector.add(dir, pos) + digtron.move(digtron_id, dest_pos) + end + end end --TODO: this isn't recording the field when using ESC to exit the formspec diff --git a/functions.lua b/functions.lua index be8b712..6a0ed81 100644 --- a/functions.lua +++ b/functions.lua @@ -470,40 +470,53 @@ digtron.remove_from_world = function(digtron_id, root_pos, player_name) end -- Tests if a Digtron can be built at the designated location -digtron.is_buildable_to = function(digtron_id, root_pos, player_name, show_visualization) +--TODO implement ignore_nodes, needed for ignoring self nodes and nodes that have been flagged as dug +digtron.is_buildable_to = function(digtron_id, root_pos, player_name, ignore_nodes, return_immediately_on_failure) local layout = retrieve_layout(digtron_id) local root_hash = minetest.hash_node_position(root_pos) - + local show_anything = show_successes or show_failures + local succeeded = {} + local failed = {} local permitted = true + for hash, data in pairs(layout) do local node_pos = minetest.get_position_from_hash(hash + root_hash - origin_hash) local node = minetest.get_node(node_pos) local node_def = minetest.registered_nodes[node.name] -- TODO: lots of testing needed here if not (node_def and node_def.buildable_to) then - minetest.chat_send_all("not permitted due to " .. node.name .. " at " .. minetest.pos_to_string(node_pos)) - if not show_visualization then - return false + if return_immediately_on_failure then + return false -- no need to test further, don't return node positions else permitted = false - digtron.safe_add_entity(node_pos, "digtron:marker_crate_bad") + table.insert(failed, node_pos) end - elseif show_visualization then - digtron.safe_add_entity(node_pos, "digtron:marker_crate_good") + elseif not return_immediately_on_failure then + table.insert(succeeded, node_pos) + end + end + + return permitted, succeeded, failed +end + +digtron.show_buildable_nodes = function(succeeded, failed) + if succeeded then + for _, pos in ipairs(succeeded) do + digtron.safe_add_entity(pos, "digtron:marker_crate_good") + end + end + if failed then + for _, pos in ipairs(failed) do + digtron.safe_add_entity(pos, "digtron:marker_crate_bad") end end - return permitted end -- Places the Digtron into the world. digtron.build_to_world = function(digtron_id, root_pos, player_name) local layout = retrieve_layout(digtron_id) local root_hash = minetest.hash_node_position(root_pos) - - if not digtron.is_buildable_to(digtron_id, root_pos, player_name, true) then - return false - end - + -- TODO: voxelmanip might be better here, less likely than with destroy though since metadata needs to be written for hash, data in pairs(layout) do local node_pos = minetest.get_position_from_hash(hash + root_hash - origin_hash) @@ -527,6 +540,12 @@ digtron.build_to_world = function(digtron_id, root_pos, player_name) return true end +digtron.move = function(digtron_id, dest_pos, player_name) + minetest.chat_send_all("move attempt") + +end + + --------------------------------------------------------------------------------- -- Misc diff --git a/init.lua b/init.lua index 9287770..2968f62 100644 --- a/init.lua +++ b/init.lua @@ -12,4 +12,5 @@ dofile(modpath.."/entities.lua") dofile(modpath.."/functions.lua") dofile(modpath.."/controller.lua") dofile(modpath.."/nodes/node_misc.lua") -dofile(modpath.."/nodes/node_storage.lua") \ No newline at end of file +dofile(modpath.."/nodes/node_storage.lua") +dofile(modpath.."/nodes/node_digger.lua") \ 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/nodes/node_digger.lua b/nodes/node_digger.lua new file mode 100644 index 0000000..5aa2c1a --- /dev/null +++ b/nodes/node_digger.lua @@ -0,0 +1,42 @@ +local def = { + description = "Digger", + _doc_items_longdesc = nil, + _doc_items_usagehelp = nil, + 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 = { + 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 + }, + }, + selection_box = { + 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 + }, + }, + paramtype2 = "facedir", + paramtype = "light", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, + sounds = digtron.metal_sounds, +} + +minetest.register_node("digtron:digger", def) \ No newline at end of file diff --git a/textures/digtron_drill_head_animated.png b/textures/digtron_drill_head_animated.png new file mode 100644 index 0000000000000000000000000000000000000000..b4caa2483093806d1358852a9cd2050253b4c0b1 GIT binary patch literal 731 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?31We{epSZZGe6l5>) z^mS!_%puCorYR-D)(;etEOCt}an8@pP0cG|a4t$sEJ;mKD99Ik%f z_~fExf8B;J=Rcm9-({$jDJNHVX-yZ@KU_2|cmt5!o82 zg_2ETuRp9+=u=*&k;0Z^CVlR?@xFDL|L!usw@r;KvYWs0T0!~u-n)6)#t;7fox3d4 z;8?(Q)6me3QEM+mZH~|}IQFkz&*;I+mz>EuZrR&z>u%q^J!37<16$R899WoXXK&oe z*#7LmM$4q$gAv{`%6~5iI~|DX4&q9hBE8}EyBqJnGsMKl7nfFcUTnMk^2qJm@^gxH z!oAxTx_k2kcnSU#X)0C~l=#J%t$QxA$FS!y2gl!uYp=(1C>roFw`v!(F8*eE*dQR( z)X?o%Vv9szakR!&vj(rq`f0pNZm5-RVp(!ynFd!Sqvke2$q7lbAMD-JAaZ+6{J=^l%9|>+mmrpj Date: Thu, 22 Aug 2019 01:13:59 -0600 Subject: [PATCH 18/63] static digger variant for unassembled digtrons --- models/digtron_digger_static.obj | 313 +++++++++++++++++++++++++++++++ nodes/node_digger.lua | 38 +++- 2 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 models/digtron_digger_static.obj 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/nodes/node_digger.lua b/nodes/node_digger.lua index 5aa2c1a..6efa31e 100644 --- a/nodes/node_digger.lua +++ b/nodes/node_digger.lua @@ -39,4 +39,40 @@ local def = { sounds = digtron.metal_sounds, } -minetest.register_node("digtron:digger", def) \ No newline at end of file +minetest.register_node("digtron:digger", def) + +local def_static = { + description = "Digger", + _doc_items_longdesc = nil, + _doc_items_usagehelp = nil, + 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 = { + 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 + }, + }, + selection_box = { + 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 + }, + }, + paramtype2 = "facedir", + paramtype = "light", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, + sounds = digtron.metal_sounds, +} + +minetest.register_node("digtron:digger_static", def_static) From c2959998fd446bc06de4e9f7b408c1c7beea9287 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Thu, 22 Aug 2019 21:16:08 -0600 Subject: [PATCH 19/63] digtron 2.0 can now move --- .gitignore | 3 + controller.lua | 22 ++- entities.lua | 20 ++- functions.lua | 115 ++++++++----- nodes/node_digger.lua | 153 +++++++++--------- nodes/node_misc.lua | 8 +- sounds/{buzzer.ogg => digtron_buzzer.ogg} | Bin ...struction.ogg => digtron_construction.ogg} | Bin sounds/{dingding.ogg => digtron_dingding.ogg} | Bin sounds/{honk.ogg => digtron_honk.ogg} | Bin ...hine1.ogg => digtron_machine_assemble.ogg} | Bin ...e2.ogg => digtron_machine_disassemble.ogg} | Bin sounds/{sploosh.ogg => digtron_sploosh.ogg} | Bin sounds/{squeal.ogg => digtron_squeal.ogg} | Bin ...{steam_puff.ogg => digtron_steam_puff.ogg} | Bin sounds/{truck.ogg => digtron_truck.ogg} | Bin sounds/{whirr.ogg => digtron_whirr.ogg} | Bin ...pwoopwoop.ogg => digtron_woopwoopwoop.ogg} | Bin sounds/license.txt | 22 +-- 19 files changed, 204 insertions(+), 139 deletions(-) rename sounds/{buzzer.ogg => digtron_buzzer.ogg} (100%) rename sounds/{construction.ogg => digtron_construction.ogg} (100%) rename sounds/{dingding.ogg => digtron_dingding.ogg} (100%) rename sounds/{honk.ogg => digtron_honk.ogg} (100%) rename sounds/{machine1.ogg => digtron_machine_assemble.ogg} (100%) rename sounds/{machine2.ogg => digtron_machine_disassemble.ogg} (100%) rename sounds/{sploosh.ogg => digtron_sploosh.ogg} (100%) rename sounds/{squeal.ogg => digtron_squeal.ogg} (100%) rename sounds/{steam_puff.ogg => digtron_steam_puff.ogg} (100%) rename sounds/{truck.ogg => digtron_truck.ogg} (100%) rename sounds/{whirr.ogg => digtron_whirr.ogg} (100%) rename sounds/{woopwoopwoop.ogg => digtron_woopwoopwoop.ogg} (100%) 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/controller.lua b/controller.lua index 0560889..d81db51 100644 --- a/controller.lua +++ b/controller.lua @@ -72,6 +72,7 @@ minetest.register_node("digtron:controller", { }, drawtype = "nodebox", node_box = controller_nodebox, + sounds = default.node_sound_metal_defaults(), -- on_construct = function(pos) -- end, @@ -95,7 +96,10 @@ minetest.register_node("digtron:controller", { end -- call on_dignodes callback if digtron_id ~= "" then - digtron.remove_from_world(digtron_id, pos, player_name) + local removed = digtron.remove_from_world(digtron_id, pos, player_name) + for _, removed_pos in ipairs(removed) do + minetest.check_for_falling(removed_pos) + end else minetest.remove_node(pos) end @@ -139,12 +143,14 @@ minetest.register_node("digtron:controller", { local success, succeeded, failed = digtron.is_buildable_to(digtron_id, target_pos, player_name) if success then digtron.build_to_world(digtron_id, target_pos, player_name) + minetest.sound_play("digtron_machine_assemble", {gain = 0.5, pos=root_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 digtron.show_buildable_nodes(succeeded, failed) + minetest.sound_play("digtron_buzzer", {gain = 0.5, pos=root_pos}) end end return itemstack @@ -226,17 +232,17 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if formname ~= "digtron:controller_assembled" then return end - local name = player:get_player_name() - local digtron_id = player_interacting_with_digtron_id[name] + local player_name = player:get_player_name() + local digtron_id = player_interacting_with_digtron_id[player_name] if digtron_id == nil then return end if fields.disassemble then - local pos = digtron.disassemble(digtron_id, name) + local pos = digtron.disassemble(digtron_id, player_name) if pos then - player_interacting_with_digtron_pos[name] = pos - minetest.show_formspec(name, + player_interacting_with_digtron_pos[player_name] = pos + minetest.show_formspec(player_name, "digtron:controller_unassembled", - get_controller_unassembled_formspec(pos, name)) + get_controller_unassembled_formspec(pos, player_name)) end end @@ -247,7 +253,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if node.name == "digtron:controller" then local dir = minetest.facedir_to_dir(node.param2) local dest_pos = vector.add(dir, pos) - digtron.move(digtron_id, dest_pos) + digtron.move(digtron_id, dest_pos, player_name) end end end diff --git a/entities.lua b/entities.lua index 568ad75..ba1e954 100644 --- a/entities.lua +++ b/entities.lua @@ -1,12 +1,10 @@ -- 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. -digtron.safe_add_entity = function(pos, name) - success, ret = pcall(minetest.add_entity, pos, name) +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 -local safe_add_entity = digtron.safe_add_entity - ------------------------------------------------------------------------------------------------- -- For displaying where things get built under which periodicities @@ -78,6 +76,20 @@ 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 + + minetest.register_entity("digtron:marker_crate_good", { initial_properties = { visual = "cube", diff --git a/functions.lua b/functions.lua index 6a0ed81..286e129 100644 --- a/functions.lua +++ b/functions.lua @@ -164,19 +164,13 @@ end local get_table_functions = function(identifier) cache[identifier] = {} - -- add a callback for dispose_id - table.insert(dispose_callbacks, function(digtron_id) - mod_meta:set_string(digtron_id..":"..identifier, "") - cache[identifier][digtron_id] = nil - end) - return function(digtron_id, tbl) - minetest.chat_send_all("persisting " .. identifier .. " " .. digtron_id) + local persist_func = function(digtron_id, tbl) mod_meta:set_string(digtron_id..":"..identifier, minetest.serialize(tbl)) cache[identifier][digtron_id] = tbl - end, - function(digtron_id) - minetest.chat_send_all("retrieving " .. identifier .. " " .. digtron_id) + end + + local retrieve_func = function(digtron_id) local current = cache[identifier][digtron_id] if current then return current @@ -190,12 +184,22 @@ local get_table_functions = function(identifier) return current end 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_adjacent, retrieve_adjacent = get_table_functions("adjacent") local persist_bounding_box, retrieve_bounding_box = get_table_functions("bounding_box") -local persist_pos, retrieve_pos = get_table_functions("pos") +local persist_pos, retrieve_pos, dispose_pos = get_table_functions("pos") digtron.get_pos = retrieve_pos @@ -315,6 +319,12 @@ digtron.assemble = function(root_pos, player_name) -- store the inventory size so the inventory can be easily recreated current_meta_table.inventory[listname] = #items end + + 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 layout[relative_hash] = {meta = current_meta_table, node = node} @@ -353,6 +363,7 @@ digtron.assemble = function(root_pos, player_name) 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 @@ -361,7 +372,8 @@ 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, root_hash, hash, data, function_name) - local node_pos = minetest.get_position_from_hash(hash + root_hash - origin_hash) + local node_hash = hash + root_hash - origin_hash -- TODO may want to return this as well? + local node_pos = minetest.get_position_from_hash(node_hash) 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") @@ -425,6 +437,11 @@ digtron.disassemble = function(digtron_id, player_name) 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 + -- TODO: special handling for builder node inventories -- Ensure node metadata fields are all set, too @@ -437,13 +454,18 @@ digtron.disassemble = function(digtron_id, player_name) end end + 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 -- 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 digtron.remove_from_world = function(digtron_id, root_pos, player_name) local layout = retrieve_layout(digtron_id) @@ -453,7 +475,7 @@ digtron.remove_from_world = function(digtron_id, root_pos, player_name) local meta = minetest.get_meta(root_pos) meta:set_string("digtron_id", "") dispose_id(digtron_id) - return + return {} end local root_hash = minetest.hash_node_position(root_pos) @@ -466,25 +488,41 @@ digtron.remove_from_world = function(digtron_id, root_pos, player_name) end -- TODO: voxelmanip might be better here? - minetest.bulk_set_node(nodes_to_destroy, {name="air"}) + 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 ---TODO implement ignore_nodes, needed for ignoring self nodes and nodes that have been flagged as dug +--TODO implement ignore_nodes, needed for ignoring nodes that have been flagged as dug digtron.is_buildable_to = function(digtron_id, root_pos, player_name, ignore_nodes, return_immediately_on_failure) local layout = retrieve_layout(digtron_id) + + -- 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_pos = retrieve_pos(digtron_id) + local old_hashes = {} + if old_pos then + local old_root_hash = minetest.hash_node_position(old_pos) + local old_root_minus_origin = old_root_hash - origin_hash + for layout_hash, _ in pairs(layout) do + old_hashes[layout_hash + old_root_minus_origin] = true + end + end + local root_hash = minetest.hash_node_position(root_pos) - local show_anything = show_successes or show_failures + local root_minus_origin = root_hash - origin_hash local succeeded = {} local failed = {} local permitted = true - for hash, data in pairs(layout) do - local node_pos = minetest.get_position_from_hash(hash + root_hash - origin_hash) + for layout_hash, data in pairs(layout) do + local node_hash = layout_hash + root_minus_origin + local node_pos = minetest.get_position_from_hash(node_hash) local node = minetest.get_node(node_pos) local node_def = minetest.registered_nodes[node.name] -- TODO: lots of testing needed here - if not (node_def and node_def.buildable_to) then + if not ((node_def and node_def.buildable_to) or old_hashes[node_hash]) then if return_immediately_on_failure then return false -- no need to test further, don't return node positions else @@ -499,25 +537,11 @@ digtron.is_buildable_to = function(digtron_id, root_pos, player_name, ignore_nod return permitted, succeeded, failed end -digtron.show_buildable_nodes = function(succeeded, failed) - if succeeded then - for _, pos in ipairs(succeeded) do - digtron.safe_add_entity(pos, "digtron:marker_crate_good") - end - end - if failed then - for _, pos in ipairs(failed) do - digtron.safe_add_entity(pos, "digtron:marker_crate_bad") - end - end -end - -- Places the Digtron into the world. digtron.build_to_world = function(digtron_id, root_pos, player_name) local layout = retrieve_layout(digtron_id) local root_hash = minetest.hash_node_position(root_pos) - -- TODO: voxelmanip might be better here, less likely than with destroy though since metadata needs to be written for hash, data in pairs(layout) do local node_pos = minetest.get_position_from_hash(hash + root_hash - origin_hash) minetest.set_node(node_pos, data.node) @@ -527,11 +551,6 @@ digtron.build_to_world = function(digtron_id, root_pos, player_name) end meta:set_string("digtron_id", digtron_id) meta:mark_as_private("digtron_id") - -- Not needed - local inventories not used by active digtron, will be restored if disassembled --- local inv = meta:get_inventory() --- for listname, size in pairs(data.meta.inventory) do --- inv:set_size(listname, size) --- end end local bbox = retrieve_bounding_box(digtron_id) persist_bounding_box(digtron_id, bbox) @@ -542,7 +561,23 @@ end digtron.move = function(digtron_id, dest_pos, player_name) minetest.chat_send_all("move attempt") - + local current_pos = retrieve_pos(digtron_id) + if current_pos == nil then + minetest.chat_send_all("no pos recorded for digtron") + return + end + local permitted, succeeded, failed = digtron.is_buildable_to(digtron_id, dest_pos, player_name) + if permitted then + local removed = digtron.remove_from_world(digtron_id, current_pos, player_name) + digtron.build_to_world(digtron_id, dest_pos, player_name) + minetest.sound_play("digtron_truck", {gain = 0.5, pos=dest_pos}) + for _, removed_pos in ipairs(removed) do + minetest.check_for_falling(removed_pos) + end + else + digtron.show_buildable_nodes({}, failed) + minetest.sound_play("digtron_squeal", {gain = 0.5, pos=current_pos}) + end end diff --git a/nodes/node_digger.lua b/nodes/node_digger.lua index 6efa31e..4cbe02e 100644 --- a/nodes/node_digger.lua +++ b/nodes/node_digger.lua @@ -1,78 +1,87 @@ -local def = { - description = "Digger", - _doc_items_longdesc = nil, - _doc_items_usagehelp = nil, - 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 }, +-- internationalization boilerplate +local MP = minetest.get_modpath(minetest.get_current_modname()) +local S, NS = dofile(MP.."/intllib.lua") + +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, + } }, - collision_box = { - 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 - }, + { name = "digtron_plate.png^digtron_motor.png", backface_culling = true }, + }, + collision_box = { + 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 }, - selection_box = { - 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 - }, + }, + selection_box = { + 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 }, - paramtype2 = "facedir", - paramtype = "light", - groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, - sounds = digtron.metal_sounds, -} - -minetest.register_node("digtron:digger", def) + }, + paramtype2 = "facedir", + paramtype = "light", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, + is_ground_content = false, + sounds = default.node_sound_metal_defaults(), + can_dig = digtron.can_dig, + on_blast = digtron.on_blast, +}) -local def_static = { - description = "Digger", - _doc_items_longdesc = nil, - _doc_items_usagehelp = nil, - 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 }, +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 = { + 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 }, - collision_box = { - 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 - }, + }, + selection_box = { + 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 }, - selection_box = { - 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 - }, - }, - paramtype2 = "facedir", - paramtype = "light", - groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, - sounds = digtron.metal_sounds, -} - -minetest.register_node("digtron:digger_static", def_static) + }, + paramtype2 = "facedir", + paramtype = "light", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, + is_ground_content = false, + sounds = default.node_sound_metal_defaults(), + can_dig = digtron.can_dig, + on_blast = digtron.on_blast, +}) diff --git a/nodes/node_misc.lua b/nodes/node_misc.lua index 976b86b..9ae346d 100644 --- a/nodes/node_misc.lua +++ b/nodes/node_misc.lua @@ -10,7 +10,7 @@ minetest.register_node("digtron:structure", { groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 1}, tiles = {"digtron_plate.png"}, drawtype = "nodebox", - sounds = digtron.metal_sounds, + sounds = default.node_sound_metal_defaults(), climbable = true, walkable = false, paramtype = "light", @@ -69,7 +69,7 @@ minetest.register_node("digtron:panel", { drawtype = "nodebox", paramtype = "light", is_ground_content = false, - sounds = digtron.metal_sounds, + sounds = default.node_sound_metal_defaults(), paramtype2 = "facedir", node_box = { type = "fixed", @@ -93,7 +93,7 @@ minetest.register_node("digtron:edge_panel", { drawtype = "nodebox", paramtype = "light", is_ground_content = false, - sounds = digtron.metal_sounds, + sounds = default.node_sound_metal_defaults(), paramtype2 = "facedir", node_box = { type = "fixed", @@ -122,7 +122,7 @@ minetest.register_node("digtron:corner_panel", { drawtype = "nodebox", paramtype = "light", is_ground_content = false, - sounds = digtron.metal_sounds, + sounds = default.node_sound_metal_defaults(), paramtype2 = "facedir", node_box = { type = "fixed", 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/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/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..245e486 100644 --- a/sounds/license.txt +++ b/sounds/license.txt @@ -1,16 +1,16 @@ 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 Creative Commons Attribution 3.0 license: From 80b95300334dbbd6e358ee7db005093e9345f8d9 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Fri, 23 Aug 2019 23:53:38 -0600 Subject: [PATCH 20/63] add a basic beginning to dig prediction --- controller.lua | 9 ++- functions.lua | 84 +++++++++++++++++++---- textures/digtron_axel_side.png | Bin 238 -> 236 bytes textures/digtron_control.png | Bin 376 -> 356 bytes textures/digtron_core.png | Bin 364 -> 363 bytes textures/digtron_digger_yb_frame.png | Bin 876 -> 870 bytes textures/digtron_drill_head_animated.png | Bin 731 -> 643 bytes textures/digtron_marker.png | Bin 563 -> 553 bytes textures/digtron_motor.png | Bin 457 -> 444 bytes textures/digtron_output.png | Bin 131 -> 129 bytes 10 files changed, 79 insertions(+), 14 deletions(-) diff --git a/controller.lua b/controller.lua index d81db51..0f0699f 100644 --- a/controller.lua +++ b/controller.lua @@ -41,6 +41,7 @@ local get_controller_assembled_formspec = function(pos, digtron_id, player_name) .. "field[1.2,0.25;2,1;digtron_name;Digtron name;"..digtron.get_name(digtron_id).."]" .. "field_close_on_enter[digtron_name;false]" .. "button[3,0;1,1;move_forward;Move forward]" + .. "button[4,0;1,1;test_dig;Test dig]" .. "container_end[]" .. "container[0.5,1]" .. "list[detached:" .. digtron_id .. ";main;0,0;8,2]" -- TODO: paging system for inventory, guard against non-existent listname @@ -96,7 +97,7 @@ minetest.register_node("digtron:controller", { end -- call on_dignodes callback if digtron_id ~= "" then - local removed = digtron.remove_from_world(digtron_id, pos, player_name) + local removed = digtron.remove_from_world(digtron_id, player_name) for _, removed_pos in ipairs(removed) do minetest.check_for_falling(removed_pos) end @@ -258,6 +259,12 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end + if fields.test_dig then + local products, nodes_to_dig, cost = digtron.predict_dig(digtron_id, player_name) + minetest.chat_send_all("products: " .. dump(products)) + minetest.chat_send_all("positions: " .. dump(nodes_to_dig)) + end + --TODO: this isn't recording the field when using ESC to exit the formspec if fields.key_enter_field == "digtron_name" or fields.digtron_name then local pos = digtron.get_pos(digtron_id) diff --git a/functions.lua b/functions.lua index 286e129..e84d6b5 100644 --- a/functions.lua +++ b/functions.lua @@ -466,18 +466,27 @@ end -- 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 -digtron.remove_from_world = function(digtron_id, root_pos, player_name) +digtron.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", "Unable to find layout record for " .. digtron_id + minetest.log("error", "digtron.remove_from_world Unable to find layout record for " .. digtron_id .. ", wiping any remaining metadata for this id to prevent corruption. Sorry!") - local meta = minetest.get_meta(root_pos) - meta:set_string("digtron_id", "") + if root_pos then + local meta = minetest.get_meta(root_pos) + meta:set_string("digtron_id", "") + end dispose_id(digtron_id) return {} end + if not root_pos then + minetest.log("error", "digtron.remove_from_world Unable to find position for " .. digtron_id + .. ", it may have already been removed from the world.") + return {} + end + local root_hash = minetest.hash_node_position(root_pos) local nodes_to_destroy = {} for hash, data in pairs(layout) do @@ -541,9 +550,10 @@ end digtron.build_to_world = function(digtron_id, root_pos, player_name) local layout = retrieve_layout(digtron_id) local root_hash = minetest.hash_node_position(root_pos) + local root_hash_minus_origin = root_hash - origin_hash for hash, data in pairs(layout) do - local node_pos = minetest.get_position_from_hash(hash + root_hash - origin_hash) + local node_pos = minetest.get_position_from_hash(hash + root_hash_minus_origin) minetest.set_node(node_pos, data.node) local meta = minetest.get_meta(node_pos) for field, value in pairs(data.meta.fields) do @@ -561,14 +571,9 @@ end digtron.move = function(digtron_id, dest_pos, player_name) minetest.chat_send_all("move attempt") - local current_pos = retrieve_pos(digtron_id) - if current_pos == nil then - minetest.chat_send_all("no pos recorded for digtron") - return - end local permitted, succeeded, failed = digtron.is_buildable_to(digtron_id, dest_pos, player_name) if permitted then - local removed = digtron.remove_from_world(digtron_id, current_pos, player_name) + local removed = digtron.remove_from_world(digtron_id, player_name) digtron.build_to_world(digtron_id, dest_pos, player_name) minetest.sound_play("digtron_truck", {gain = 0.5, pos=dest_pos}) for _, removed_pos in ipairs(removed) do @@ -576,13 +581,66 @@ digtron.move = function(digtron_id, dest_pos, player_name) end else digtron.show_buildable_nodes({}, failed) - minetest.sound_play("digtron_squeal", {gain = 0.5, pos=current_pos}) + minetest.sound_play("digtron_squeal", {gain = 0.5, pos=dest_pos}) end end +digtron.predict_dig = function(digtron_id, player_name) + local layout = retrieve_layout(digtron_id) + local root_pos = retrieve_pos(digtron_id) + -- TODO standard check for nil returns, not bothering right now because I'm lazy + local root_hash = minetest.hash_node_position(root_pos) + local root_hash_minus_origin = root_hash - origin_hash + + local products = {} + local dug_positions = {} + local cost = 0 + + for hash, data in pairs(layout) do + if data.node.name == "digtron:digger" then -- TODO: something better than this based on group, ideally pre-gather this info on assembly + local node_pos = minetest.get_position_from_hash(hash + root_hash_minus_origin) + local target_pos = vector.add(node_pos, minetest.facedir_to_dir(data.node.param2)) + if not layout[minetest.hash_node_position(target_pos)] then -- check if the digger is pointed inward, if so ignore it. TODO some way to cull these permanently upon assembly, probably factoring in to the "something better than this" above + --TODO protection test, can_dig test, periodicity test + --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 + local target_node = minetest.get_node(target_pos) + + -- TODO: move this into some kind of shared definition + --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 + + local drops = minetest.get_node_drops(target_node.name, "") + for _, drop in ipairs(drops) do + products[drop] = (products[drop] or 0) + 1 + end + table.insert(dug_positions, target_pos) + end + end + end + + return products, dug_positions, cost +end + --------------------------------------------------------------------------------- --- Misc +-- 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. diff --git a/textures/digtron_axel_side.png b/textures/digtron_axel_side.png index aaf91b347f4dc0954779c25dfd7c085f2d88c878..81694fc6dd0c6ce1d12ee3d162e119fb356bf7b0 100644 GIT binary patch delta 92 zcmV-i0Hgoz0qg;gmsLPXL_t&-8KuCt0e~<71HqTc$z<(6DxEQuaC{`*il!oi&;$Zy4 zD8=}i0WQFI7o>#=A@GC)O#sZtC-9yLEbxwjN&o=My9yRa;R?I}0000SL1q`nDt$07y%wXPLJ|S_!~-rfYPuyAr7ZQSa^6O~Fm$(;p>J zH0L`sY0T4xQKL{!dS#bComjD0&07*qoM6N<$f@Y{$7XSbN delta 195 zcmV;!06hQe0_*~iv45sXL_t(|UPX>s4#O}A1)ZdJ>;$|o*|+;2vb3tz_k$i1%&?lK zW&d=%SK}bM+FugZYn!!v{W<`+@)#Q5O(y})JE!Rcke@4|;oJircBc(ztQ}@E*|=Z~ zw_AI{joXtB^nSryE=3G=m5ht`c%)|=8^-$8-67% xRx$GwN297rJ#W_vRh_QHFshz+?4P!&CrKfozD zIHYJ~2?FV&dw2wd(XnxdDUl_qb~)&Oq4?yKjTx#=rom=r=RlZWSX^RuQS-3nmDRN; z;_DlmTQ)hM2)6B=-8~TAuzzqEbR8X^oSr>^Qv{Fa7njbf>zmuVdwhfNgI`3z!NKu| a*gqZjK`TIJF>?$60000-N-YHj7ZCyRSbJ38InbMLfyQL0a zRd!AkLV5Y#0^(7HlG3ua_DU`~t80jEfTrlAv8kB>bGAyhBAS078V>5~;-b4JAZS3T z398)J&mb`{D63#n3Yy>^8s=hTbj)FhV2X+@1^v$uo0znSu4rKBWO`07 g!@t>ozZm@k9rqGJEAMLr!T_AdH?_b diff --git a/textures/digtron_drill_head_animated.png b/textures/digtron_drill_head_animated.png index b4caa2483093806d1358852a9cd2050253b4c0b1..83be6057f188f4c85ed30cdf61213a69a53098ad 100644 GIT binary patch delta 629 zcmV-*0*d|H1%m~U8Gi!+003az3AF$K0d!DIR7L;){}B-pV`F24goKiklE=r#|Ns9e zC@Co@C@Lu_D=RB4EiEoCE-*1LGc!^&G&4IpJ3Ko(JUl!VYo}r<})z{b8+1lFL+1c9J+S=OL+}qsT z+}z*Z-{9ck<$vVl<>lt*=I86`>+9?6@bK^R^Yire_4@ky`}_O-{r&#_{w5|S#l^+O z$Hr=EYHMq2ZES2;S69x?&RSYpTU%RPTwHf|cX)VsdwP0&e0zX^fP#X8t*))Hv9YqU zv9hwVva_K~#8N?b5TBL{Ss~(SB^(wr$(C$F@B`KOfM^iD%u)cUBBYf@Ztd z?+m6ZdPBp&SLuV=uXM-rH4x;}*~gbRsydu4VIUUthU2N%&-!Qq12-2}PB)pkFV`k8 zaDX3~FK%x`iRWqy22NG#Gu%q}lJ5-|kjRwsV}r#Jc{+gs{+7*^YR#_b!yXLSF{~)o z8Eu}s9~kiKF{Vg!cpWJG!hn#&;wjEe1eyE(AOE0#&_C!O^bh(6{j=L2PJw%o#t@QD P00000NkvXXu0mjf<<&{~ delta 718 zcmZo>z0Ep7vYw5BfgzmfXFHJMEbxddW?;}$24TjErS@e&LG}_)Usv|W9HQ)Ono=Tc z{Xikf64!_l=ltB<)VvY~=c3falGGH1^30M91$R&1fbd2>aRvsaP)`@fkc@k0uSNUa zb`WWJSg$8{+nB{ks>@|MqjskG)u|V18$*^j)C-F|a`~A1%i-$xjZZFG_SbFra{l9q z`CW!enR0S8?P0VfA77Ur)~V;@87x0G7XLeTsI93-59m@ zLe%C69fM>4>h+8synM--tmBrw?Y8dr?b|cf0)4Pm?Z<(InRfN|#+{7q&kk&~OzJ%t z;Vq;5_kys~fvD~vuB0i_8*ab5@%}qQOniKCX=Ufdw#zS%+`cV8r&uT4yKSMnH&1|< z;7^gJVpTzjUyRwh=OTLydmeLe{GGV=dOU}s0UvX#c0udnZ>EP00zyp<-Hs);NCXx~ zYg{#J@T#nz#=GQ(TIr^GmL)fqX>e6CYHkyhoRBp8!QMR$BDdG%U;D%ql%xDfX!#Ad z6o;-{7dEi!2#up=O6EXiPY!!-y>MsS$xV%xxsz2B{BEak&bVwA$@FZC@3aQ&LPwp7 zZ>gPT1FHCxc<+9_c_j4|Q|2s*F>BNs+=p diff --git a/textures/digtron_marker.png b/textures/digtron_marker.png index cbd42d5f6875c004a5d2d4c749dabfa772ad0524..41bc1939653e4132d360ae601bdde727185577b4 100644 GIT binary patch delta 180 zcmV;l089U~1gQkDegS`$NklM iD|%A(_G@5M^R_R34HmotIU|Pv0000^=mhpEwnq`9$5XsaD?$>N**@Bm@k&AH8$Jb{=l5m s-TlMk6P{mQ-`57BB)K5@3Q*%rHI+D;qlpCl@ylFI0e!U%=kM z-9u1F7^Xx--@x3$(oU2C2E-hloLyYq#Nh%Go?6;Ex_Xju0VzWxV-r&|X}Exltd+Hm wt(-iJufU+Fq^zQ<#-I)p(ST5z4CDa-UA_vBnDU-mcK`qY diff --git a/textures/digtron_output.png b/textures/digtron_output.png index a34f7f51a9624856da48295fb05f0f5f9263ba79..4e5e9a78ae480ad269371196edfb9b5a6109a27f 100644 GIT binary patch delta 66 zcmZo>Y-F6^X07Jw;uyjaot(hH$g9vGz_CD4iAiK~6qCr~r~?d&2Vyp`OtKQ$%OGUW V_Hat7)^-LU@O1TaS?83{1OQdZ64w9# delta 68 zcmZocjv9p00i_>zopr00xW?z5oCK From 63ea1314ab65c1fb00e4e91d158ceba9d2361ab4 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sat, 24 Aug 2019 00:04:19 -0600 Subject: [PATCH 21/63] remove origin displacement, not really needed --- functions.lua | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/functions.lua b/functions.lua index e84d6b5..612849f 100644 --- a/functions.lua +++ b/functions.lua @@ -256,7 +256,6 @@ end -------------------------------------------------------------------------------------------------------- -- assemble and disassemble -local origin_hash = minetest.hash_node_position({x=0,y=0,z=0}) -- Returns the id of the new Digtron record, or nil on failure digtron.assemble = function(root_pos, player_name) @@ -287,7 +286,7 @@ digtron.assemble = function(root_pos, player_name) local layout = {} for hash, node in pairs(digtron_nodes) do - local relative_hash = hash - root_hash + origin_hash + local relative_hash = hash - root_hash 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 @@ -372,7 +371,7 @@ 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, root_hash, hash, data, function_name) - local node_hash = hash + root_hash - origin_hash -- TODO may want to return this as well? + local node_hash = hash + root_hash -- TODO may want to return this as well? local node_pos = minetest.get_position_from_hash(node_hash) local node = minetest.get_node(node_pos) local node_meta = minetest.get_meta(node_pos) @@ -407,6 +406,11 @@ end digtron.disassemble = function(digtron_id, player_name) local bbox = retrieve_bounding_box(digtron_id) local root_pos = retrieve_pos(digtron_id) + if not root_pos then + minetest.log("error", "digtron.disassemble was unable to find a position for " .. digtron_id + .. ", disassembly was impossible. Has the digtron been removed from world?") + return + end local root_meta = minetest.get_meta(root_pos) root_meta:set_string("infotext", digtron.get_name(digtron_id)) @@ -513,20 +517,18 @@ digtron.is_buildable_to = function(digtron_id, root_pos, player_name, ignore_nod local old_hashes = {} if old_pos then local old_root_hash = minetest.hash_node_position(old_pos) - local old_root_minus_origin = old_root_hash - origin_hash for layout_hash, _ in pairs(layout) do - old_hashes[layout_hash + old_root_minus_origin] = true + old_hashes[layout_hash + old_root_hash] = true end end local root_hash = minetest.hash_node_position(root_pos) - local root_minus_origin = root_hash - origin_hash local succeeded = {} local failed = {} local permitted = true for layout_hash, data in pairs(layout) do - local node_hash = layout_hash + root_minus_origin + local node_hash = layout_hash + root_hash local node_pos = minetest.get_position_from_hash(node_hash) local node = minetest.get_node(node_pos) local node_def = minetest.registered_nodes[node.name] @@ -550,10 +552,9 @@ end digtron.build_to_world = function(digtron_id, root_pos, player_name) local layout = retrieve_layout(digtron_id) local root_hash = minetest.hash_node_position(root_pos) - local root_hash_minus_origin = root_hash - origin_hash for hash, data in pairs(layout) do - local node_pos = minetest.get_position_from_hash(hash + root_hash_minus_origin) + local node_pos = minetest.get_position_from_hash(hash + root_hash) minetest.set_node(node_pos, data.node) local meta = minetest.get_meta(node_pos) for field, value in pairs(data.meta.fields) do @@ -589,9 +590,8 @@ end digtron.predict_dig = function(digtron_id, player_name) local layout = retrieve_layout(digtron_id) local root_pos = retrieve_pos(digtron_id) - -- TODO standard check for nil returns, not bothering right now because I'm lazy + if not (layout and root_pos) then return end -- TODO error messages etc local root_hash = minetest.hash_node_position(root_pos) - local root_hash_minus_origin = root_hash - origin_hash local products = {} local dug_positions = {} @@ -599,7 +599,7 @@ digtron.predict_dig = function(digtron_id, player_name) for hash, data in pairs(layout) do if data.node.name == "digtron:digger" then -- TODO: something better than this based on group, ideally pre-gather this info on assembly - local node_pos = minetest.get_position_from_hash(hash + root_hash_minus_origin) + local node_pos = minetest.get_position_from_hash(hash + root_hash) local target_pos = vector.add(node_pos, minetest.facedir_to_dir(data.node.param2)) if not layout[minetest.hash_node_position(target_pos)] then -- check if the digger is pointed inward, if so ignore it. TODO some way to cull these permanently upon assembly, probably factoring in to the "something better than this" above --TODO protection test, can_dig test, periodicity test @@ -675,7 +675,7 @@ digtron.can_dig = function(pos, digger) local root_hash = minetest.hash_node_position(root_pos) local here_hash = minetest.hash_node_position(pos) - local layout_hash = here_hash - root_hash + origin_hash + local layout_hash = here_hash - root_hash 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 " From a0f47b50bc311291b75b04ee00a0c8f159568806 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sat, 24 Aug 2019 22:22:40 -0600 Subject: [PATCH 22/63] no need to persist derived values. Cache digger targets. --- controller.lua | 6 +- functions.lua | 176 ++++++++++++++++++++++++++++++------------ nodes/node_digger.lua | 4 +- 3 files changed, 133 insertions(+), 53 deletions(-) diff --git a/controller.lua b/controller.lua index 0f0699f..f8b8e14 100644 --- a/controller.lua +++ b/controller.lua @@ -144,14 +144,14 @@ minetest.register_node("digtron:controller", { local success, succeeded, failed = digtron.is_buildable_to(digtron_id, target_pos, player_name) if success then digtron.build_to_world(digtron_id, target_pos, player_name) - minetest.sound_play("digtron_machine_assemble", {gain = 0.5, pos=root_pos}) + 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! + -- human sacrifice, dogs and cats living together, mass hysteria return ItemStack("") else digtron.show_buildable_nodes(succeeded, failed) - minetest.sound_play("digtron_buzzer", {gain = 0.5, pos=root_pos}) + minetest.sound_play("digtron_buzzer", {gain = 0.5, pos=target_pos}) end end return itemstack diff --git a/functions.lua b/functions.lua index 612849f..b7ff875 100644 --- a/functions.lua +++ b/functions.lua @@ -197,8 +197,6 @@ local get_table_functions = function(identifier) end local persist_layout, retrieve_layout = get_table_functions("layout") -local persist_adjacent, retrieve_adjacent = get_table_functions("adjacent") -local persist_bounding_box, retrieve_bounding_box = get_table_functions("bounding_box") local persist_pos, retrieve_pos, dispose_pos = get_table_functions("pos") digtron.get_pos = retrieve_pos @@ -206,28 +204,39 @@ digtron.get_pos = retrieve_pos ------------------------------------------------------------------------------------------------------- -- Layout creation helpers -local cardinal_directions = { - {x=1,y=0,z=0}, - {x=-1,y=0,z=0}, - {x=0,y=1,z=0}, - {x=0,y=-1,z=0}, - {x=0,y=0,z=1}, - {x=0,y=0,z=-1}, +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}, +} +-- 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 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) +-- Turn the cardinal directions into a set of integers you can add to a hash to step in that direction. +local cardinal_dirs_hash = {} +for i, dir in ipairs(cardinal_dirs) do + cardinal_dirs_hash[i] = minetest.hash_node_position(dir) - minetest.hash_node_position({x=0, y=0, z=0}) +end + +-- Given a facedir, returns an index into either cardinal_dirs or cardinal_dirs_hash that +local facedir_to_dir_index = function(param2) + return facedir_to_dir_map[param2 % 32] end -- recursive function searches out all connected unassigned digtron nodes -local get_all_adjacent_digtron_nodes -get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, digtron_adjacent, bounding_box, player_name) - for _, dir in ipairs(cardinal_directions) do +local get_all_digtron_nodes +get_all_digtron_nodes = function(pos, digtron_nodes, digtron_adjacent, player_name) + for _, dir in ipairs(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 @@ -242,8 +251,7 @@ get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, digtron_adjacent, else --test_node.group_value = group_value -- for later ease of reference digtron_nodes[test_hash] = test_node - update_bounding_box(bounding_box, test_pos) - get_all_adjacent_digtron_nodes(test_pos, digtron_nodes, digtron_adjacent, bounding_box, player_name) -- recurse + get_all_digtron_nodes(test_pos, digtron_nodes, digtron_adjacent, player_name) -- recurse end else -- don't record details, the content of this node will change as the digtron moves @@ -253,6 +261,82 @@ get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, digtron_adjacent, 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 hash, data in pairs(layout) do + update_bounding_box(bbox, minetest.get_position_from_hash(hash)) + end + cache_bounding_box[digtron_id] = bbox + return bbox +end + +cache_all_adjacent_pos = {} +cache_all_digger_targets = {} +local refresh_adjacent = function(digtron_id) + local layout = retrieve_layout(digtron_id) + if layout == nil then return nil end + + local adjacent = {} + local adjacent_to_diggers = {} + for hash, data in pairs(layout) do + for _, dir_hash in ipairs(cardinal_dirs_hash) do + local potential_adjacent = hash+dir_hash + if layout[potential_adjacent] == nil then + adjacent[potential_adjacent] = true + end + end + + if minetest.get_item_group(data.node.name, "digtron") == 3 then + local potential_target = hash + cardinal_dirs_hash[facedir_to_dir_index(data.node.param2)] + if layout[potential_target] == nil then + local fields = data.meta.fields + adjacent_to_diggers[potential_target] = {periodicity = fields.periodicity, offset = fields.offset} + end + end + end + cache_all_adjacent_pos[digtron_id] = adjacent + cache_all_digger_targets[digtron_id] = adjacent_to_diggers +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 + +-- 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 +end +table.insert(dispose_callbacks, invalidate_layout_cache) + -------------------------------------------------------------------------------------------------------- -- assemble and disassemble @@ -278,8 +362,7 @@ digtron.assemble = function(root_pos, player_name) local digtron_nodes = {[root_hash] = 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 - local bounding_box = {minp=vector.new(root_pos), maxp=vector.new(root_pos)} - get_all_adjacent_digtron_nodes(root_pos, digtron_nodes, digtron_adjacent, bounding_box, player_name) + get_all_digtron_nodes(root_pos, digtron_nodes, digtron_adjacent, player_name) local digtron_id, digtron_inv = create_new_id(root_pos) @@ -329,14 +412,10 @@ digtron.assemble = function(root_pos, player_name) layout[relative_hash] = {meta = current_meta_table, node = node} end - bounding_box.minp = vector.subtract(bounding_box.minp, root_pos) - bounding_box.maxp = vector.subtract(bounding_box.maxp, root_pos) - digtron.set_name(digtron_id, root_meta:get_string("infotext")) persist_inventory(digtron_id) persist_layout(digtron_id, layout) - persist_adjacent(digtron_id, digtron_adjacent) - persist_bounding_box(digtron_id, bounding_box) + invalidate_layout_cache(digtron_id) persist_pos(digtron_id, root_pos) -- Wipe out the inventories of all in-world nodes, it's stored in the mod_meta now. @@ -563,8 +642,6 @@ digtron.build_to_world = function(digtron_id, root_pos, player_name) meta:set_string("digtron_id", digtron_id) meta:mark_as_private("digtron_id") end - local bbox = retrieve_bounding_box(digtron_id) - persist_bounding_box(digtron_id, bbox) persist_pos(digtron_id, root_pos) return true @@ -597,16 +674,22 @@ digtron.predict_dig = function(digtron_id, player_name) local dug_positions = {} local cost = 0 - for hash, data in pairs(layout) do - if data.node.name == "digtron:digger" then -- TODO: something better than this based on group, ideally pre-gather this info on assembly - local node_pos = minetest.get_position_from_hash(hash + root_hash) - local target_pos = vector.add(node_pos, minetest.facedir_to_dir(data.node.param2)) - if not layout[minetest.hash_node_position(target_pos)] then -- check if the digger is pointed inward, if so ignore it. TODO some way to cull these permanently upon assembly, probably factoring in to the "something better than this" above - --TODO protection test, can_dig test, periodicity test - --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 - local target_node = minetest.get_node(target_pos) + for target_hash, digger_data in pairs(retrieve_all_digger_targets(digtron_id)) do + local target_pos = minetest.get_position_from_hash(target_hash + root_hash) + local target_node = minetest.get_node(target_pos) + local target_name = target_node.name + local targetdef = minetest.registered_nodes[target_name] + --TODO periodicity/offset test + if 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 minetest.is_protected(target_pos, player_name) + then -- TODO: move this into some kind of shared definition --if digtron.config.uses_resources then @@ -627,12 +710,11 @@ digtron.predict_dig = function(digtron_id, player_name) -- end --end - local drops = minetest.get_node_drops(target_node.name, "") - for _, drop in ipairs(drops) do - products[drop] = (products[drop] or 0) + 1 - end - table.insert(dug_positions, target_pos) + local drops = minetest.get_node_drops(target_name, "") + for _, drop in ipairs(drops) do + products[drop] = (products[drop] or 0) + 1 end + table.insert(dug_positions, target_pos) end end @@ -654,13 +736,11 @@ digtron.can_dig = function(pos, digger) local node = minetest.get_node(pos) - local bbox = retrieve_bounding_box(digtron_id) local root_pos = retrieve_pos(digtron_id) local layout = retrieve_layout(digtron_id) - if bbox == nil or root_pos == nil or layout == nil then + 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 bbox == nil then missing = missing .. "bounding_box " end if root_pos == nil then missing = missing .. "root_pos " end if layout == nil then missing = missing .. "layout " end diff --git a/nodes/node_digger.lua b/nodes/node_digger.lua index 4cbe02e..889f7bc 100644 --- a/nodes/node_digger.lua +++ b/nodes/node_digger.lua @@ -41,7 +41,7 @@ minetest.register_node("digtron:digger", { }, paramtype2 = "facedir", paramtype = "light", - groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 3}, is_ground_content = false, sounds = default.node_sound_metal_defaults(), can_dig = digtron.can_dig, @@ -79,7 +79,7 @@ minetest.register_node("digtron:digger_static",{ }, paramtype2 = "facedir", paramtype = "light", - groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 3}, is_ground_content = false, sounds = default.node_sound_metal_defaults(), can_dig = digtron.can_dig, From a74cc1492e15e81a6af8934c805a297738558e6f Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sat, 24 Aug 2019 22:52:02 -0600 Subject: [PATCH 23/63] add setting formspec to disassembled digger --- functions.lua | 2 +- nodes/node_digger.lua | 65 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/functions.lua b/functions.lua index b7ff875..ed165e6 100644 --- a/functions.lua +++ b/functions.lua @@ -308,7 +308,7 @@ local refresh_adjacent = function(digtron_id) local potential_target = hash + cardinal_dirs_hash[facedir_to_dir_index(data.node.param2)] if layout[potential_target] == nil then local fields = data.meta.fields - adjacent_to_diggers[potential_target] = {periodicity = fields.periodicity, offset = fields.offset} + adjacent_to_diggers[potential_target] = {period = fields.period, offset = fields.offset} end end end diff --git a/nodes/node_digger.lua b/nodes/node_digger.lua index 889f7bc..c33de51 100644 --- a/nodes/node_digger.lua +++ b/nodes/node_digger.lua @@ -2,6 +2,38 @@ local MP = minetest.get_modpath(minetest.get_current_modname()) local S, NS = dofile(MP.."/intllib.lua") +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 period @1 offset @2", period, offset)) +end + minetest.register_node("digtron:digger", { description = S("Digtron Digger"), _doc_items_longdesc = nil, @@ -84,4 +116,37 @@ minetest.register_node("digtron:digger_static",{ sounds = default.node_sound_metal_defaults(), can_dig = digtron.can_dig, on_blast = digtron.on_blast, + + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + 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, }) + +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) From cafb2ec5458601bfb2d70ed968edb7455afd4038 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 25 Aug 2019 11:15:50 -0600 Subject: [PATCH 24/63] add config, add material cost to predict_dig --- config.lua | 69 ++++++++++++++++++++++++++++++++++ controller.lua | 1 + functions.lua | 98 ++++++++++++++++++++++++------------------------ init.lua | 8 +--- mod.conf | 9 +++++ settingtypes.txt | 51 +++++++++++++++++++++++++ 6 files changed, 182 insertions(+), 54 deletions(-) create mode 100644 config.lua create mode 100644 mod.conf create mode 100644 settingtypes.txt diff --git a/config.lua b/config.lua new file mode 100644 index 0000000..7e50163 --- /dev/null +++ b/config.lua @@ -0,0 +1,69 @@ +local CONFIG_FILE_PREFIX = "digtron_" + +digtron.config = {} + +local print_settingtypes = false + +local function setting(stype, name, default, description) + local value + if stype == "bool" then + value = minetest.settings:get_bool(CONFIG_FILE_PREFIX..name) + elseif stype == "string" then + value = minetest.settings:get(CONFIG_FILE_PREFIX..name) + elseif stype == "int" or stype == "float" then + value = tonumber(minetest.settings:get(CONFIG_FILE_PREFIX..name)) + end + if value == nil then + value = default + end + digtron.config[name] = value + + if print_settingtypes then + minetest.debug(CONFIG_FILE_PREFIX..name.." ("..description..") "..stype.." "..tostring(default)) + end +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") +digtron.config.particle_effects = particle_effects or particle_effects == nil -- default true + + +setting("int", "maximum_extrusion", 25, "Maximum builder extrusion distance") +setting("float", "cycle_time", 1.0, "Minimum Digtron cycle time") +setting("float", "traction_factor", 3.0, "Traction factor") + +-- fuel costs. For comparison, in the default game: +-- one default tree block is 30 units +-- one coal lump is 40 units +-- one coal block is 370 units (apparently it's slightly more productive making your coal lumps into blocks before burning) +-- 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", 0.5, "Default dig cost") +-- eg, stone +setting("float", "dig_cost_cracky", 1.0, "Cracky dig cost") +-- eg, dirt, sand +setting("float", "dig_cost_crumbly", 0.5, "Crumbly dig cost") +-- eg, wood +setting("float", "dig_cost_choppy", 0.75, "Choppy dig cost") +-- how much fuel is required to build a node +setting("float", "build_cost", 1.0, "Build cost") + +-- How much charge (an RE battery holds 10000 EU) is equivalent to 1 unit of heat produced by burning coal +-- With 100, the battery is 2.5 better than a coal lump, yet 3.7 less powerful than a coal block +-- Being rechargeable should pay off for this "average" performance. +setting("int", "power_ratio", 100, "The electrical charge to 1 coal heat unit conversion ratio") + + +setting("float", "marker_crate_good_duration", 3.0, "Duration that 'good' crate markers last") +setting("float", "marker_crate_bad_duration", 9.0, "Duration that 'bad' crate markers last") + +setting("bool", "emerge_unloaded_mapblocks", true, "When Digtron encounters unloaded map blocks, emerge them.") diff --git a/controller.lua b/controller.lua index f8b8e14..575fead 100644 --- a/controller.lua +++ b/controller.lua @@ -263,6 +263,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local products, nodes_to_dig, cost = digtron.predict_dig(digtron_id, player_name) minetest.chat_send_all("products: " .. dump(products)) minetest.chat_send_all("positions: " .. dump(nodes_to_dig)) + minetest.chat_send_all("cost: " .. cost) end --TODO: this isn't recording the field when using ESC to exit the formspec diff --git a/functions.lua b/functions.lua index ed165e6..8870859 100644 --- a/functions.lua +++ b/functions.lua @@ -573,7 +573,7 @@ digtron.remove_from_world = function(digtron_id, player_name) local root_hash = minetest.hash_node_position(root_pos) local nodes_to_destroy = {} for hash, data in pairs(layout) do - local node_pos, node, node_meta = get_valid_data(digtron_id, root_hash, hash, data, "digtron.destroy") + local node_pos = get_valid_data(digtron_id, root_hash, hash, data, "digtron.destroy") if node_pos then table.insert(nodes_to_destroy, node_pos) end @@ -607,12 +607,17 @@ digtron.is_buildable_to = function(digtron_id, root_pos, player_name, ignore_nod local permitted = true for layout_hash, data in pairs(layout) do + -- Don't use get_valid_data, the Digtron isn't in-world yet local node_hash = layout_hash + root_hash local node_pos = minetest.get_position_from_hash(node_hash) local node = minetest.get_node(node_pos) local node_def = minetest.registered_nodes[node.name] -- TODO: lots of testing needed here - if not ((node_def and node_def.buildable_to) or old_hashes[node_hash]) then + if not ( + (node_def and node_def.buildable_to) + or old_hashes[node_hash]) or + minetest.is_protected(target_pos, player_name) + then if return_immediately_on_failure then return false -- no need to test further, don't return node positions else @@ -633,6 +638,7 @@ digtron.build_to_world = function(digtron_id, root_pos, player_name) local root_hash = minetest.hash_node_position(root_pos) for hash, data in pairs(layout) do + -- Don't use get_valid_data, the Digtron isn't in-world yet local node_pos = minetest.get_position_from_hash(hash + root_hash) minetest.set_node(node_pos, data.node) local meta = minetest.get_meta(node_pos) @@ -680,7 +686,9 @@ digtron.predict_dig = function(digtron_id, player_name) local target_name = target_node.name local targetdef = minetest.registered_nodes[target_name] --TODO periodicity/offset test - if minetest.get_item_group(target_name, "digtron") == 0 and + if + target_name ~= "air" and -- TODO: generalise this somehow for liquids and other undiggables + 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 ( @@ -689,26 +697,27 @@ digtron.predict_dig = function(digtron_id, player_name) targetdef.can_dig(target_pos, minetest.get_player_by_name(player_name)) ) and not minetest.is_protected(target_pos, player_name) - then - - -- TODO: move this into some kind of shared definition - --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 + then + local material_cost = 0 + if digtron.config.uses_resources then + 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 + end + cost = cost + material_cost local drops = minetest.get_node_drops(target_name, "") for _, drop in ipairs(drops) do @@ -804,32 +813,25 @@ end ------------------------------------------------------------------------------------ -- Creative trash --- This is wrapped in an after() call as a workaround for to https://github.com/minetest/minetest/issues/8827 +-- Catch when someone throws a Digtron controller with an ID into the trash, dispose +-- of the persisted layout. if minetest.get_modpath("creative") then - minetest.after(1, function() - if minetest.get_inventory({type="detached", name="creative_trash"}) then - if minetest.remove_detached_inventory("creative_trash") then - -- Create the trash field - local trash = minetest.create_detached_inventory("creative_trash", { - -- Allow the stack to be placed and remove it in on_put() - -- This allows the creative inventory to restore the stack - allow_put = function(inv, listname, index, stack, player) - return stack:get_count() - end, - 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 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 - inv:set_list(listname, {}) - end, - }) - trash:set_size("main", 1) + 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 end diff --git a/init.lua b/init.lua index 2968f62..96cecdb 100644 --- a/init.lua +++ b/init.lua @@ -1,13 +1,9 @@ digtron = {} -digtron.doc = {} - -digtron.config = {} - -digtron.config.marker_crate_bad_duration = 5 -digtron.config.marker_crate_good_duration = 5 +digtron.doc = {} -- TODO: move to doc file local modpath = minetest.get_modpath(minetest.get_current_modname()) +dofile(modpath.."/config.lua") dofile(modpath.."/entities.lua") dofile(modpath.."/functions.lua") dofile(modpath.."/controller.lua") diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..1e351e6 --- /dev/null +++ b/mod.conf @@ -0,0 +1,9 @@ +name = digtron +title = Digtron +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 = 2.0 +depends = default +optional_depends = intllib, creative \ No newline at end of file diff --git a/settingtypes.txt b/settingtypes.txt new file mode 100644 index 0000000..49d8344 --- /dev/null +++ b/settingtypes.txt @@ -0,0 +1,51 @@ +#This can cause digtrons to operate without consuming fuel or building materials, +#though they still check whether they have enough in inventory. +#It's a separate setting from regular creative mode. +digtron_uses_resources (Digtron uses resources) bool true + +#When true, lava counts as protected blocks. +digtron_lava_impassible (Lava is impassible to Digtrons) bool true + +#Sets how much HP damage a digger does. Soft material diggers do half this. +#Set to 0 to disable damage entirely. +digtron_damage_hp (Diggers damage this many hp) int 8 + +#How many seconds a digtron waits between cycles. +#Auto-controllers can make this wait longer, but cannot make it shorter. +digtron_cycle_time (Minimum Digtron cycle time in seconds) float 1.0 0.0 60.0 + +#How many Digtron blocks can be moved for each adjacent +#solid block that the Digtron has traction against +digtron_traction_factor (Digtron traction factor) float 3.0 0.0 1000.0 + +#The maximum extrusion setting permitted for a Digtron builder module. +digtron_maximum_extrusion (Digtron maximum extrusion) int 25 1 100 + +digtron_marker_crate_good_duration (Duration that 'good' crate markers last) float 3.0 0.0 100.0 +digtron_marker_crate_bad_duration (Duration that 'bad' crate markers last) float 9.0 0.0 100.0 + +#When a Digtron encounters unloaded map blocks, cause them to load +#so that the Digtron can continue moving. +digtron_emerge_unloaded_mapblocks (Emerge unloaded map blocks) bool true + +[Fuel costs] + +#eg, stone. +#In a default Minetest game one lump of coal provides 40.0 units of fuel. +digtron_dig_cost_cracky (Fuel cost for digging cracky blocks) float 1.0 0.0 100.0 +# eg, dirt, sand. +#In a default Minetest game one lump of coal provides 40.0 units of fuel. +digtron_dig_cost_crumbly (Fuel cost for digging crumbly blocks) float 0.5 0.0 100.0 +#eg, wood. +#In a default Minetest game one lump of coal provides 40.0 units of fuel. +digtron_dig_cost_choppy (Fuel cost for digging choppy blocks) float 0.75 0.0 100.0 +#Fuel cost to dig a block that doesn't fall into one of the other categories. +#In a default Minetest game one lump of coal provides 40.0 units of fuel. +digtron_dig_cost_default (Fuel cost for digging other block types) float 0.5 0.0 100.0 +#How much fuel is required to build a block +digtron_build_cost (Fuel cost to build one block) float 1.0 0.0 100.0 + +#If the [technic] mod is installed Digtron can draw power from technic batteries. +#A full battery holds 10000 electrical units. This is divided by the power ratio +#setting to convert it into fuel units. +digtron_power_ratio (Electrical charge to coal heat conversion ratio) int 1 1000 100 From 683b941622a8dbcc4d860e705603626dc91f9f33 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 25 Aug 2019 12:33:44 -0600 Subject: [PATCH 25/63] split inventory functions into a separate file, add predictive inventory (untested) --- functions.lua | 127 ++++++------------------------------ init.lua | 2 + inventories.lua | 170 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 107 deletions(-) create mode 100644 inventories.lua diff --git a/functions.lua b/functions.lua index 8870859..6a4e7f4 100644 --- a/functions.lua +++ b/functions.lua @@ -1,4 +1,4 @@ -local mod_meta = minetest.get_mod_storage() +local mod_meta = digtron.mod_meta local cache = {} @@ -9,111 +9,17 @@ local cache = {} -- mod_meta:set_string(field, "") --end ------------------------------------------------------------------------------------- --- Inventory - --- 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" - local stack = inv:get_stack(from_list, from_index) - if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then - return stack:get_count() - 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, -} +local modpath = minetest.get_modpath(minetest.get_current_modname()) --- 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 -digtron.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 +local inventory_functions = dofile(modpath.."/inventories.lua") --- 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 +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 -minetest.register_globalstep(function(dtime) - for digtron_id, _ in pairs(dirty_inventories) do - persist_inventory(digtron_id) - dirty_inventories[digtron_id] = nil - end -end) +digtron.retrieve_inventory = retrieve_inventory -------------------------------------------------------------------------------------- @@ -514,9 +420,17 @@ digtron.disassemble = function(digtron_id, player_name) local node_inv = node_meta:get_inventory() for listname, size in pairs(data.meta.inventory) do node_inv:set_size(listname, size) - for i, itemstack in ipairs(inv:get_list(listname)) do - -- add everything, putting leftovers back in the main inventory - inv:set_stack(listname, i, node_inv:add_item(listname, itemstack)) + 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 @@ -669,7 +583,6 @@ digtron.move = function(digtron_id, dest_pos, player_name) end end - digtron.predict_dig = function(digtron_id, player_name) local layout = retrieve_layout(digtron_id) local root_pos = retrieve_pos(digtron_id) diff --git a/init.lua b/init.lua index 96cecdb..ca14db4 100644 --- a/init.lua +++ b/init.lua @@ -1,6 +1,8 @@ digtron = {} digtron.doc = {} -- TODO: move to doc file +digtron.mod_meta = minetest.get_mod_storage() + local modpath = minetest.get_modpath(minetest.get_current_modname()) dofile(modpath.."/config.lua") diff --git a/inventories.lua b/inventories.lua new file mode 100644 index 0000000..2b772f8 --- /dev/null +++ b/inventories.lua @@ -0,0 +1,170 @@ +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 predictive_inv = minetest.create_detached_inventory("digtron_predictive_"..digtron_id, detached_inventory_callbacks) + predictive_inventory[digtron_id] = predictive_inv + local source_inv = digtron.retrieve_inventory(digtron_id) + + -- Populate predictive inventory with the digtron's contents + for _, listname in ipairs(source_inv:get_lists()) do + predictive_inv:set_size(listname, source_inv:get_size(listname)) + predictive_inv:set_list(listname, source_inv:get_list(listname)) + end + + return predictive_inv +end +-- Copies the predictive inventory's contents into the actual digtron's inventory after success +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 = digtron.retrieve_inventory(digtron_id) + for _, listname in ipairs(predictive_inv:get_lists()) do + source_inv:set_list(listname, predictive_inv:get_list(listname)) + end + dirty_inventories[digtron_id] = true + + minetest.remove_detached_inventory("digtron_predictive_"..digtron_id) + predictive_inventory[digtron_id] = nil + + if not next(predictive_inventory) then + minetest.log("warning", "[Digtron] multiple predictive inventories were in existence, this shouldn't be happening. File an issue with Digtron programmers.") + end +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 not next(predictive_inventory) then + minetest.log("warning", "[Digtron] multiple predictive inventories were in existence, this shouldn't be happening. File an issue with Digtron programmers.") + end +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 From 426cd4d82d204580f2d7a03d1f375ea68d5cd727 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 25 Aug 2019 15:52:35 -0600 Subject: [PATCH 26/63] IT DIGS --- controller.lua | 5 +-- functions.lua | 70 +++++++++++++++++++++++++++++++++--------- inventories.lua | 39 +++++++++++------------ nodes/node_storage.lua | 6 ++-- 4 files changed, 77 insertions(+), 43 deletions(-) diff --git a/controller.lua b/controller.lua index 575fead..18c3c90 100644 --- a/controller.lua +++ b/controller.lua @@ -260,10 +260,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end if fields.test_dig then - local products, nodes_to_dig, cost = digtron.predict_dig(digtron_id, player_name) - minetest.chat_send_all("products: " .. dump(products)) - minetest.chat_send_all("positions: " .. dump(nodes_to_dig)) - minetest.chat_send_all("cost: " .. cost) + digtron.execute_cycle(digtron_id, player_name) end --TODO: this isn't recording the field when using ESC to exit the formspec diff --git a/functions.lua b/functions.lua index 6a4e7f4..6caa4fc 100644 --- a/functions.lua +++ b/functions.lua @@ -19,7 +19,7 @@ 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 -digtron.retrieve_inventory = retrieve_inventory +digtron.retrieve_inventory = retrieve_inventory -- used by formspecs -------------------------------------------------------------------------------------- @@ -134,7 +134,8 @@ for i, dir in ipairs(cardinal_dirs) do cardinal_dirs_hash[i] = minetest.hash_node_position(dir) - minetest.hash_node_position({x=0, y=0, z=0}) end --- Given a facedir, returns an index into either cardinal_dirs or cardinal_dirs_hash that +-- Given a facedir, returns an index into either cardinal_dirs or cardinal_dirs_hash that +-- can be used to determine what this node is "aimed" at local facedir_to_dir_index = function(param2) return facedir_to_dir_map[param2 % 32] end @@ -160,7 +161,7 @@ get_all_digtron_nodes = function(pos, digtron_nodes, digtron_adjacent, player_na get_all_digtron_nodes(test_pos, digtron_nodes, digtron_adjacent, player_name) -- recurse end else - -- don't record details, the content of this node will change as the digtron moves + -- don't record details, just keeping track of Digtron's borders digtron_adjacent[test_hash] = true end end @@ -267,7 +268,9 @@ digtron.assemble = function(root_pos, player_name) local root_hash = minetest.hash_node_position(root_pos) local digtron_nodes = {[root_hash] = 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 + 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, digtron_inv = create_new_id(root_pos) @@ -352,7 +355,6 @@ digtron.assemble = function(root_pos, player_name) return digtron_id 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, root_hash, hash, data, function_name) @@ -500,18 +502,22 @@ digtron.remove_from_world = function(digtron_id, player_name) end -- Tests if a Digtron can be built at the designated location ---TODO implement ignore_nodes, needed for ignoring nodes that have been flagged as dug digtron.is_buildable_to = function(digtron_id, root_pos, player_name, ignore_nodes, return_immediately_on_failure) local layout = retrieve_layout(digtron_id) -- 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_pos = retrieve_pos(digtron_id) - local old_hashes = {} + local ignore_hashes = {} if old_pos then local old_root_hash = minetest.hash_node_position(old_pos) for layout_hash, _ in pairs(layout) do - old_hashes[layout_hash + old_root_hash] = true + ignore_hashes[layout_hash + old_root_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 @@ -529,7 +535,7 @@ digtron.is_buildable_to = function(digtron_id, root_pos, player_name, ignore_nod -- TODO: lots of testing needed here if not ( (node_def and node_def.buildable_to) - or old_hashes[node_hash]) or + or ignore_hashes[node_hash]) or minetest.is_protected(target_pos, player_name) then if return_immediately_on_failure then @@ -568,7 +574,6 @@ digtron.build_to_world = function(digtron_id, root_pos, player_name) end digtron.move = function(digtron_id, dest_pos, player_name) - minetest.chat_send_all("move attempt") local permitted, succeeded, failed = digtron.is_buildable_to(digtron_id, dest_pos, player_name) if permitted then local removed = digtron.remove_from_world(digtron_id, player_name) @@ -583,13 +588,18 @@ digtron.move = function(digtron_id, dest_pos, player_name) end end -digtron.predict_dig = function(digtron_id, player_name) +local predict_dig = function(digtron_id, player_name) + local predictive_inv = get_predictive_inventory(digtron_id) local layout = retrieve_layout(digtron_id) local root_pos = retrieve_pos(digtron_id) - if not (layout and root_pos) then return end -- TODO error messages etc + if not (layout and root_pos and predictive_inv) then + minetest.log("error", "[Digtron] predict_dig failed to retrieve either " + .."a predictive inventory, a layout, or a root position for " .. digtron_id) + return + end local root_hash = minetest.hash_node_position(root_pos) - local products = {} + local leftovers = {} local dug_positions = {} local cost = 0 @@ -634,15 +644,45 @@ digtron.predict_dig = function(digtron_id, player_name) local drops = minetest.get_node_drops(target_name, "") for _, drop in ipairs(drops) do - products[drop] = (products[drop] or 0) + 1 + 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) end end - return products, dug_positions, cost + return leftovers, dug_positions, cost end +digtron.execute_cycle = function(digtron_id, player_name) + local leftovers, nodes_to_dig, cost = predict_dig(digtron_id, player_name) + local root_pos = retrieve_pos(digtron_id) + local root_node = minetest.get_node(root_pos) + local new_pos = vector.add(root_pos, cardinal_dirs[facedir_to_dir_index(root_node.param2)]) + local buildable_to, succeeded, failed = digtron.is_buildable_to(digtron_id, new_pos, player_name, nodes_to_dig, return_immediately_on_failure) + + if buildable_to then + local removed = digtron.remove_from_world(digtron_id, player_name) + minetest.bulk_set_node(nodes_to_dig, {name="air"}) + digtron.build_to_world(digtron_id, new_pos, player_name) + minetest.sound_play("digtron_construction", {gain = 0.5, pos=new_pos}) + for _, removed_pos in ipairs(removed) do + minetest.check_for_falling(removed_pos) + end + for _, dug_pos in ipairs(nodes_to_dig) do + -- TODO: other on-dug callbacks + minetest.check_for_falling(dug_pos) + end + commit_predictive_inventory(digtron_id) + else + digtron.show_buildable_nodes({}, failed) + minetest.sound_play("digtron_squeal", {gain = 0.5, pos=new_pos}) + end +end + + --------------------------------------------------------------------------------- -- Node callbacks diff --git a/inventories.lua b/inventories.lua index 2b772f8..aa2cdda 100644 --- a/inventories.lua +++ b/inventories.lua @@ -110,33 +110,30 @@ 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 + local predictive_inv = minetest.create_detached_inventory("digtron_predictive_"..digtron_id, detached_inventory_callbacks) predictive_inventory[digtron_id] = predictive_inv - local source_inv = digtron.retrieve_inventory(digtron_id) + local source_inv = retrieve_inventory(digtron_id) -- Populate predictive inventory with the digtron's contents - for _, listname in ipairs(source_inv:get_lists()) do - predictive_inv:set_size(listname, source_inv:get_size(listname)) - predictive_inv:set_list(listname, source_inv:get_list(listname)) + 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 --- Copies the predictive inventory's contents into the actual digtron's inventory after success -local commit_predictive_inventory = function(digtron_id) +-- 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] commit_predictive_inventory called for " .. digtron_id + minetest.log("error", "[Digtron] clear_predictive_inventory called for " .. digtron_id .. " but predictive inventory did not exist") return end - local source_inv = digtron.retrieve_inventory(digtron_id) - for _, listname in ipairs(predictive_inv:get_lists()) do - source_inv:set_list(listname, predictive_inv:get_list(listname)) - end - dirty_inventories[digtron_id] = true - minetest.remove_detached_inventory("digtron_predictive_"..digtron_id) predictive_inventory[digtron_id] = nil @@ -144,21 +141,21 @@ local commit_predictive_inventory = function(digtron_id) minetest.log("warning", "[Digtron] multiple predictive inventories were in existence, this shouldn't be happening. File an issue with Digtron programmers.") end end --- Wipes predictive inventory without committing it (eg, on failure of predicted operation) -local clear_predictive_inventory = function(digtron_id) +-- 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] clear_predictive_inventory called for " .. digtron_id + minetest.log("error", "[Digtron] commit_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 not next(predictive_inventory) then - minetest.log("warning", "[Digtron] multiple predictive inventories were in existence, this shouldn't be happening. File an issue with Digtron programmers.") + 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 { diff --git a/nodes/node_storage.lua b/nodes/node_storage.lua index 586d6da..56ba22f 100644 --- a/nodes/node_storage.lua +++ b/nodes/node_storage.lua @@ -24,7 +24,7 @@ minetest.register_node("digtron:inventory", { _doc_items_longdesc = digtron.doc.inventory_longdesc, _doc_items_usagehelp = digtron.doc.inventory_usagehelp, groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 2, tubedevice = 1, tubedevice_receiver = 1}, - sounds = digtron.metal_sounds, + sounds = default.node_sound_metal_defaults(), paramtype2= "facedir", drawtype = "nodebox", node_box = { @@ -112,7 +112,7 @@ minetest.register_node("digtron:fuelstore", { _doc_items_longdesc = digtron.doc.fuelstore_longdesc, _doc_items_usagehelp = digtron.doc.fuelstore_usagehelp, groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 5, tubedevice = 1, tubedevice_receiver = 1}, - sounds = digtron.metal_sounds, + sounds = default.node_sound_metal_defaults(), paramtype2= "facedir", drawtype = "nodebox", node_box = { @@ -220,7 +220,7 @@ minetest.register_node("digtron:combined_storage", { _doc_items_longdesc = digtron.doc.combined_storage_longdesc, _doc_items_usagehelp = digtron.doc.combined_storage_usagehelp, groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 6, tubedevice = 1, tubedevice_receiver = 1}, - sounds = digtron.metal_sounds, + sounds = default.node_sound_metal_defaults(), paramtype2= "facedir", drawtype = "nodebox", node_box = { From b90e5713aa887a32fc041b2f8512fc9030328f34 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 25 Aug 2019 17:16:23 -0600 Subject: [PATCH 27/63] Executing all on-dig callbacks "properly" with a fake player --- class_fakeplayer.lua | 145 +++++++++++++++++++++++++++++++++++++++++++ functions.lua | 81 +++++++++++++++++++----- init.lua | 4 ++ inventories.lua | 2 +- 4 files changed, 217 insertions(+), 15 deletions(-) create mode 100644 class_fakeplayer.lua diff --git a/class_fakeplayer.lua b/class_fakeplayer.lua new file mode 100644 index 0000000..7976cb4 --- /dev/null +++ b/class_fakeplayer.lua @@ -0,0 +1,145 @@ +-- The purpose of this class is to have something that can be passed into callbacks that +-- demand a "Player" object as a parameter and hopefully prevent the mods that have +-- registered with those callbacks from crashing on a nil dereference or bad function +-- call. This is not supposed to be a remotely functional thing, it's just supposed +-- to provide dummy methods and return values of the correct data type for anything that +-- might ignore the false "is_player()" return and go ahead and try to use this thing +-- anyway. + +-- I'm trying to patch holes in bad mod programming, essentially. If a mod is so badly +-- programmed that it crashes anyway there's not a lot else I can do on my end of things. + +DigtronFakePlayer = {} +DigtronFakePlayer.__index = DigtronFakePlayer + +local function return_value(x) + return (function() return x end) +end + +local function return_nil() + return nil +end + +local function return_empty_string() + return "" +end + +local function return_zero() + return 0 +end + +local function return_empty_table() + return {} +end + +function DigtronFakePlayer.update(self, pos, player_name) + self.is_fake_player = ":digtron " .. player_name + self.get_pos = return_value(pos) +end + +function DigtronFakePlayer.create(pos, player_name) + local self = {} + setmetatable(self, DigtronFakePlayer) + + self.is_fake_player = ":digtron " .. player_name + + -- ObjectRef + self.get_pos = return_value(pos) + self.set_pos = return_nil + self.move_to = return_nil + self.punch = return_nil + self.right_click = return_nil + self.get_hp = return_value(10) + self.set_hp = return_nil + self.get_inventory = return_nil -- returns an `InvRef` + self.get_wield_list = return_empty_string + self.get_wield_index = return_value(1) + self.get_wielded_item = return_value(ItemStack(nil)) + self.set_wielded_item = return_value(false) + self.set_armor_groups = return_nil + self.get_armor_groups = return_empty_table + self.set_animation = return_nil + self.get_animation = return_nil -- a set of values, maybe important? + self.set_attach = return_nil + self.get_attach = return_nil + self.set_detach = return_nil + self.set_bone_position = return_nil + self.get_bone_position = return_nil + self.set_properties = return_nil + self.get_properties = return_empty_table + + self.is_player = return_value(false) + + self.get_nametag_attributes = return_empty_table + self.set_nametag_attributes = return_nil + + --LuaEntitySAO + self.set_velocity = return_nil + self.get_velocity = return_value({x=0,y=0,z=0}) + self.set_acceleration = return_nil + self.get_acceleration = return_value({x=0,y=0,z=0}) + self.set_yaw = return_nil + self.get_yaw = return_zero + self.set_texture_mod = return_nil + self.get_texture_mod = return_nil -- maybe important? + self.set_sprite = return_nil + --self.get_entity_name` (**Deprecated**: Will be removed in a future version) + self.get_luaentity = return_nil + + -- Player object + + self.get_player_name = return_empty_string + self.get_player_velocity = return_nil + self.get_look_dir = return_value({x=0,y=1,z=0}) + self.get_look_horizontal = return_zero + self.set_look_horizontal = return_nil + self.get_look_vertical = return_zero + self.set_look_vertical = return_nil + + --self.get_look_pitch`: pitch in radians - Deprecated as broken. Use `get_look_vertical` + --self.get_look_yaw`: yaw in radians - Deprecated as broken. Use `get_look_horizontal` + --self.set_look_pitch(radians)`: sets look pitch - Deprecated. Use `set_look_vertical`. + --self.set_look_yaw(radians)`: sets look yaw - Deprecated. Use `set_look_horizontal`. + self.get_breath = return_value(10) + self.set_breath = return_nil + self.get_attribute = return_nil + self.set_attribute = return_nil + + self.set_inventory_formspec = return_nil + self.get_inventory_formspec = return_empty_string + self.get_player_control = return_value({jump=false, right=false, left=false, LMB=false, RMB=false, sneak=false, aux1=false, down=false, up=false} ) + self.get_player_control_bits = return_zero + + self.set_physics_override = return_nil + self.get_physics_override = return_value({speed = 1, jump = 1, gravity = 1, sneak = true, sneak_glitch = false, new_move = true,}) + + + self.hud_add = return_nil + self.hud_remove = return_nil + self.hud_change = return_nil + self.hud_get = return_nil -- possibly important return value? + self.hud_set_flags = return_nil + self.hud_get_flags = return_value({ hotbar=true, healthbar=true, crosshair=true, wielditem=true, breathbar=true, minimap=true }) + self.hud_set_hotbar_itemcount = return_nil + self.hud_get_hotbar_itemcount = return_zero + self.hud_set_hotbar_image = return_nil + self.hud_get_hotbar_image = return_empty_string + self.hud_set_hotbar_selected_image = return_nil + self.hud_get_hotbar_selected_image = return_empty_string + self.set_sky = return_nil + self.get_sky = return_empty_table -- may need members on this table + + self.set_clouds = return_nil + self.get_clouds = return_value({density = 0, color = "#fff0f0e5", ambient = "#000000", height = 120, thickness = 16, speed = {x=0, y=-2}}) + + self.override_day_night_ratio = return_nil + self.get_day_night_ratio = return_nil + + self.set_local_animation = return_nil + self.get_local_animation = return_empty_table + + self.set_eye_offset = return_nil + self.get_eye_offset = return_value({x=0,y=0,z=0},{x=0,y=0,z=0}) + + return self +end \ No newline at end of file diff --git a/functions.lua b/functions.lua index 6caa4fc..29ea0c2 100644 --- a/functions.lua +++ b/functions.lua @@ -30,8 +30,7 @@ local create_new_id = function() while mod_meta:get_string(digtron_id..":layout") ~= "" do digtron_id = "digtron" .. tostring(math.random(1, 2^21)) end - local inv = minetest.create_detached_inventory(digtron_id, detached_inventory_callbacks) - return digtron_id, inv + return digtron_id end -- Deletes a Digtron record. Note: just throws everything away, this is not digtron.disassemble. @@ -273,7 +272,8 @@ digtron.assemble = function(root_pos, player_name) -- 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, digtron_inv = create_new_id(root_pos) + local digtron_id = create_new_id(root_pos) + local digtron_inv = retrieve_inventory(digtron_id) local layout = {} @@ -536,7 +536,7 @@ digtron.is_buildable_to = function(digtron_id, root_pos, player_name, ignore_nod if not ( (node_def and node_def.buildable_to) or ignore_hashes[node_hash]) or - minetest.is_protected(target_pos, player_name) + minetest.is_protected(node_pos, player_name) then if return_immediately_on_failure then return false -- no need to test further, don't return node positions @@ -656,27 +656,80 @@ local predict_dig = function(digtron_id, player_name) return leftovers, dug_positions, cost end +-- Removes nodes and records node info so on-dig 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 + digtron.execute_cycle = function(digtron_id, player_name) local leftovers, nodes_to_dig, cost = predict_dig(digtron_id, player_name) - local root_pos = retrieve_pos(digtron_id) - local root_node = minetest.get_node(root_pos) - local new_pos = vector.add(root_pos, cardinal_dirs[facedir_to_dir_index(root_node.param2)]) - local buildable_to, succeeded, failed = digtron.is_buildable_to(digtron_id, new_pos, player_name, nodes_to_dig, return_immediately_on_failure) + local old_root_pos = retrieve_pos(digtron_id) + local root_node = minetest.get_node(old_root_pos) + local new_root_pos = vector.add(old_root_pos, cardinal_dirs[facedir_to_dir_index(root_node.param2)]) + local buildable_to, succeeded, failed = digtron.is_buildable_to(digtron_id, new_root_pos, player_name, nodes_to_dig) if buildable_to then + digtron.fake_player:update(old_root_pos, player_name) local removed = digtron.remove_from_world(digtron_id, player_name) - minetest.bulk_set_node(nodes_to_dig, {name="air"}) - digtron.build_to_world(digtron_id, new_pos, player_name) - minetest.sound_play("digtron_construction", {gain = 0.5, pos=new_pos}) + + local nodes_dug = get_and_remove_nodes(nodes_to_dig) + + 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(new_root_pos) + .. " operated by by " .. player_name) + end + + digtron.build_to_world(digtron_id, new_root_pos, player_name) + minetest.sound_play("digtron_construction", {gain = 0.5, pos=new_root_pos}) + + -- 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 - for _, dug_pos in ipairs(nodes_to_dig) do - -- TODO: other on-dug callbacks - minetest.check_for_falling(dug_pos) + + -- Execute various on-dig callbacks for the nodes that Digtron dug + -- Must be called after digtron.build_to_world because it triggers falling nodes + 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 + commit_predictive_inventory(digtron_id) else + clear_predictive_inventory(digtron_id) digtron.show_buildable_nodes({}, failed) minetest.sound_play("digtron_squeal", {gain = 0.5, pos=new_pos}) end diff --git a/init.lua b/init.lua index ca14db4..52393aa 100644 --- a/init.lua +++ b/init.lua @@ -5,6 +5,10 @@ digtron.mod_meta = minetest.get_mod_storage() local modpath = minetest.get_modpath(minetest.get_current_modname()) + +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.."/config.lua") dofile(modpath.."/entities.lua") dofile(modpath.."/functions.lua") diff --git a/inventories.lua b/inventories.lua index aa2cdda..b902a7b 100644 --- a/inventories.lua +++ b/inventories.lua @@ -137,7 +137,7 @@ local clear_predictive_inventory = function(digtron_id) minetest.remove_detached_inventory("digtron_predictive_"..digtron_id) predictive_inventory[digtron_id] = nil - if not next(predictive_inventory) then + 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 From 7b2b29687cdbd847d4c189dab17b8bc883986f3d Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 25 Aug 2019 17:44:05 -0600 Subject: [PATCH 28/63] pull some code out into subroutines --- functions.lua | 81 ++++++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/functions.lua b/functions.lua index 29ea0c2..e5ee3da 100644 --- a/functions.lua +++ b/functions.lua @@ -656,8 +656,8 @@ local predict_dig = function(digtron_id, player_name) return leftovers, dug_positions, cost end --- Removes nodes and records node info so on-dig callbacks can be called later -local get_and_remove_nodes = function(nodes_to_dig) +-- Removes nodes and records node info so execute_dug_callbacks can be called later +local get_and_remove_nodes = function(nodes_to_dig, player_name) local ret = {} for _, pos in ipairs(nodes_to_dig) do local record = {} @@ -667,9 +667,45 @@ local get_and_remove_nodes = function(nodes_to_dig) minetest.remove_node(pos) table.insert(ret, record) end + + 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(new_root_pos) + .. " operated by by " .. player_name) + end + return ret end +local execute_dug_callbacks = function(dug_data) + -- 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 + digtron.execute_cycle = function(digtron_id, player_name) local leftovers, nodes_to_dig, cost = predict_dig(digtron_id, player_name) local old_root_pos = retrieve_pos(digtron_id) @@ -679,24 +715,17 @@ digtron.execute_cycle = function(digtron_id, player_name) if buildable_to then digtron.fake_player:update(old_root_pos, player_name) - local removed = digtron.remove_from_world(digtron_id, player_name) - local nodes_dug = get_and_remove_nodes(nodes_to_dig) + -- Removing old nodes + local removed = digtron.remove_from_world(digtron_id, player_name) + local nodes_dug = get_and_remove_nodes(nodes_to_dig, 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(new_root_pos) - .. " operated by by " .. player_name) - end - + -- Building new Digtron digtron.build_to_world(digtron_id, new_root_pos, player_name) minetest.sound_play("digtron_construction", {gain = 0.5, pos=new_root_pos}) + -- TODO add build portion of the cycle + -- 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. @@ -704,28 +733,8 @@ digtron.execute_cycle = function(digtron_id, player_name) minetest.check_for_falling(removed_pos) end - -- Execute various on-dig callbacks for the nodes that Digtron dug -- Must be called after digtron.build_to_world because it triggers falling nodes - 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 + execute_dug_callbacks(nodes_dug) commit_predictive_inventory(digtron_id) else From 1333748b37af97ad7481655a2163a1a7eaa75fae Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 25 Aug 2019 17:49:52 -0600 Subject: [PATCH 29/63] further subroutine breakdown --- functions.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/functions.lua b/functions.lua index e5ee3da..c0f5a4f 100644 --- a/functions.lua +++ b/functions.lua @@ -657,7 +657,7 @@ local predict_dig = function(digtron_id, player_name) end -- Removes nodes and records node info so execute_dug_callbacks can be called later -local get_and_remove_nodes = function(nodes_to_dig, player_name) +local get_and_remove_nodes = function(nodes_to_dig) local ret = {} for _, pos in ipairs(nodes_to_dig) do local record = {} @@ -667,7 +667,10 @@ local get_and_remove_nodes = function(nodes_to_dig, player_name) 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" @@ -675,14 +678,12 @@ local get_and_remove_nodes = function(nodes_to_dig, player_name) pluralized = "nodes" end minetest.log("action", nodes_dug_count .. " " .. pluralized .. " dug by " - .. digtron_id .. " near ".. minetest.pos_to_string(new_root_pos) + .. digtron_id .. " near ".. minetest.pos_to_string(root_pos) .. " operated by by " .. player_name) end - - return ret end -local execute_dug_callbacks = function(dug_data) +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 @@ -719,6 +720,7 @@ digtron.execute_cycle = function(digtron_id, player_name) -- Removing old nodes local removed = digtron.remove_from_world(digtron_id, player_name) 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) -- Building new Digtron digtron.build_to_world(digtron_id, new_root_pos, player_name) From beed475fc79a733390830756a47abedc14c53dd5 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 25 Aug 2019 20:09:09 -0600 Subject: [PATCH 30/63] begin reintroducing builder nodes --- controller.lua | 6 + entities.lua | 9 +- functions.lua | 21 +++- init.lua | 3 +- nodes/node_builder.lua | 254 +++++++++++++++++++++++++++++++++++++++ nodes/node_digger.lua | 1 + nodes/node_storage.lua | 39 ++++-- sounds/digtron_error.ogg | Bin 0 -> 11504 bytes 8 files changed, 311 insertions(+), 22 deletions(-) create mode 100644 nodes/node_builder.lua create mode 100644 sounds/digtron_error.ogg diff --git a/controller.lua b/controller.lua index 18c3c90..ef8282e 100644 --- a/controller.lua +++ b/controller.lua @@ -173,7 +173,13 @@ minetest.register_node("digtron:controller", { 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() diff --git a/entities.lua b/entities.lua index ba1e954..26ba9c0 100644 --- a/entities.lua +++ b/entities.lua @@ -160,11 +160,10 @@ 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() + local meta = minetest.get_meta(pos) + local item = meta:get_string("builder_item") + if item ~= "" then + digtron.create_builder_item = item minetest.add_entity(pos,"digtron:builder_item") end end diff --git a/functions.lua b/functions.lua index c0f5a4f..c212ff1 100644 --- a/functions.lua +++ b/functions.lua @@ -394,7 +394,7 @@ digtron.disassemble = function(digtron_id, player_name) local bbox = retrieve_bounding_box(digtron_id) local root_pos = retrieve_pos(digtron_id) if not root_pos then - minetest.log("error", "digtron.disassemble was unable to find a position for " .. digtron_id + 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 @@ -406,7 +406,7 @@ digtron.disassemble = function(digtron_id, player_name) local inv = digtron.retrieve_inventory(digtron_id) if not (layout and inv) then - minetest.log("error", "digtron.disassemble was unable to find either layout or inventory record for " .. digtron_id + 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 @@ -470,7 +470,7 @@ digtron.remove_from_world = function(digtron_id, player_name) local root_pos = retrieve_pos(digtron_id) if not layout then - minetest.log("error", "digtron.remove_from_world Unable to find layout record for " .. digtron_id + 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) @@ -481,7 +481,7 @@ digtron.remove_from_world = function(digtron_id, player_name) end if not root_pos then - minetest.log("error", "digtron.remove_from_world Unable to find position for " .. digtron_id + 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 {} end @@ -826,6 +826,19 @@ digtron.on_blast = function(pos, intensity) 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. +digtron.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 diff --git a/init.lua b/init.lua index 52393aa..61ad2ab 100644 --- a/init.lua +++ b/init.lua @@ -15,4 +15,5 @@ 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") \ No newline at end of file +dofile(modpath.."/nodes/node_digger.lua") +dofile(modpath.."/nodes/node_builder.lua") \ No newline at end of file diff --git a/nodes/node_builder.lua b/nodes/node_builder.lua new file mode 100644 index 0000000..9f1eea4 --- /dev/null +++ b/nodes/node_builder.lua @@ -0,0 +1,254 @@ +-- internationalization boilerplate +local MP = minetest.get_modpath(minetest.get_current_modname()) +local S, NS = dofile(MP.."/intllib.lua") + + +-- Note: builders go in group 4 + +-- TODO make this global +local player_interacting_with_builder_pos = {} + +local get_formspec = function(pos) + 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") + local extrusion = meta:get_int("extrusion") + local build_facing = meta:get_int("build_facing") + local item_name = meta:get_string("build_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 .. "]" .. + "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 .. "]" .. + "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 .. "]" .. + "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") .. "]" .. + "field[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[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[detached:digtron:builder_item;main]" +end + +---------------------------------------------------------------------- +-- Detached inventory for setting the builder item + +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() + + -- Ignore unknown items + if minetest.registered_items[item] == nil 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") + if digtron_id ~= "" then + minetest.log("warning", "[Digtron] builder detached inventory had player " .. player_name + .. " attempt to set " .. item .. " at " .. minetest.pos_to_string(pos) .. + " but the builder node at that location was already assembled into " .. digtron_id) + return 0 + end + + meta:set_string("build_item", item) + minetest.show_formspec(player_name, "digtron:builder", get_formspec(pos)) + + return 0 + end, + allow_take = function(inv, listname, index, stack, player) + return 0 + end, +-- on_move = function(inv, from_list, from_index, to_list, to_index, count, player) +-- end, +-- on_take = function(inv, listname, index, stack, player) +-- end, +-- on_put = function(inv, listname, index, stack, player) +-- 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") + if digtron_id ~= "" then + minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=account.name}) + minetest.chat_send_player(player_name, "This Digtron is active, interact with it via the controller node.") + return + end + + player_interacting_with_builder_pos[player_name] = pos + minetest.show_formspec(player_name, + "digtron:builder", + get_formspec(pos)) +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 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 then + 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 = 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("build_facing", 0) + meta:set_int("extrusion", 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, + + can_dig = digtron.can_dig, + on_blast = digtron.on_blast, +}) \ No newline at end of file diff --git a/nodes/node_digger.lua b/nodes/node_digger.lua index c33de51..24bdbdc 100644 --- a/nodes/node_digger.lua +++ b/nodes/node_digger.lua @@ -2,6 +2,7 @@ local MP = minetest.get_modpath(minetest.get_current_modname()) local S, NS = dofile(MP.."/intllib.lua") +-- TODO: make global local player_interacting_with_digtron_pos = {} local get_formspec = function(pos, player_name) diff --git a/nodes/node_storage.lua b/nodes/node_storage.lua index 56ba22f..9a28347 100644 --- a/nodes/node_storage.lua +++ b/nodes/node_storage.lua @@ -6,9 +6,6 @@ local S, NS = dofile(MP.."/intllib.lua") local get_inventory_formspec = function(pos, player_name) return "size[8,9.3]" .. - default.gui_bg .. - default.gui_bg_img .. - default.gui_slots .. "label[0,0;" .. S("Inventory items") .. "]" .. "list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;0,0.6;8,4;]" .. "list[current_player;main;0,5.15;8,1;]" .. @@ -57,6 +54,13 @@ minetest.register_node("digtron:inventory", { 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 digtron_id = meta:get("digtron_id") local player_name = clicker:get_player_name() @@ -65,7 +69,8 @@ minetest.register_node("digtron:inventory", { "digtron_inventory:"..minetest.pos_to_string(pos)..":"..player_name, get_inventory_formspec(pos, player_name)) else - minetest.chat_send_player(clicker:get_player_name(), "This Digtron is active, interact with its inventory via the controller node.") + minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=account.name}) + minetest.chat_send_player(clicker:get_player_name(), "This Digtron is active, interact with it via the controller node.") end end, @@ -94,9 +99,6 @@ minetest.register_node("digtron:inventory", { local get_fuelstore_formspec = function(pos, player_name) return "size[8,9.3]" .. - default.gui_bg .. - default.gui_bg_img .. - default.gui_slots .. "label[0,0;" .. S("Fuel items") .. "]" .. "list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";fuel;0,0.6;8,4;]" .. "list[current_player;main;0,5.15;8,1;]" .. @@ -157,6 +159,13 @@ minetest.register_node("digtron:fuelstore", { 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 digtron_id = meta:get("digtron_id") local player_name = clicker:get_player_name() @@ -165,7 +174,8 @@ minetest.register_node("digtron:fuelstore", { "digtron_fuelstore:"..minetest.pos_to_string(pos)..":"..player_name, get_fuelstore_formspec(pos, player_name)) else - minetest.chat_send_player(clicker:get_player_name(), "This Digtron is active, interact with its inventory via the controller node.") + minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=account.name}) + minetest.chat_send_player(clicker:get_player_name(), "This Digtron is active, interact with it via the controller node.") end end, @@ -200,9 +210,6 @@ minetest.register_node("digtron:fuelstore", { -- local get_combined_formspec = function(pos, player_name) return "size[8,9.9]" .. - default.gui_bg .. - default.gui_bg_img .. - default.gui_slots .. "label[0,0;" .. S("Inventory items") .. "]" .. "list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;0,0.6;8,3;]" .. "label[0,3.5;" .. S("Fuel items") .. "]" .. @@ -279,6 +286,13 @@ minetest.register_node("digtron:combined_storage", { 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 digtron_id = meta:get("digtron_id") local player_name = clicker:get_player_name() @@ -287,7 +301,8 @@ minetest.register_node("digtron:combined_storage", { "digtron_combined_storage:"..minetest.pos_to_string(pos)..":"..player_name, get_combined_formspec(pos, player_name)) else - minetest.chat_send_player(clicker:get_player_name(), "This Digtron is active, interact with its inventory via the controller node.") + minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=account.name}) + minetest.chat_send_player(clicker:get_player_name(), "This Digtron is active, interact with it via the controller node.") end end, -- diff --git a/sounds/digtron_error.ogg b/sounds/digtron_error.ogg new file mode 100644 index 0000000000000000000000000000000000000000..d1ae7c953723886e0459c05a760a01bf93715e4d GIT binary patch literal 11504 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 Date: Sun, 25 Aug 2019 22:12:05 -0600 Subject: [PATCH 31/63] IT BUILDS --- entities.lua | 31 +++++++--- functions.lua | 136 ++++++++++++++++++++++++++++++++++++++--- nodes/node_builder.lua | 35 ++++++----- nodes/node_storage.lua | 6 +- 4 files changed, 174 insertions(+), 34 deletions(-) diff --git a/entities.lua b/entities.lua index 26ba9c0..28a830f 100644 --- a/entities.lua +++ b/entities.lua @@ -159,12 +159,17 @@ digtron.remove_builder_item = function(pos) end digtron.update_builder_item = function(pos) - digtron.remove_builder_item(pos) + local node = minetest.get_node(pos) + if minetest.get_node_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("builder_item") + local item = meta:get_string("item") if item ~= "" then digtron.create_builder_item = item - minetest.add_entity(pos,"digtron:builder_item") + minetest.add_entity(target_pos,"digtron:builder_item") end end @@ -174,7 +179,7 @@ minetest.register_entity("digtron:builder_item", { 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 = {""}, @@ -185,12 +190,24 @@ minetest.register_entity("digtron:builder_item", { 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 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_node_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 index c212ff1..f4b6663 100644 --- a/functions.lua +++ b/functions.lua @@ -117,6 +117,7 @@ local cardinal_dirs = { {x= 0, y=-1, z= 0}, {x= 0, y=1, z= 0}, } +digtron.cardinal_dirs = cardinal_dirs -- used by builder entities as well -- Mapping from facedir value to index in cardinal_dirs. local facedir_to_dir_map = { [0]=1, 2, 3, 4, @@ -196,12 +197,14 @@ 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 = {} local adjacent_to_diggers = {} + local adjacent_to_builders = {} for hash, data in pairs(layout) do for _, dir_hash in ipairs(cardinal_dirs_hash) do local potential_adjacent = hash+dir_hash @@ -216,10 +219,23 @@ local refresh_adjacent = function(digtron_id) local fields = data.meta.fields adjacent_to_diggers[potential_target] = {period = fields.period, offset = fields.offset} end - end + end + if minetest.get_item_group(data.node.name, "digtron") == 4 then + local potential_target = hash + cardinal_dirs_hash[facedir_to_dir_index(data.node.param2)] + if layout[potential_target] == nil then + local fields = data.meta.fields + adjacent_to_builders[potential_target] = { + period = fields.period, + offset = fields.offset, + item = fields.item, + facing = fields.facing, + } + 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] @@ -233,6 +249,12 @@ local retrieve_all_digger_targets = function(digtron_id) 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 @@ -240,13 +262,13 @@ 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 digtron.assemble = function(root_pos, player_name) local node = minetest.get_node(root_pos) @@ -462,6 +484,9 @@ digtron.disassemble = function(digtron_id, player_name) 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 @@ -588,13 +613,15 @@ digtron.move = function(digtron_id, dest_pos, player_name) end end +------------------------------------------------------------------------------------ +-- Digging + local predict_dig = function(digtron_id, player_name) local predictive_inv = get_predictive_inventory(digtron_id) - local layout = retrieve_layout(digtron_id) local root_pos = retrieve_pos(digtron_id) - if not (layout and root_pos and predictive_inv) then + if not (root_pos and predictive_inv) then minetest.log("error", "[Digtron] predict_dig failed to retrieve either " - .."a predictive inventory, a layout, or a root position for " .. digtron_id) + .."a predictive inventory or a root position for " .. digtron_id) return end local root_hash = minetest.hash_node_position(root_pos) @@ -707,14 +734,106 @@ local execute_dug_callbacks = function(nodes_dug) end end +------------------------------------------------------------------------------------------------------ +-- Building + +-- need to provide new_pos because Digtron moves before building +local predict_build = function(digtron_id, new_pos, player_name, ignore_nodes) + 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 root_hash = minetest.hash_node_position(new_pos) + + 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 target_hash, builder_data in pairs(retrieve_all_builder_targets(digtron_id)) do + local target_pos = minetest.get_position_from_hash(target_hash + root_hash) + local target_node = minetest.get_node(target_pos) + local target_name = target_node.name + local targetdef = minetest.registered_nodes[target_name] + --TODO periodicity/offset test + if + ignore_hashes[target_hash] or + (targetdef ~= nil + and targetdef.buildable_to + and not minetest.is_protected(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, + }) + end + end + + return missing_items, built_nodes, cost +end + +local build_nodes = function(built_nodes) + for _, build_info in ipairs(built_nodes) do + -- TODO: much more complicated than this, see hacked place_item method and stuff + minetest.set_node(build_info.pos, build_info.node) + end +end + +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 + digtron.execute_cycle = function(digtron_id, player_name) - local leftovers, nodes_to_dig, cost = predict_dig(digtron_id, player_name) + local leftovers, nodes_to_dig, dig_cost = predict_dig(digtron_id, player_name) local old_root_pos = retrieve_pos(digtron_id) local root_node = minetest.get_node(old_root_pos) local new_root_pos = vector.add(old_root_pos, cardinal_dirs[facedir_to_dir_index(root_node.param2)]) + -- TODO: convert nodes_to_dig into a hash map here and pass that in to reduce duplication? local buildable_to, succeeded, failed = digtron.is_buildable_to(digtron_id, new_root_pos, player_name, nodes_to_dig) + local missing_items, built_nodes, build_cost = predict_build(digtron_id, new_root_pos, player_name, nodes_to_dig) - if buildable_to then + if buildable_to and next(missing_items) == nil then digtron.fake_player:update(old_root_pos, player_name) -- Removing old nodes @@ -726,7 +845,7 @@ digtron.execute_cycle = function(digtron_id, player_name) digtron.build_to_world(digtron_id, new_root_pos, player_name) minetest.sound_play("digtron_construction", {gain = 0.5, pos=new_root_pos}) - -- TODO add build portion of the cycle + build_nodes(built_nodes) -- 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. @@ -737,6 +856,7 @@ digtron.execute_cycle = function(digtron_id, player_name) -- Must be called after digtron.build_to_world because it triggers falling nodes execute_dug_callbacks(nodes_dug) + execute_built_callbacks(built_nodes) commit_predictive_inventory(digtron_id) else diff --git a/nodes/node_builder.lua b/nodes/node_builder.lua index 9f1eea4..81fe0b6 100644 --- a/nodes/node_builder.lua +++ b/nodes/node_builder.lua @@ -15,8 +15,8 @@ local get_formspec = function(pos) if period < 1 then period = 1 end local offset = meta:get_int("offset") local extrusion = meta:get_int("extrusion") - local build_facing = meta:get_int("build_facing") - local item_name = meta:get_string("build_item") + local facing = meta:get_int("facing") + local item_name = meta:get_string("item") return "size[8,5.2]" .. "item_image[0,0;1,1;" .. item_name .. "]".. @@ -30,8 +30,8 @@ local get_formspec = function(pos) "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") .. "]" .. - "field[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.") .. "]" .. + "field[5.3,0.8;1,0.1;facing;" .. S("Facing") .. ";" .. facing .. "]" .. + "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 &\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;]" .. @@ -78,7 +78,8 @@ local inv = minetest.create_detached_inventory("digtron:builder_item", { return 0 end - meta:set_string("build_item", item) + meta:set_string("item", item) + digtron.update_builder_item(pos) minetest.show_formspec(player_name, "digtron:builder", get_formspec(pos)) return 0 @@ -108,7 +109,7 @@ local builder_on_rightclick = function(pos, node, clicker, itemstack, pointed_th local digtron_id = meta:get_string("digtron_id") if digtron_id ~= "" then - minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=account.name}) + minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=player_name}) minetest.chat_send_player(player_name, "This Digtron is active, interact with it via the controller node.") return end @@ -135,7 +136,7 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields) local meta = minetest.get_meta(pos) local period = tonumber(fields.period) local offset = tonumber(fields.offset) - local build_facing = tonumber(fields.build_facing) + local facing = tonumber(fields.facing) local extrusion = tonumber(fields.extrusion) if period and period > 0 then @@ -148,16 +149,16 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields) else offset = meta:get_int("offset") end - if build_facing and build_facing >= 0 and build_facing < 24 then + if facing and facing >= 0 and 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)) + if facing < 6 then + meta:set_int("facing", math.floor(facing)) -- wallmounted facings only run from 0-5 end else - meta:set_int("build_facing", math.floor(build_facing)) + meta:set_int("facing", math.floor(facing)) end end if extrusion and extrusion > 0 and extrusion <= digtron.config.maximum_extrusion then @@ -178,19 +179,19 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields) -- 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) +-- meta:set_int("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 - + digtron.update_builder_item(pos) end) --- Builds objects in the targeted node. This is a complicated beastie. +-- Builds objects in the targeted node. minetest.register_node("digtron:builder", { description = S("Digtron Builder Module"), _doc_items_longdesc = digtron.doc.builder_longdesc, @@ -235,14 +236,16 @@ minetest.register_node("digtron:builder", { 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("facing", 0) meta:set_int("extrusion", 1) end, on_rightclick = builder_on_rightclick, on_destruct = function(pos) - digtron.remove_builder_item(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) diff --git a/nodes/node_storage.lua b/nodes/node_storage.lua index 9a28347..e4be73d 100644 --- a/nodes/node_storage.lua +++ b/nodes/node_storage.lua @@ -69,7 +69,7 @@ minetest.register_node("digtron:inventory", { "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=account.name}) + minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=player_name}) minetest.chat_send_player(clicker:get_player_name(), "This Digtron is active, interact with it via the controller node.") end end, @@ -174,7 +174,7 @@ minetest.register_node("digtron:fuelstore", { "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=account.name}) + minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=player_name}) minetest.chat_send_player(clicker:get_player_name(), "This Digtron is active, interact with it via the controller node.") end end, @@ -301,7 +301,7 @@ minetest.register_node("digtron:combined_storage", { "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=account.name}) + minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=player_name}) minetest.chat_send_player(clicker:get_player_name(), "This Digtron is active, interact with it via the controller node.") end end, From 256ec951e2e6f686e064eaa71a5b4e22028a3928 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 25 Aug 2019 23:49:04 -0600 Subject: [PATCH 32/63] bring back the hacked item place function --- functions.lua | 38 ++++---- init.lua | 24 +++++- nodes/node_builder.lua | 24 ++++-- util_item_place_node.lua | 181 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 242 insertions(+), 25 deletions(-) create mode 100644 util_item_place_node.lua diff --git a/functions.lua b/functions.lua index f4b6663..6aeebcf 100644 --- a/functions.lua +++ b/functions.lua @@ -119,14 +119,7 @@ local cardinal_dirs = { } digtron.cardinal_dirs = cardinal_dirs -- used by builder entities as well -- 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_dir_map = digtron.facedir_to_dir_map -- Turn the cardinal directions into a set of integers you can add to a hash to step in that direction. local cardinal_dirs_hash = {} @@ -225,10 +218,11 @@ local refresh_adjacent = function(digtron_id) if layout[potential_target] == nil then local fields = data.meta.fields adjacent_to_builders[potential_target] = { - period = fields.period, - offset = fields.offset, + period = tonumber(fields.period) or 1, + offset = tonumber(fields.offset) or 0, item = fields.item, - facing = fields.facing, + facing = tonumber(fields.facing) or 0, + extrusion = tonumber(fields.extrusion) or 1, } end end @@ -783,6 +777,8 @@ local predict_build = function(digtron_id, new_pos, player_name, ignore_nodes) cost = cost + digtron.config.build_cost end + -- TODO handle extrusion + table.insert(built_nodes, { pos = target_pos, node = {name=item, param2=facing }, @@ -795,10 +791,17 @@ local predict_build = function(digtron_id, new_pos, player_name, ignore_nodes) end local build_nodes = function(built_nodes) + local leftovers = {} for _, build_info in ipairs(built_nodes) do - -- TODO: much more complicated than this, see hacked place_item method and stuff - minetest.set_node(build_info.pos, build_info.node) - end + 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 returned_stack:get_count() > 0 then + table.insert(leftovers, returned_stack) + end + end + return leftovers end local execute_built_callbacks = function(built_nodes) @@ -825,7 +828,7 @@ end -- Execute cycle digtron.execute_cycle = function(digtron_id, player_name) - local leftovers, nodes_to_dig, dig_cost = predict_dig(digtron_id, player_name) + local dig_leftovers, nodes_to_dig, dig_cost = predict_dig(digtron_id, player_name) local old_root_pos = retrieve_pos(digtron_id) local root_node = minetest.get_node(old_root_pos) local new_root_pos = vector.add(old_root_pos, cardinal_dirs[facedir_to_dir_index(root_node.param2)]) @@ -845,7 +848,8 @@ digtron.execute_cycle = function(digtron_id, player_name) digtron.build_to_world(digtron_id, new_root_pos, player_name) minetest.sound_play("digtron_construction", {gain = 0.5, pos=new_root_pos}) - build_nodes(built_nodes) + local build_leftovers = build_nodes(built_nodes, player_name) + -- There shouldn't normally be build_leftovers, but it's possible. -- 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. @@ -857,6 +861,8 @@ digtron.execute_cycle = function(digtron_id, player_name) -- Must be called after digtron.build_to_world because it triggers falling nodes execute_dug_callbacks(nodes_dug) execute_built_callbacks(built_nodes) + + -- TODO try putting dig_leftovers and build_leftovers into the inventory one last time before ejecting it commit_predictive_inventory(digtron_id) else diff --git a/init.lua b/init.lua index 61ad2ab..5df8ef8 100644 --- a/init.lua +++ b/init.lua @@ -1,15 +1,33 @@ digtron = {} digtron.doc = {} -- TODO: move to doc file + +-- 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. + + + digtron.mod_meta = minetest.get_mod_storage() local modpath = minetest.get_modpath(minetest.get_current_modname()) +dofile(modpath.."/config.lua") - -dofile(modpath .. "/class_fakeplayer.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(modpath.."/config.lua") dofile(modpath.."/entities.lua") dofile(modpath.."/functions.lua") dofile(modpath.."/controller.lua") diff --git a/nodes/node_builder.lua b/nodes/node_builder.lua index 81fe0b6..ef201bb 100644 --- a/nodes/node_builder.lua +++ b/nodes/node_builder.lua @@ -78,6 +78,16 @@ local inv = minetest.create_detached_inventory("digtron:builder_item", { return 0 end + local stack_def = minetest.registered_nodes[item] + if not stack_def and not digtron.whitelisted_on_place(item) then + return 0 -- don't allow craft items unless their on_place is whitelisted. + 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) + end + meta:set_string("item", item) digtron.update_builder_item(pos) minetest.show_formspec(player_name, "digtron:builder", get_formspec(pos)) @@ -140,29 +150,31 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields) local extrusion = tonumber(fields.extrusion) if period and period > 0 then - meta:set_int("period", math.floor(tonumber(fields.period))) + meta:set_int("period", math.floor(period)) else period = meta:get_int("period") end if offset then - meta:set_int("offset", math.floor(tonumber(fields.offset))) + meta:set_int("offset", math.floor(offset)) else offset = meta:get_int("offset") end if facing and facing >= 0 and facing < 24 then - local inv = meta:get_inventory() - local target_item = inv:get_stack("main",1) + local target_item = ItemStack(meta:get_string("item")) if target_item:get_definition().paramtype2 == "wallmounted" then if facing < 6 then - meta:set_int("facing", math.floor(facing)) + meta:set_int("facing", facing) -- wallmounted facings only run from 0-5 end else meta:set_int("facing", math.floor(facing)) end + else + facing = meta:get_int("facing") end + if extrusion and extrusion > 0 and extrusion <= digtron.config.maximum_extrusion then - meta:set_int("extrusion", math.floor(tonumber(fields.extrusion))) + meta:set_int("extrusion", math.floor(extrusion)) else extrusion = meta:get_int("extrusion") end diff --git a/util_item_place_node.lua b/util_item_place_node.lua new file mode 100644 index 0000000..ba6ff37 --- /dev/null +++ b/util_item_place_node.lua @@ -0,0 +1,181 @@ +-- The default minetest.item_place_node from item.lua was hard to work with given some of the details +-- of how it handled pointed_thing. It also didn't work right with default:torch and seeds. It was simpler to +-- just copy it here and chop out the special cases that were causing problems, and add some special handling. +-- for nodes that define on_place + +-- This specific file is therefore licensed under the LGPL 2.1 + +--GNU Lesser General Public License, version 2.1 +--Copyright (C) 2011-2016 celeron55, Perttu Ahola +--Copyright (C) 2011-2016 Various Minetest developers and contributors + +--This program is free software; you can redistribute it and/or modify it under the terms +--of the GNU Lesser General Public License as published by the Free Software Foundation; +--either version 2.1 of the License, or (at your option) any later version. + +--This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +--without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +--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 + +digtron.whitelisted_on_place = function (item_name) + for listed_item, value in pairs(digtron.builder_on_place_items) do + if item_name == listed_item then return value end + end + + for prefix, value in pairs(digtron.builder_on_place_prefixes) do + if has_prefix(item_name, prefix) then return value end + end + + if minetest.get_item_group(item_name, "digtron_on_place") > 0 then return true end + + return false +end + +local function copy_pointed_thing(pointed_thing) + return { + type = pointed_thing.type, + above = vector.new(pointed_thing.above), + under = vector.new(pointed_thing.under), + } +end + +local function check_attached_node(p, n) + local def = minetest.registered_nodes[n.name] + local d = {x = 0, y = 0, z = 0} + if def.paramtype2 == "wallmounted" then + -- The fallback vector here is in case 'wallmounted to dir' is nil due + -- to voxelmanip placing a wallmounted node without resetting a + -- pre-existing param2 value that is out-of-range for wallmounted. + -- The fallback vector corresponds to param2 = 0. + d = minetest.wallmounted_to_dir(n.param2) or {x = 0, y = 1, z = 0} + else + d.y = -1 + end + local p2 = vector.add(p, d) + local nn = minetest.get_node(p2).name + local def2 = minetest.registered_nodes[nn] + if def2 and not def2.walkable then + return false + end + return true +end + +digtron.item_place_node = function(itemstack, placer, place_to, param2) + local item_name = itemstack:get_name() + local def = itemstack:get_definition() + if (not def) or (param2 < 0) or (def.paramtype2 == "wallmounted" and param2 > 5) or (param2 > 23) then -- validate parameters + return itemstack, false + end + + local pointed_thing = {} + pointed_thing.type = "node" + pointed_thing.above = {x=place_to.x, y=place_to.y, z=place_to.z} + pointed_thing.under = {x=place_to.x, y=place_to.y - 1, z=place_to.z} + + -- Handle node-specific on_place calls as best we can. + if def.on_place and def.on_place ~= minetest.nodedef_default.on_place and digtron.whitelisted_on_place(item_name) then + if def.paramtype2 == "facedir" then + pointed_thing.under = vector.add(place_to, minetest.facedir_to_dir(param2)) + elseif def.paramtype2 == "wallmounted" then + pointed_thing.under = vector.add(place_to, minetest.wallmounted_to_dir(param2)) + end + + -- pass a copy of the item stack parameter because on_place might modify it directly and then we can't tell if we succeeded or not + -- though note that some mods do "creative_mode" handling within their own on_place methods, which makes it impossible for Digtron + -- to know what to do in that case - if you're in creative_mode Digtron will place such items but it will think it failed and not + -- deduct them from inventory no matter what Digtron's settings are. Unfortunate, but not very harmful and I have no workaround. + local returnstack, success = def.on_place(ItemStack(itemstack), placer, pointed_thing) + if returnstack and returnstack:get_count() < itemstack:get_count() then success = true end -- some mods neglect to return a success condition + if success then + -- Override the param2 value to force it to be what Digtron wants + local placed_node = minetest.get_node(place_to) + placed_node.param2 = param2 + minetest.set_node(place_to, placed_node) + end + + return returnstack, success + end + + if minetest.registered_nodes[item_name] == nil then + -- Permitted craft items are handled by the node-specific on_place call, above. + -- if we are a craft item and we get here then we're not whitelisted and we should fail. + -- Note that builder settings should be filtering out craft items like this before we get here, + -- but this will protect us just in case. + return itemstack, false + end + + local oldnode = minetest.get_node_or_nil(place_to) + + --this should never happen, digtron is testing for adjacent unloaded nodes before getting here. + if not oldnode then + minetest.log("info", placer:get_player_name() .. " tried to place" + .. " node in unloaded position " .. minetest.pos_to_string(place_to) + .. " using a digtron.") + return itemstack, false + end + + local newnode = {name = def.name, param1 = 0, param2 = param2} + if def.place_param2 ~= nil then + newnode.param2 = def.place_param2 + end + + -- Check if the node is attached and if it can be placed there + if minetest.get_item_group(def.name, "attached_node") ~= 0 and + not check_attached_node(place_to, newnode) then + minetest.log("action", "attached node " .. def.name .. + " can not be placed at " .. minetest.pos_to_string(place_to)) + return itemstack, false + end + + -- Add node and update + minetest.add_node(place_to, newnode) + + local take_item = true + + -- Run callback, using genuine player for per-node definition. + if def.after_place_node then + -- Deepcopy place_to and pointed_thing because callback can modify it + local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z} + local pointed_thing_copy = copy_pointed_thing(pointed_thing) + if def.after_place_node(place_to_copy, placer, itemstack, + pointed_thing_copy) then + take_item = false + end + end + + -- Run script hook, using fake_player to take the blame. + -- Note that fake_player:update is called in the DigtronLayout class's "create" function, + -- which is called before Digtron does any of this building stuff, so it's not necessary + -- to update it here. + local _, callback + for _, callback in ipairs(minetest.registered_on_placenodes) do + -- Deepcopy pos, node and pointed_thing because callback can modify them + local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z} + local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2} + local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2} + local pointed_thing_copy = copy_pointed_thing(pointed_thing) + if callback(place_to_copy, newnode_copy, digtron.fake_player, oldnode_copy, itemstack, pointed_thing_copy) then + take_item = false + end + end + + if take_item then + itemstack:take_item() + end + return itemstack, true +end \ No newline at end of file From a410187d3a6d05ffa1aa114c91402bc9600ac8cf Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Mon, 26 Aug 2019 19:36:27 -0600 Subject: [PATCH 33/63] extrusion, and account for diggers/builders pointing at the same target node --- entities.lua | 12 +++--- functions.lua | 93 +++++++++++++++++++++++------------------- nodes/node_builder.lua | 18 ++++++-- nodes/node_digger.lua | 6 ++- nodes/node_storage.lua | 6 +-- 5 files changed, 79 insertions(+), 56 deletions(-) diff --git a/entities.lua b/entities.lua index 28a830f..5b1fe03 100644 --- a/entities.lua +++ b/entities.lua @@ -18,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) @@ -101,8 +101,8 @@ minetest.register_entity("digtron:marker_crate_good", { }, 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) @@ -128,8 +128,8 @@ minetest.register_entity("digtron:marker_crate_bad", { }, 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) diff --git a/functions.lua b/functions.lua index 6aeebcf..2a8bb22 100644 --- a/functions.lua +++ b/functions.lua @@ -195,7 +195,7 @@ local refresh_adjacent = function(digtron_id) local layout = retrieve_layout(digtron_id) if layout == nil then return nil end - local adjacent = {} + local adjacent = {} -- all adjacent nodes. TODO: if implementing traction wheels, won't be needed local adjacent_to_diggers = {} local adjacent_to_builders = {} for hash, data in pairs(layout) do @@ -207,22 +207,30 @@ local refresh_adjacent = function(digtron_id) end if minetest.get_item_group(data.node.name, "digtron") == 3 then - local potential_target = hash + cardinal_dirs_hash[facedir_to_dir_index(data.node.param2)] - if layout[potential_target] == nil then + local dir_hash = cardinal_dirs_hash[facedir_to_dir_index(data.node.param2)] + local potential_target = hash + dir_hash -- pointed at this hash + if layout[potential_target] == nil then -- not pointed at another Digtron node local fields = data.meta.fields - adjacent_to_diggers[potential_target] = {period = fields.period, offset = fields.offset} + adjacent_to_diggers[hash] = { + period = fields.period, + offset = fields.offset, + dir_hash = dir_hash, + } end end if minetest.get_item_group(data.node.name, "digtron") == 4 then - local potential_target = hash + cardinal_dirs_hash[facedir_to_dir_index(data.node.param2)] + local dir_hash = cardinal_dirs_hash[facedir_to_dir_index(data.node.param2)] + local potential_target = hash + dir_hash if layout[potential_target] == nil then local fields = data.meta.fields - adjacent_to_builders[potential_target] = { + -- TODO: trace extrusion and if it intersects Digtron layout cap it there. + adjacent_to_builders[hash] = { period = tonumber(fields.period) or 1, offset = tonumber(fields.offset) or 0, item = fields.item, - facing = tonumber(fields.facing) or 0, + facing = tonumber(fields.facing) or 0, -- facing of built node extrusion = tonumber(fields.extrusion) or 1, + dir_hash = dir_hash, -- Record in table form, it'll be more convenient for use later } end end @@ -267,7 +275,7 @@ table.insert(dispose_callbacks, invalidate_layout_cache) digtron.assemble = function(root_pos, player_name) local node = minetest.get_node(root_pos) -- TODO: a more generic test? Not needed with the more generic controller design, as far as I can tell. There's only going to be the one type of controller. - if node.name ~= "digtron:controller" then + if node.name ~= "digtron:controller" 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 " .. node.name) @@ -313,7 +321,6 @@ digtron.assemble = function(root_pos, player_name) return nil end -- Process inventories specially - -- TODO Builder inventory gets turned into an itemname in a special key in the builder's meta -- fuel and main get added to corresponding detached inventory lists for listname, items in pairs(current_meta_table.inventory) do local count = #items @@ -457,8 +464,6 @@ digtron.disassemble = function(digtron_id, player_name) minetest.swap_node(node_pos, {name=node_def._digtron_disassembled_node, param2=node.param2}) end - -- TODO: special handling for builder node inventories - -- Ensure node metadata fields are all set, too for field, value in pairs(data.meta.fields) do node_meta:set_string(field, value) @@ -625,7 +630,7 @@ local predict_dig = function(digtron_id, player_name) local cost = 0 for target_hash, digger_data in pairs(retrieve_all_digger_targets(digtron_id)) do - local target_pos = minetest.get_position_from_hash(target_hash + root_hash) + local target_pos = minetest.get_position_from_hash(target_hash + root_hash + digger_data.dir_hash) local target_node = minetest.get_node(target_pos) local target_name = target_node.name local targetdef = minetest.registered_nodes[target_name] @@ -753,37 +758,41 @@ local predict_build = function(digtron_id, new_pos, player_name, ignore_nodes) local cost = 0 for target_hash, builder_data in pairs(retrieve_all_builder_targets(digtron_id)) do - local target_pos = minetest.get_position_from_hash(target_hash + root_hash) - local target_node = minetest.get_node(target_pos) - local target_name = target_node.name - local targetdef = minetest.registered_nodes[target_name] - --TODO periodicity/offset test - if - ignore_hashes[target_hash] or - (targetdef ~= nil - and targetdef.buildable_to - and not minetest.is_protected(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 + local absolute_hash = target_hash + root_hash + local dir_hash = builder_data.dir_hash + for i = 1, builder_data.extrusion do + local target_pos = minetest.get_position_from_hash(absolute_hash + i * dir_hash) + local target_node = minetest.get_node(target_pos) + local target_name = target_node.name + local targetdef = minetest.registered_nodes[target_name] + --TODO periodicity/offset test + if + ignore_hashes[target_hash] or + (targetdef ~= nil + and targetdef.buildable_to + and not minetest.is_protected(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 - - -- TODO handle extrusion - - table.insert(built_nodes, { - pos = target_pos, - node = {name=item, param2=facing }, - old_node = target_node, - }) end end diff --git a/nodes/node_builder.lua b/nodes/node_builder.lua index ef201bb..e6ba720 100644 --- a/nodes/node_builder.lua +++ b/nodes/node_builder.lua @@ -120,7 +120,7 @@ local builder_on_rightclick = function(pos, node, clicker, itemstack, pointed_th local digtron_id = meta:get_string("digtron_id") if digtron_id ~= "" then minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=player_name}) - minetest.chat_send_player(player_name, "This Digtron is active, interact with it via the controller node.") + minetest.chat_send_player(player_name, S("This Digtron is active, interact with it via the controller node.")) return end @@ -149,6 +149,8 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields) local facing = tonumber(fields.facing) local extrusion = tonumber(fields.extrusion) + local item = meta:get_string("item") + if period and period > 0 then meta:set_int("period", math.floor(period)) else @@ -160,7 +162,7 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields) offset = meta:get_int("offset") end if facing and facing >= 0 and facing < 24 then - local target_item = ItemStack(meta:get_string("item")) + local target_item = ItemStack(item) if target_item:get_definition().paramtype2 == "wallmounted" then if facing < 6 then meta:set_int("facing", facing) @@ -199,6 +201,14 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields) 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 = "Nothing" + if item_def then + item_desc = item_def.description + end + + meta:set_string("infotext", S("Builder for @1\nperiod @2, offset @3, extrusion @4", item_desc, period, offset, extrusion)) + digtron.update_builder_item(pos) end) @@ -246,8 +256,8 @@ minetest.register_node("digtron:builder", { on_construct = function(pos) local meta = minetest.get_meta(pos) - meta:set_int("period", 1) - meta:set_int("offset", 0) + meta:set_int("period", 1) + meta:set_int("offset", 0) meta:set_int("facing", 0) meta:set_int("extrusion", 1) end, diff --git a/nodes/node_digger.lua b/nodes/node_digger.lua index 24bdbdc..903234d 100644 --- a/nodes/node_digger.lua +++ b/nodes/node_digger.lua @@ -32,7 +32,7 @@ local update_infotext = function(meta) if period < 1 then period = 1 end local offset = meta:get_int("offset") - meta:set_string("infotext", S("Digger period @1 offset @2", period, offset)) + meta:set_string("infotext", S("Digger\nperiod @1, offset @2", period, offset)) end minetest.register_node("digtron:digger", { @@ -119,6 +119,10 @@ minetest.register_node("digtron:digger_static",{ 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 player_name = clicker:get_player_name() player_interacting_with_digtron_pos[player_name] = pos diff --git a/nodes/node_storage.lua b/nodes/node_storage.lua index e4be73d..1bd5dae 100644 --- a/nodes/node_storage.lua +++ b/nodes/node_storage.lua @@ -70,7 +70,7 @@ minetest.register_node("digtron:inventory", { 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(), "This Digtron is active, interact with it via the controller node.") + minetest.chat_send_player(clicker:get_player_name(), S("This Digtron is active, interact with it via the controller node.")) end end, @@ -175,7 +175,7 @@ minetest.register_node("digtron:fuelstore", { 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(), "This Digtron is active, interact with it via the controller node.") + minetest.chat_send_player(clicker:get_player_name(), S("This Digtron is active, interact with it via the controller node.")) end end, @@ -302,7 +302,7 @@ minetest.register_node("digtron:combined_storage", { 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(), "This Digtron is active, interact with it via the controller node.") + minetest.chat_send_player(clicker:get_player_name(), S("This Digtron is active, interact with it via the controller node.")) end end, -- From 395a7945693e154912f18a3db75ed64d50f16229 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Mon, 26 Aug 2019 21:18:18 -0600 Subject: [PATCH 34/63] offset/periodicity implemented --- functions.lua | 90 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 13 deletions(-) diff --git a/functions.lua b/functions.lua index 2a8bb22..1dd2e50 100644 --- a/functions.lua +++ b/functions.lua @@ -212,8 +212,8 @@ local refresh_adjacent = function(digtron_id) if layout[potential_target] == nil then -- not pointed at another Digtron node local fields = data.meta.fields adjacent_to_diggers[hash] = { - period = fields.period, - offset = fields.offset, + period = tonumber(fields.period) or 1, + offset = tonumber(fields.offset) or 0, dir_hash = dir_hash, } end @@ -615,7 +615,7 @@ end ------------------------------------------------------------------------------------ -- Digging -local predict_dig = function(digtron_id, player_name) +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 @@ -634,8 +634,8 @@ local predict_dig = function(digtron_id, player_name) local target_node = minetest.get_node(target_pos) local target_name = target_node.name local targetdef = minetest.registered_nodes[target_name] - --TODO periodicity/offset test if + (target_pos[controlling_coordinate] + digger_data.offset) % digger_data.period == 0 and -- test periodicity and offset target_name ~= "air" and -- TODO: generalise this somehow for liquids and other undiggables minetest.get_item_group(target_name, "digtron") == 0 and minetest.get_item_group(target_name, "digtron_protected") == 0 and @@ -709,6 +709,10 @@ local log_dug_nodes = function(nodes_to_dig, digtron_id, root_pos, 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 @@ -737,7 +741,7 @@ end -- Building -- need to provide new_pos because Digtron moves before building -local predict_build = function(digtron_id, new_pos, player_name, ignore_nodes) +local predict_build = function(digtron_id, new_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 " @@ -760,12 +764,19 @@ local predict_build = function(digtron_id, new_pos, player_name, ignore_nodes) for target_hash, builder_data in pairs(retrieve_all_builder_targets(digtron_id)) do local absolute_hash = target_hash + root_hash local dir_hash = builder_data.dir_hash + local periodicity_permitted = nil for i = 1, builder_data.extrusion do local target_pos = minetest.get_position_from_hash(absolute_hash + i * dir_hash) + 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] - --TODO periodicity/offset test if ignore_hashes[target_hash] or (targetdef ~= nil @@ -799,20 +810,42 @@ local predict_build = function(digtron_id, new_pos, player_name, ignore_nodes) 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 + 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 @@ -836,14 +869,43 @@ 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 = 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 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 + digtron.execute_cycle = function(digtron_id, player_name) - local dig_leftovers, nodes_to_dig, dig_cost = predict_dig(digtron_id, player_name) local old_root_pos = retrieve_pos(digtron_id) local root_node = minetest.get_node(old_root_pos) - local new_root_pos = vector.add(old_root_pos, cardinal_dirs[facedir_to_dir_index(root_node.param2)]) + local root_facedir = root_node.param2 + local controlling_coordinate = get_controlling_coordinate(root_facedir) + + local dig_leftovers, nodes_to_dig, dig_cost = predict_dig(digtron_id, player_name, controlling_coordinate) + local new_root_pos = vector.add(old_root_pos, cardinal_dirs[facedir_to_dir_index(root_facedir)]) -- TODO: convert nodes_to_dig into a hash map here and pass that in to reduce duplication? local buildable_to, succeeded, failed = digtron.is_buildable_to(digtron_id, new_root_pos, player_name, nodes_to_dig) - local missing_items, built_nodes, build_cost = predict_build(digtron_id, new_root_pos, player_name, nodes_to_dig) + local missing_items, built_nodes, build_cost = predict_build(digtron_id, new_root_pos, player_name, nodes_to_dig, controlling_coordinate) if buildable_to and next(missing_items) == nil then digtron.fake_player:update(old_root_pos, player_name) @@ -857,8 +919,8 @@ digtron.execute_cycle = function(digtron_id, player_name) digtron.build_to_world(digtron_id, new_root_pos, player_name) minetest.sound_play("digtron_construction", {gain = 0.5, pos=new_root_pos}) - local build_leftovers = build_nodes(built_nodes, player_name) - -- There shouldn't normally be build_leftovers, but it's possible. + 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. @@ -871,7 +933,9 @@ digtron.execute_cycle = function(digtron_id, player_name) execute_dug_callbacks(nodes_dug) execute_built_callbacks(built_nodes) - -- TODO try putting dig_leftovers and build_leftovers into the inventory one last time before ejecting it + -- 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) commit_predictive_inventory(digtron_id) else From 861d9b275c84d1247e0d2ca1d26431d3b4e64411 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Tue, 27 Aug 2019 01:50:07 -0600 Subject: [PATCH 35/63] messy formspec code, but can now translate Digtron around freely --- controller.lua | 356 ++++++++++++++++++++++++++++++++++--------------- functions.lua | 11 ++ 2 files changed, 257 insertions(+), 110 deletions(-) diff --git a/controller.lua b/controller.lua index ef8282e..31b92db 100644 --- a/controller.lua +++ b/controller.lua @@ -2,27 +2,17 @@ local MP = minetest.get_modpath(minetest.get_current_modname()) local S, NS = dofile(MP.."/intllib.lua") +local listname_to_title = +{ + ["main"] = "Main Inventory", + ["fuel"] = "Fuel", +} + -- 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 player_interacting_with_digtron_pos = {} -local controller_nodebox = { - 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 - }, -} - local get_controller_unassembled_formspec = function(pos, player_name) local meta = minetest.get_meta(pos) return "size[9,9]" @@ -33,24 +23,233 @@ local get_controller_unassembled_formspec = function(pos, player_name) .. "container_end[]" end -local get_controller_assembled_formspec = function(pos, digtron_id, player_name) - digtron.retrieve_inventory(digtron_id) -- ensures the detatched inventory exists and is populated - return "size[9,9]" - .. "container[0.5,0]" +local get_controller_assembled_formspec = function(digtron_id, player_name) + local context = player_interacting_with_digtron_id[player_name] + if context == nil or context.digtron_id ~= digtron_id then + minetest.log("error", "[Digtron] get_controller_assembled_formspec was called for Digtron " + ..digtron_id .. " by " .. player_name .. " but there was no context recorded or the context was" + .." for the wrong digtron. This shouldn't be possible.") + return "" + end + + local inv = digtron.retrieve_inventory(digtron_id) -- ensures the detatched inventory exists and is populated + + -- TODO: will probably want a centralized cache for most of this, right now there's tons of redundancy + if context.tabs == nil then + context.tabs = {} + local lists = inv:get_lists() + for listname, contents in pairs(lists) do + table.insert(context.tabs, { + tab_type = "inventory", + listname = listname, + size = #contents, + current_page = 1}) + end + context.current_tab = 1 + end + + local tabs = "" + if next(context.tabs) ~= nil then + tabs = "tabheader[0,0;tab_header;Controls" + for _, tab in ipairs(context.tabs) do + tabs = tabs .. "," .. listname_to_title[tab.listname] or tab.listname + end + tabs = tabs .. ";" .. context.current_tab .. "]" + end + + local inv_tab = function(inv_list) + return "size[9,9]" + .. "position[0.025,0.1]" + .. "anchor[0,0]" + .. "container[0.5,0]" + .. "list[detached:" .. digtron_id .. ";"..inv_list..";0,0;8,4]" -- TODO: paging system for inventory + .. "container_end[]" + .. "container[0.5,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 + + local controls = "size[7,3]" + .. "position[0.025,0.1]" + .. "anchor[0,0]" + .. "container[0,0]" .. "button[0,0;1,1;disassemble;Disassemble]" - .. "field[1.2,0.25;2,1;digtron_name;Digtron name;"..digtron.get_name(digtron_id).."]" + .. "field[1.2,0.3;2,1;digtron_name;Digtron name;"..digtron.get_name(digtron_id).."]" .. "field_close_on_enter[digtron_name;false]" - .. "button[3,0;1,1;move_forward;Move forward]" - .. "button[4,0;1,1;test_dig;Test dig]" + .. "field[4.2,0.3;1,1;cycles;Cycles;1]" -- TODO persist + .. "button[5,0;1,1;test_dig;Execute]" .. "container_end[]" - .. "container[0.5,1]" - .. "list[detached:" .. digtron_id .. ";main;0,0;8,2]" -- TODO: paging system for inventory, guard against non-existent listname - .. "list[detached:" .. digtron_id .. ";fuel;0,2.5;8,2]" -- TODO: paging system for inventory, guard against non-existent listname + + .. "container[0,1]" + .. "box[0,0;4,2;#DDDDDD]" + .. "label[1.8,0.825;Move]" + .. "button[1.1,0.1;1,1;move_up;Up]" + .. "button[1.1,1.1;1,1;move_down;Down]" + .. "button[2.1,0.1;1,1;move_forward;Forward]" + .. "button[2.1,1.1;1,1;move_back;Back]" + .. "button[0.1,0.6;1,1;move_left;Left]" + .. "button[3.1,0.6;1,1;move_right;Right]" .. "container_end[]" - .. "container[0.5,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 .. ";main]" + + .. "container[4,1]" + .. "box[0,0;3,2;#CCCCCC]" + .. "label[1.3,0.825;Rotate]" + .. "button[0.1,0.1;1,1;rot_clockwise;Clockwise]" + .. "button[2.1,0.1;1,1;rot_counterclockwise;Widdershins]" + .. "button[1.1,0.1;1,1;rot_up;Pitch Up]" + .. "button[1.1,1.1;1,1;rot_down;Pitch Down]" + .. "button[0.1,1.1;1,1;rot_left;Yaw Left]" + .. "button[2.1,1.1;1,1;rot_right;Yaw Right]" + .. "container_end[]" + + + minetest.chat_send_all(dump(context)) + if context.current_tab == 1 then + return controls .. tabs + else + return inv_tab(context.tabs[context.current_tab - 1].listname) .. tabs + end +end + +-- Dealing with an unassembled Digtron controller +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname ~= "digtron:controller_unassembled" then + return + end + local name = player:get_player_name() + local pos = player_interacting_with_digtron_pos[name] + + if pos == nil then return end + + if fields.assemble then + local digtron_id = digtron.assemble(pos, name) + if digtron_id then + local meta = minetest.get_meta(pos) + meta:set_string("digtron_id", digtron_id) + meta:mark_as_private("digtron_id") + player_interacting_with_digtron_id[name] = {digtron_id = digtron_id} + minetest.show_formspec(name, + "digtron:controller_assembled", + get_controller_assembled_formspec(digtron_id, name)) + end + end + + --TODO: this isn't recording the field when using ESC to exit the formspec + if fields.key_enter_field == "digtron_name" or fields.digtron_name then + local meta = minetest.get_meta(pos) + meta:set_string("infotext", fields.digtron_name) + end +end) + + +local get_down = function(facedir) + local top = { + [0]={x=0,y=-1,z=0}, + {x=0,y=0,z=1}, + {x=0,y=0,z=-1}, + {x=1,y=0,y=0}, + {x=-1,y=0,z=0}, + {x=0,y=1,z=0}, + } + return top[math.floor(facedir/4)] end +function crossProduct( a, b ) + local x, y, z + x = a.y * (b.z or 0) - (a.z or 0) * b.y + y = (a.z or 0) * b.x - a.x * (b.z or 0) + z = a.x * b.y - a.y * b.x + return { x=x, y=y, z=z } +end +local facedir_to_right = {} +for i = 0, 23 do + local dir = minetest.facedir_to_dir(i) + local down = get_down(i) + facedir_to_right[i] = crossProduct(dir, down) +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() + local context = player_interacting_with_digtron_id[player_name] + if context == nil then + return + end + local digtron_id = context.digtron_id + if digtron_id == nil then + return + end + + local refresh = false + + if fields.tab_header then + local new_tab = tonumber(fields.tab_header) + if new_tab <= #(context.tabs) + 1 then + context.current_tab = new_tab + refresh = true + else + --TODO error message + end + end + + if fields.disassemble then + local pos = digtron.disassemble(digtron_id, player_name) + if pos then + player_interacting_with_digtron_pos[player_name] = pos + minetest.show_formspec(player_name, + "digtron:controller_unassembled", + get_controller_unassembled_formspec(pos, player_name)) + end + end + + local pos = digtron.get_pos(digtron_id) + if pos then + local node = minetest.get_node(pos) + if node.name == "digtron:controller" then + local facedir = node.param2 + if fields.move_forward then + digtron.move(digtron_id, vector.add(pos, minetest.facedir_to_dir(facedir)), player_name) + elseif fields.move_back then + digtron.move(digtron_id, vector.add(pos, vector.multiply(minetest.facedir_to_dir(facedir), -1)), player_name) + elseif fields.move_up then + digtron.move(digtron_id, vector.add(pos, vector.multiply(get_down(facedir), -1)), player_name) + elseif fields.move_down then + digtron.move(digtron_id, vector.add(pos, get_down(facedir)), player_name) + elseif fields.move_left then + digtron.move(digtron_id, vector.add(pos, vector.multiply(facedir_to_right[facedir % 23], -1)), player_name) + elseif fields.move_right then + digtron.move(digtron_id, vector.add(pos, facedir_to_right[facedir % 23]), player_name) + end + end + end + + if fields.test_dig then + digtron.execute_cycle(digtron_id, player_name) + end + + --TODO: this isn't recording the field when using ESC to exit the formspec + 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) + end + end + + if refresh then + minetest.show_formspec(player_name, + "digtron:controller_assembled", + get_controller_assembled_formspec(digtron_id, player_name)) + end + +end) + + + + minetest.register_node("digtron:controller", { description = S("Digtron Control Module"), @@ -72,7 +271,21 @@ minetest.register_node("digtron:controller", { "digtron_plate.png^digtron_control.png", }, drawtype = "nodebox", - node_box = controller_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_construct = function(pos) @@ -191,10 +404,10 @@ minetest.register_node("digtron:controller", { get_controller_unassembled_formspec(pos, player_name)) else -- initialized - player_interacting_with_digtron_id[player_name] = digtron_id + player_interacting_with_digtron_id[player_name] = {digtron_id = digtron_id} minetest.show_formspec(player_name, "digtron:controller_assembled", - get_controller_assembled_formspec(pos, digtron_id, player_name)) + get_controller_assembled_formspec(digtron_id, player_name)) end end, @@ -202,81 +415,4 @@ minetest.register_node("digtron:controller", { end, on_blast = digtron.on_blast, -}) - --- Dealing with an unassembled Digtron controller -minetest.register_on_player_receive_fields(function(player, formname, fields) - if formname ~= "digtron:controller_unassembled" then - return - end - local name = player:get_player_name() - local pos = player_interacting_with_digtron_pos[name] - - if pos == nil then return end - - if fields.assemble then - local digtron_id = digtron.assemble(pos, name) - if digtron_id then - local meta = minetest.get_meta(pos) - meta:set_string("digtron_id", digtron_id) - meta:mark_as_private("digtron_id") - player_interacting_with_digtron_id[name] = digtron_id - minetest.show_formspec(name, - "digtron:controller_assembled", - get_controller_assembled_formspec(pos, digtron_id, name)) - end - end - - --TODO: this isn't recording the field when using ESC to exit the formspec - if fields.key_enter_field == "digtron_name" or fields.digtron_name then - local meta = minetest.get_meta(pos) - meta:set_string("infotext", fields.digtron_name) - 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() - local digtron_id = player_interacting_with_digtron_id[player_name] - if digtron_id == nil then return end - - if fields.disassemble then - local pos = digtron.disassemble(digtron_id, player_name) - if pos then - player_interacting_with_digtron_pos[player_name] = pos - minetest.show_formspec(player_name, - "digtron:controller_unassembled", - get_controller_unassembled_formspec(pos, player_name)) - end - end - - if fields.move_forward then - local pos = digtron.get_pos(digtron_id) - if pos then - local node = minetest.get_node(pos) - if node.name == "digtron:controller" then - local dir = minetest.facedir_to_dir(node.param2) - local dest_pos = vector.add(dir, pos) - digtron.move(digtron_id, dest_pos, player_name) - end - end - end - - if fields.test_dig then - digtron.execute_cycle(digtron_id, player_name) - end - - --TODO: this isn't recording the field when using ESC to exit the formspec - 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) - end - end - -end) +}) \ No newline at end of file diff --git a/functions.lua b/functions.lua index 1dd2e50..239fee1 100644 --- a/functions.lua +++ b/functions.lua @@ -9,6 +9,11 @@ local cache = {} -- mod_meta:set_string(field, "") --end + +-- TODO +-- return core.is_protected(pos, name) and +-- not minetest.check_player_privs(name, "protection_bypass") + local modpath = minetest.get_modpath(minetest.get_current_modname()) local inventory_functions = dofile(modpath.."/inventories.lua") @@ -528,6 +533,12 @@ end -- Tests if a Digtron can be built at the designated location digtron.is_buildable_to = function(digtron_id, root_pos, player_name, ignore_nodes, return_immediately_on_failure) local layout = retrieve_layout(digtron_id) + if layout == nil then + minetest.log("error", "[Digtron] digtron.is_buildable_to called by " .. player_name + .. " for " .. digtron_id .. " at " .. minetest.pos_to_string(root_pos) .. " but no layout " + .. "could be retrieved.") + return false, {}, {} + end -- 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 From e63f016fc303d259f6dadd7bac47d40253ff3f73 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Tue, 27 Aug 2019 20:23:02 -0600 Subject: [PATCH 36/63] implement rotation. Broke builders in the process. --- controller.lua | 107 +++++++++++++--------------- functions.lua | 147 ++++++++++++++++++++++----------------- geometry.lua | 146 ++++++++++++++++++++++++++++++++++++++ init.lua | 1 + util_item_place_node.lua | 10 --- 5 files changed, 281 insertions(+), 130 deletions(-) create mode 100644 geometry.lua diff --git a/controller.lua b/controller.lua index 31b92db..0da3c5a 100644 --- a/controller.lua +++ b/controller.lua @@ -58,13 +58,13 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) end local inv_tab = function(inv_list) - return "size[9,9]" + return "size[8,9]" .. "position[0.025,0.1]" .. "anchor[0,0]" - .. "container[0.5,0]" + .. "container[0,0]" .. "list[detached:" .. digtron_id .. ";"..inv_list..";0,0;8,4]" -- TODO: paging system for inventory .. "container_end[]" - .. "container[0.5,5]list[current_player;main;0,0;8,1;]list[current_player;main;0,1.25;8,3;8]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 @@ -94,16 +94,14 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) .. "container[4,1]" .. "box[0,0;3,2;#CCCCCC]" .. "label[1.3,0.825;Rotate]" - .. "button[0.1,0.1;1,1;rot_clockwise;Clockwise]" - .. "button[2.1,0.1;1,1;rot_counterclockwise;Widdershins]" + .. "button[0.1,0.1;1,1;rot_counterclockwise;Widdershins]" + .. "button[2.1,0.1;1,1;rot_clockwise;Clockwise]" .. "button[1.1,0.1;1,1;rot_up;Pitch Up]" .. "button[1.1,1.1;1,1;rot_down;Pitch Down]" .. "button[0.1,1.1;1,1;rot_left;Yaw Left]" .. "button[2.1,1.1;1,1;rot_right;Yaw Right]" .. "container_end[]" - - minetest.chat_send_all(dump(context)) if context.current_tab == 1 then return controls .. tabs else @@ -141,32 +139,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end) - -local get_down = function(facedir) - local top = { - [0]={x=0,y=-1,z=0}, - {x=0,y=0,z=1}, - {x=0,y=0,z=-1}, - {x=1,y=0,y=0}, - {x=-1,y=0,z=0}, - {x=0,y=1,z=0}, - } - return top[math.floor(facedir/4)] -end -function crossProduct( a, b ) - local x, y, z - x = a.y * (b.z or 0) - (a.z or 0) * b.y - y = (a.z or 0) * b.x - a.x * (b.z or 0) - z = a.x * b.y - a.y * b.x - return { x=x, y=y, z=z } -end -local facedir_to_right = {} -for i = 0, 23 do - local dir = minetest.facedir_to_dir(i) - local down = get_down(i) - facedir_to_right[i] = crossProduct(dir, down) -end - -- Controlling a fully armed and operational Digtron minetest.register_on_player_receive_fields(function(player, formname, fields) if formname ~= "digtron:controller_assembled" then @@ -175,15 +147,29 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local player_name = player:get_player_name() local context = player_interacting_with_digtron_id[player_name] if context == nil then + minetest.chat_send_all("no context") return end local digtron_id = context.digtron_id if digtron_id == nil then + minetest.chat_send_all("no id") return end + local pos = digtron.get_pos(digtron_id) + if pos == nil then + minetest.chat_send_all("no pos") + return + end + local node = minetest.get_node(pos) + if node.name ~= "digtron:controller" then + minetest.chat_send_all("not controller " .. node.name .. " " .. minetest.pos_to_string(pos)) + -- this happened somehow in testing, Digtron needs to be able to recover from this situation. + -- TODO catch this on_rightclick and try remapping the layout to the new position. + return + end + local refresh = false - if fields.tab_header then local new_tab = tonumber(fields.tab_header) if new_tab <= #(context.tabs) + 1 then @@ -204,27 +190,35 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end - local pos = digtron.get_pos(digtron_id) - if pos then - local node = minetest.get_node(pos) - if node.name == "digtron:controller" then - local facedir = node.param2 - if fields.move_forward then - digtron.move(digtron_id, vector.add(pos, minetest.facedir_to_dir(facedir)), player_name) - elseif fields.move_back then - digtron.move(digtron_id, vector.add(pos, vector.multiply(minetest.facedir_to_dir(facedir), -1)), player_name) - elseif fields.move_up then - digtron.move(digtron_id, vector.add(pos, vector.multiply(get_down(facedir), -1)), player_name) - elseif fields.move_down then - digtron.move(digtron_id, vector.add(pos, get_down(facedir)), player_name) - elseif fields.move_left then - digtron.move(digtron_id, vector.add(pos, vector.multiply(facedir_to_right[facedir % 23], -1)), player_name) - elseif fields.move_right then - digtron.move(digtron_id, vector.add(pos, facedir_to_right[facedir % 23]), player_name) - end - end - end - + local facedir = node.param2 + -- Translation + if fields.move_forward then + digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_dir(facedir)), player_name) + elseif fields.move_back then + digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_dir(facedir), -1)), player_name) + elseif fields.move_up then + digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_up(facedir)), player_name) + elseif fields.move_down then + digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_up(facedir), -1)), player_name) + elseif fields.move_left then + digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_right(facedir), -1)), player_name) + elseif fields.move_right then + digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_right(facedir)), player_name) + -- Rotation + elseif fields.rot_counterclockwise then + digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_dir(facedir), -1), player_name) + elseif fields.rot_clockwise then + digtron.rotate(digtron_id, digtron.facedir_to_dir(facedir), player_name) + elseif fields.rot_up then + digtron.rotate(digtron_id, digtron.facedir_to_right(facedir), player_name) + elseif fields.rot_down then + digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_right(facedir), -1), player_name) + elseif fields.rot_left then + digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_up(facedir), -1), player_name) + elseif fields.rot_right then + digtron.rotate(digtron_id, digtron.facedir_to_up(facedir), player_name) + end + if fields.test_dig then digtron.execute_cycle(digtron_id, player_name) end @@ -243,8 +237,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) minetest.show_formspec(player_name, "digtron:controller_assembled", get_controller_assembled_formspec(digtron_id, player_name)) - end - + end end) diff --git a/functions.lua b/functions.lua index 239fee1..58b2aa3 100644 --- a/functions.lua +++ b/functions.lua @@ -29,7 +29,7 @@ digtron.retrieve_inventory = retrieve_inventory -- used by formspecs -------------------------------------------------------------------------------------- local create_new_id = function() - local digtron_id = "digtron" .. tostring(math.random(1, 2^21)) -- TODO: use SecureRandom() + 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 @@ -114,34 +114,10 @@ digtron.get_pos = retrieve_pos ------------------------------------------------------------------------------------------------------- -- Layout creation helpers -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}, -} -digtron.cardinal_dirs = cardinal_dirs -- used by builder entities as well --- Mapping from facedir value to index in cardinal_dirs. -local facedir_to_dir_map = digtron.facedir_to_dir_map - --- Turn the cardinal directions into a set of integers you can add to a hash to step in that direction. -local cardinal_dirs_hash = {} -for i, dir in ipairs(cardinal_dirs) do - cardinal_dirs_hash[i] = minetest.hash_node_position(dir) - minetest.hash_node_position({x=0, y=0, z=0}) -end - --- Given a facedir, returns an index into either cardinal_dirs or cardinal_dirs_hash that --- can be used to determine what this node is "aimed" at -local facedir_to_dir_index = function(param2) - return facedir_to_dir_map[param2 % 32] -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(cardinal_dirs) do + 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 @@ -204,15 +180,15 @@ local refresh_adjacent = function(digtron_id) local adjacent_to_diggers = {} local adjacent_to_builders = {} for hash, data in pairs(layout) do - for _, dir_hash in ipairs(cardinal_dirs_hash) do - local potential_adjacent = hash+dir_hash + for _, dir_hash in ipairs(digtron.cardinal_dirs_hash) do + local potential_adjacent = hash + dir_hash if layout[potential_adjacent] == nil then adjacent[potential_adjacent] = true end end if minetest.get_item_group(data.node.name, "digtron") == 3 then - local dir_hash = cardinal_dirs_hash[facedir_to_dir_index(data.node.param2)] + local dir_hash = digtron.facedir_to_dir_hash(data.node.param2) local potential_target = hash + dir_hash -- pointed at this hash if layout[potential_target] == nil then -- not pointed at another Digtron node local fields = data.meta.fields @@ -224,7 +200,7 @@ local refresh_adjacent = function(digtron_id) end end if minetest.get_item_group(data.node.name, "digtron") == 4 then - local dir_hash = cardinal_dirs_hash[facedir_to_dir_index(data.node.param2)] + local dir_hash = digtron.facedir_to_dir_hash(data.node.param2) local potential_target = hash + dir_hash if layout[potential_target] == nil then local fields = data.meta.fields @@ -307,7 +283,8 @@ digtron.assemble = function(root_pos, player_name) local layout = {} for hash, node in pairs(digtron_nodes) do - local relative_hash = hash - root_hash + local relative_hash = minetest.hash_node_position(vector.subtract(minetest.get_position_from_hash(hash), 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 @@ -385,9 +362,8 @@ 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, root_hash, hash, data, function_name) - local node_hash = hash + root_hash -- TODO may want to return this as well? - local node_pos = minetest.get_position_from_hash(node_hash) +local get_valid_data = function(digtron_id, root_pos, hash, data, function_name) + local node_pos = vector.add(minetest.get_position_from_hash(hash), root_pos) 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") @@ -440,11 +416,9 @@ digtron.disassemble = function(digtron_id, player_name) return end - local root_hash = minetest.hash_node_position(root_pos) - -- Write metadata and inventory to in-world node at this location for hash, data in pairs(layout) do - local node_pos, node, node_meta = get_valid_data(digtron_id, root_hash, hash, data, "digtron.disassemble") + local node_pos, node, node_meta = get_valid_data(digtron_id, root_pos, hash, data, "digtron.disassemble") if node_pos then local node_inv = node_meta:get_inventory() @@ -515,10 +489,9 @@ digtron.remove_from_world = function(digtron_id, player_name) return {} end - local root_hash = minetest.hash_node_position(root_pos) local nodes_to_destroy = {} for hash, data in pairs(layout) do - local node_pos = get_valid_data(digtron_id, root_hash, hash, data, "digtron.destroy") + local node_pos = get_valid_data(digtron_id, root_pos, hash, data, "digtron.remove_from_world") if node_pos then table.insert(nodes_to_destroy, node_pos) end @@ -542,12 +515,12 @@ digtron.is_buildable_to = function(digtron_id, root_pos, player_name, ignore_nod -- 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_pos = retrieve_pos(digtron_id) + local old_root_pos = retrieve_pos(digtron_id) local ignore_hashes = {} - if old_pos then - local old_root_hash = minetest.hash_node_position(old_pos) - for layout_hash, _ in pairs(layout) do - ignore_hashes[layout_hash + old_root_hash] = true + if old_root_pos then + for hash, _ in pairs(layout) do + local old_hash = minetest.hash_node_position(vector.add(minetest.get_position_from_hash(hash), old_root_pos)) + ignore_hashes[old_hash] = true end end if ignore_nodes then @@ -556,15 +529,14 @@ digtron.is_buildable_to = function(digtron_id, root_pos, player_name, ignore_nod end end - local root_hash = minetest.hash_node_position(root_pos) local succeeded = {} local failed = {} local permitted = true - for layout_hash, data in pairs(layout) do + for hash, data in pairs(layout) do -- Don't use get_valid_data, the Digtron isn't in-world yet - local node_hash = layout_hash + root_hash - local node_pos = minetest.get_position_from_hash(node_hash) + 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] -- TODO: lots of testing needed here @@ -590,11 +562,10 @@ end -- Places the Digtron into the world. digtron.build_to_world = function(digtron_id, root_pos, player_name) local layout = retrieve_layout(digtron_id) - local root_hash = minetest.hash_node_position(root_pos) for hash, data in pairs(layout) do -- Don't use get_valid_data, the Digtron isn't in-world yet - local node_pos = minetest.get_position_from_hash(hash + root_hash) + local node_pos = vector.add(minetest.get_position_from_hash(hash), root_pos) minetest.set_node(node_pos, data.node) local meta = minetest.get_meta(node_pos) for field, value in pairs(data.meta.fields) do @@ -623,6 +594,61 @@ digtron.move = function(digtron_id, dest_pos, player_name) 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 hash, data in pairs(layout) do + -- Facings + local node_name = data.node.name + local node_def = minetest.registered_nodes[node_name] + if node_def.paramtype2 == "wallmounted" then + data.node.param2 = digtron.rotate_wallmounted(axis_hash, data.node.param2) + elseif node_def.paramtype2 == "facedir" then + data.node.param2 = digtron.rotate_facedir(axis_hash, data.node.param2) + end + + -- Rotate builder item facings + if minetest.get_item_group(node_name, "digtron") == 4 then + local build_item = data.meta.fields.item + local build_item_def = minetest.registered_items[build_item] + if build_item_def.paramtype2 == "wallmounted" then + data.meta.fields.facing = digtron.rotate_wallmounted(axis_hash, tonumber(data.meta.fields.facing)) + elseif build_item_def.paramtype2 == "facedir" then + data.meta.fields.facing = digtron.rotate_facedir(axis_hash, tonumber(data.meta.fields.facing)) + end + end + + -- Position + local pos = minetest.get_position_from_hash(hash) + pos = digtron.rotate_pos(axis_hash, pos) + local new_hash = minetest.hash_node_position(pos) + rotated_layout[new_hash] = data + end + + invalidate_layout_cache(digtron_id) + persist_layout(digtron_id, rotated_layout) +end + +digtron.rotate = function(digtron_id, axis, player_name) + -- TODO: rotation version of is_buildable_to + local root_pos = retrieve_pos(digtron_id) + local removed = digtron.remove_from_world(digtron_id, player_name) + rotate_layout(digtron_id, axis) + digtron.build_to_world(digtron_id, 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 +end + ------------------------------------------------------------------------------------ -- Digging @@ -634,14 +660,13 @@ local predict_dig = function(digtron_id, player_name, controlling_coordinate) .."a predictive inventory or a root position for " .. digtron_id) return end - local root_hash = minetest.hash_node_position(root_pos) local leftovers = {} local dug_positions = {} local cost = 0 for target_hash, digger_data in pairs(retrieve_all_digger_targets(digtron_id)) do - local target_pos = minetest.get_position_from_hash(target_hash + root_hash + digger_data.dir_hash) + local target_pos = vector.add(minetest.get_position_from_hash(target_hash + digger_data.dir_hash), root_pos) local target_node = minetest.get_node(target_pos) local target_name = target_node.name local targetdef = minetest.registered_nodes[target_name] @@ -751,15 +776,14 @@ end ------------------------------------------------------------------------------------------------------ -- Building --- need to provide new_pos because Digtron moves before building -local predict_build = function(digtron_id, new_pos, player_name, ignore_nodes, controlling_coordinate) +-- 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 root_hash = minetest.hash_node_position(new_pos) local ignore_hashes = {} if ignore_nodes then @@ -773,11 +797,10 @@ local predict_build = function(digtron_id, new_pos, player_name, ignore_nodes, c local cost = 0 for target_hash, builder_data in pairs(retrieve_all_builder_targets(digtron_id)) do - local absolute_hash = target_hash + root_hash local dir_hash = builder_data.dir_hash local periodicity_permitted = nil for i = 1, builder_data.extrusion do - local target_pos = minetest.get_position_from_hash(absolute_hash + i * dir_hash) + local target_pos = vector.add(minetest.get_position_from_hash(target_hash + i * dir_hash), root_pos) if periodicity_permitted == nil then -- test periodicity and offset once periodicity_permitted = (target_pos[controlling_coordinate] + builder_data.offset) % builder_data.period == 0 @@ -883,7 +906,7 @@ 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. local get_controlling_coordinate = function(facedir) -- used for determining builder period and offset - local dir = facedir_to_dir_map[facedir] + local dir = digtron.facedir_to_dir_map[facedir] if dir == 1 or dir == 3 then return "z" elseif dir == 2 or dir == 4 then @@ -913,7 +936,7 @@ digtron.execute_cycle = function(digtron_id, player_name) local controlling_coordinate = get_controlling_coordinate(root_facedir) local dig_leftovers, nodes_to_dig, dig_cost = predict_dig(digtron_id, player_name, controlling_coordinate) - local new_root_pos = vector.add(old_root_pos, cardinal_dirs[facedir_to_dir_index(root_facedir)]) + local new_root_pos = vector.add(old_root_pos, digtron.facedir_to_dir(root_facedir)) -- TODO: convert nodes_to_dig into a hash map here and pass that in to reduce duplication? local buildable_to, succeeded, failed = digtron.is_buildable_to(digtron_id, new_root_pos, player_name, nodes_to_dig) local missing_items, built_nodes, build_cost = predict_build(digtron_id, new_root_pos, player_name, nodes_to_dig, controlling_coordinate) @@ -952,7 +975,7 @@ digtron.execute_cycle = function(digtron_id, player_name) else clear_predictive_inventory(digtron_id) digtron.show_buildable_nodes({}, failed) - minetest.sound_play("digtron_squeal", {gain = 0.5, pos=new_pos}) + minetest.sound_play("digtron_squeal", {gain = 0.5, pos=new_root_pos}) end end @@ -989,9 +1012,7 @@ digtron.can_dig = function(pos, digger) return true end - local root_hash = minetest.hash_node_position(root_pos) - local here_hash = minetest.hash_node_position(pos) - local layout_hash = here_hash - root_hash + 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 " diff --git a/geometry.lua b/geometry.lua new file mode 100644 index 0000000..82ca5a0 --- /dev/null +++ b/geometry.lua @@ -0,0 +1,146 @@ +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 = {} +for i, dir in ipairs(cardinal_dirs) do + cardinal_dirs_hash[i] = minetest.hash_node_position(dir) - minetest.hash_node_position({x = 0, y = 0, z = 0}) +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_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_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 + +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_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/init.lua b/init.lua index 5df8ef8..3415d18 100644 --- a/init.lua +++ b/init.lua @@ -28,6 +28,7 @@ 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(modpath.."/geometry.lua") dofile(modpath.."/entities.lua") dofile(modpath.."/functions.lua") dofile(modpath.."/controller.lua") 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 From 8ecc738f4417d965eb19f52f9672cc30f78d4804 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Tue, 27 Aug 2019 21:25:12 -0600 Subject: [PATCH 37/63] add can-build check to rotation --- controller.lua | 4 +-- functions.lua | 90 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 56 insertions(+), 38 deletions(-) diff --git a/controller.lua b/controller.lua index 0da3c5a..d905524 100644 --- a/controller.lua +++ b/controller.lua @@ -347,9 +347,9 @@ minetest.register_node("digtron:controller", { end if target_pos then - local success, succeeded, failed = digtron.is_buildable_to(digtron_id, target_pos, player_name) + local success, succeeded, failed = digtron.is_buildable_to(digtron_id, nil, target_pos, player_name) if success then - digtron.build_to_world(digtron_id, target_pos, player_name) + digtron.build_to_world(digtron_id, nil, target_pos, player_name) 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, diff --git a/functions.lua b/functions.lua index 58b2aa3..1e55b71 100644 --- a/functions.lua +++ b/functions.lua @@ -504,21 +504,18 @@ digtron.remove_from_world = function(digtron_id, player_name) end -- Tests if a Digtron can be built at the designated location -digtron.is_buildable_to = function(digtron_id, root_pos, player_name, ignore_nodes, return_immediately_on_failure) - local layout = retrieve_layout(digtron_id) - if layout == nil then - minetest.log("error", "[Digtron] digtron.is_buildable_to called by " .. player_name - .. " for " .. digtron_id .. " at " .. minetest.pos_to_string(root_pos) .. " but no layout " - .. "could be retrieved.") - return false, {}, {} - end - +digtron.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 hash, _ in pairs(layout) do + for hash, _ in pairs(old_layout) do local old_hash = minetest.hash_node_position(vector.add(minetest.get_position_from_hash(hash), old_root_pos)) ignore_hashes[old_hash] = true end @@ -560,9 +557,10 @@ digtron.is_buildable_to = function(digtron_id, root_pos, player_name, ignore_nod end -- Places the Digtron into the world. -digtron.build_to_world = function(digtron_id, root_pos, player_name) - local layout = retrieve_layout(digtron_id) - +digtron.build_to_world = function(digtron_id, layout, root_pos, player_name) + if layout == nil then + layout = retrieve_layout(digtron_id) + end 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) @@ -580,10 +578,11 @@ digtron.build_to_world = function(digtron_id, root_pos, player_name) end digtron.move = function(digtron_id, dest_pos, player_name) - local permitted, succeeded, failed = digtron.is_buildable_to(digtron_id, dest_pos, player_name) + local layout = retrieve_layout(digtron_id) + local permitted, succeeded, failed = digtron.is_buildable_to(digtron_id, layout, dest_pos, player_name) if permitted then local removed = digtron.remove_from_world(digtron_id, player_name) - digtron.build_to_world(digtron_id, dest_pos, player_name) + digtron.build_to_world(digtron_id, layout, dest_pos, player_name) minetest.sound_play("digtron_truck", {gain = 0.5, pos=dest_pos}) for _, removed_pos in ipairs(removed) do minetest.check_for_falling(removed_pos) @@ -599,28 +598,41 @@ end ------------------------------------------------------------------------ -- Rotation +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 rotate_layout = function(digtron_id, axis) local layout = retrieve_layout(digtron_id) local axis_hash = minetest.hash_node_position(axis) local rotated_layout = {} for hash, data in pairs(layout) do + local duplicate_data = deep_copy(data) -- Facings - local node_name = data.node.name + local node_name = duplicate_data.node.name local node_def = minetest.registered_nodes[node_name] if node_def.paramtype2 == "wallmounted" then - data.node.param2 = digtron.rotate_wallmounted(axis_hash, data.node.param2) + duplicate_data.node.param2 = digtron.rotate_wallmounted(axis_hash, duplicate_data.node.param2) elseif node_def.paramtype2 == "facedir" then - data.node.param2 = digtron.rotate_facedir(axis_hash, data.node.param2) + 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 = data.meta.fields.item + local build_item = duplicate_data.meta.fields.item local build_item_def = minetest.registered_items[build_item] if build_item_def.paramtype2 == "wallmounted" then - data.meta.fields.facing = digtron.rotate_wallmounted(axis_hash, tonumber(data.meta.fields.facing)) + duplicate_data.meta.fields.facing = digtron.rotate_wallmounted(axis_hash, tonumber(duplicate_data.meta.fields.facing)) elseif build_item_def.paramtype2 == "facedir" then - data.meta.fields.facing = digtron.rotate_facedir(axis_hash, tonumber(data.meta.fields.facing)) + duplicate_data.meta.fields.facing = digtron.rotate_facedir(axis_hash, tonumber(duplicate_data.meta.fields.facing)) end end @@ -628,25 +640,30 @@ local rotate_layout = function(digtron_id, axis) local pos = minetest.get_position_from_hash(hash) pos = digtron.rotate_pos(axis_hash, pos) local new_hash = minetest.hash_node_position(pos) - rotated_layout[new_hash] = data + rotated_layout[new_hash] = duplicate_data end - invalidate_layout_cache(digtron_id) - persist_layout(digtron_id, rotated_layout) + return rotated_layout end digtron.rotate = function(digtron_id, axis, player_name) - -- TODO: rotation version of is_buildable_to + local rotated_layout = rotate_layout(digtron_id, axis) local root_pos = retrieve_pos(digtron_id) - local removed = digtron.remove_from_world(digtron_id, player_name) - rotate_layout(digtron_id, axis) - digtron.build_to_world(digtron_id, 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 + local permitted, succeeded, failed = digtron.is_buildable_to(digtron_id, rotated_layout, root_pos, player_name) + if permitted then + local removed = digtron.remove_from_world(digtron_id, player_name) + digtron.build_to_world(digtron_id, rotated_layout, root_pos, player_name) + 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 + else + digtron.show_buildable_nodes({}, failed) + minetest.sound_play("digtron_squeal", {gain = 0.5, pos=root_pos}) + end end ------------------------------------------------------------------------------------ @@ -937,8 +954,9 @@ digtron.execute_cycle = function(digtron_id, player_name) local dig_leftovers, nodes_to_dig, dig_cost = predict_dig(digtron_id, player_name, controlling_coordinate) local new_root_pos = vector.add(old_root_pos, digtron.facedir_to_dir(root_facedir)) + local layout = retrieve_layout(digtron_id) -- TODO: convert nodes_to_dig into a hash map here and pass that in to reduce duplication? - local buildable_to, succeeded, failed = digtron.is_buildable_to(digtron_id, new_root_pos, player_name, nodes_to_dig) + 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 = predict_build(digtron_id, new_root_pos, player_name, nodes_to_dig, controlling_coordinate) if buildable_to and next(missing_items) == nil then @@ -950,7 +968,7 @@ digtron.execute_cycle = function(digtron_id, player_name) log_dug_nodes(nodes_to_dig, digtron_id, old_root_pos, player_name) -- Building new Digtron - digtron.build_to_world(digtron_id, new_root_pos, player_name) + 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) From c35104e8c3ada64cfdfaae6dde71a8bfbbda4ada Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Tue, 27 Aug 2019 21:44:08 -0600 Subject: [PATCH 38/63] Make control formspec a bit snugger, eliminate unnecessary disassembled formspec --- controller.lua | 79 ++++++++++---------------------------------------- 1 file changed, 16 insertions(+), 63 deletions(-) diff --git a/controller.lua b/controller.lua index d905524..36c9ad2 100644 --- a/controller.lua +++ b/controller.lua @@ -11,17 +11,6 @@ local listname_to_title = -- 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 player_interacting_with_digtron_pos = {} - -local get_controller_unassembled_formspec = function(pos, player_name) - local meta = minetest.get_meta(pos) - return "size[9,9]" - .. "container[0.5,0]" - .. "button[0,0;1,1;assemble;Assemble]" - .. "field[1.2,0.25;2,1;digtron_name;Digtron name;"..meta:get_string("infotext").."]" - .. "field_close_on_enter[digtron_name;false]" - .. "container_end[]" -end local get_controller_assembled_formspec = function(digtron_id, player_name) local context = player_interacting_with_digtron_id[player_name] @@ -69,15 +58,16 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) .. "listring[detached:" .. digtron_id .. ";"..inv_list.."]" end - local controls = "size[7,3]" + local controls = "size[4.2,5]" .. "position[0.025,0.1]" .. "anchor[0,0]" .. "container[0,0]" .. "button[0,0;1,1;disassemble;Disassemble]" - .. "field[1.2,0.3;2,1;digtron_name;Digtron name;"..digtron.get_name(digtron_id).."]" + .. "field[1.2,0.3;1.75,1;digtron_name;Digtron name;" + ..minetest.formspec_escape(digtron.get_name(digtron_id)).."]" .. "field_close_on_enter[digtron_name;false]" - .. "field[4.2,0.3;1,1;cycles;Cycles;1]" -- TODO persist - .. "button[5,0;1,1;test_dig;Execute]" + .. "field[2.9,0.3;0.7,1;cycles;Cycles;1]" -- TODO persist, actually use + .. "button[3.2,0;1,1;test_dig;Execute]" .. "container_end[]" .. "container[0,1]" @@ -91,7 +81,7 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) .. "button[3.1,0.6;1,1;move_right;Right]" .. "container_end[]" - .. "container[4,1]" + .. "container[0.5,3.2]" .. "box[0,0;3,2;#CCCCCC]" .. "label[1.3,0.825;Rotate]" .. "button[0.1,0.1;1,1;rot_counterclockwise;Widdershins]" @@ -109,36 +99,6 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) end end --- Dealing with an unassembled Digtron controller -minetest.register_on_player_receive_fields(function(player, formname, fields) - if formname ~= "digtron:controller_unassembled" then - return - end - local name = player:get_player_name() - local pos = player_interacting_with_digtron_pos[name] - - if pos == nil then return end - - if fields.assemble then - local digtron_id = digtron.assemble(pos, name) - if digtron_id then - local meta = minetest.get_meta(pos) - meta:set_string("digtron_id", digtron_id) - meta:mark_as_private("digtron_id") - player_interacting_with_digtron_id[name] = {digtron_id = digtron_id} - minetest.show_formspec(name, - "digtron:controller_assembled", - get_controller_assembled_formspec(digtron_id, name)) - end - end - - --TODO: this isn't recording the field when using ESC to exit the formspec - if fields.key_enter_field == "digtron_name" or fields.digtron_name then - local meta = minetest.get_meta(pos) - meta:set_string("infotext", fields.digtron_name) - 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 @@ -182,12 +142,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if fields.disassemble then local pos = digtron.disassemble(digtron_id, player_name) - if pos then - player_interacting_with_digtron_pos[player_name] = pos - minetest.show_formspec(player_name, - "digtron:controller_unassembled", - get_controller_unassembled_formspec(pos, player_name)) - end + minetest.close_formspec(player_name, formname) end local facedir = node.param2 @@ -240,10 +195,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end) - - - - minetest.register_node("digtron:controller", { description = S("Digtron Control Module"), _doc_items_longdesc = nil, @@ -281,9 +232,6 @@ minetest.register_node("digtron:controller", { }, sounds = default.node_sound_metal_defaults(), --- on_construct = function(pos) --- end, - on_dig = function(pos, node, digger) local player_name if digger then @@ -391,10 +339,15 @@ minetest.register_node("digtron:controller", { local player_name = clicker:get_player_name() if digtron_id == "" then - player_interacting_with_digtron_pos[player_name] = pos - minetest.show_formspec(player_name, - "digtron:controller_unassembled", - get_controller_unassembled_formspec(pos, player_name)) + local digtron_id = digtron.assemble(pos, player_name) + if digtron_id then + meta:set_string("digtron_id", digtron_id) + meta:mark_as_private("digtron_id") + player_interacting_with_digtron_id[player_name] = {digtron_id = digtron_id} + minetest.show_formspec(player_name, + "digtron:controller_assembled", + get_controller_assembled_formspec(digtron_id, player_name)) + end else -- initialized player_interacting_with_digtron_id[player_name] = {digtron_id = digtron_id} From df4c7f7f38fa28bcf456ca55efe755bcd6d87c07 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Tue, 27 Aug 2019 21:57:09 -0600 Subject: [PATCH 39/63] add a distinct rotation sound --- functions.lua | 1 + sounds/digtron_hydraulic.ogg | Bin 0 -> 21737 bytes sounds/license.txt | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 sounds/digtron_hydraulic.ogg diff --git a/functions.lua b/functions.lua index 1e55b71..4a1fa06 100644 --- a/functions.lua +++ b/functions.lua @@ -653,6 +653,7 @@ digtron.rotate = function(digtron_id, axis, player_name) if permitted then local removed = digtron.remove_from_world(digtron_id, player_name) digtron.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. diff --git a/sounds/digtron_hydraulic.ogg b/sounds/digtron_hydraulic.ogg new file mode 100644 index 0000000000000000000000000000000000000000..995d06a3c55e67b1683c21af9aa71c2ed3faf0c0 GIT binary patch literal 21737 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 DT7^ Date: Sat, 31 Aug 2019 11:50:51 -0600 Subject: [PATCH 40/63] create separate unassembled controller, make builder "read" work. --- controller.lua | 106 ++++++++++++++++++++++++++----------- functions.lua | 115 ++++++++++++++++++++++++++--------------- init.lua | 15 ++++++ nodes/node_builder.lua | 92 +++++++++++++++++++-------------- 4 files changed, 218 insertions(+), 110 deletions(-) diff --git a/controller.lua b/controller.lua index 36c9ad2..ffda5dc 100644 --- a/controller.lua +++ b/controller.lua @@ -51,7 +51,7 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) .. "position[0.025,0.1]" .. "anchor[0,0]" .. "container[0,0]" - .. "list[detached:" .. digtron_id .. ";"..inv_list..";0,0;8,4]" -- TODO: paging system for inventory + .. "list[detached:" .. digtron_id .. ";"..inv_list..";0,0;8,5]" -- TODO: paging system for inventory .. "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]" @@ -64,10 +64,10 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) .. "container[0,0]" .. "button[0,0;1,1;disassemble;Disassemble]" .. "field[1.2,0.3;1.75,1;digtron_name;Digtron name;" - ..minetest.formspec_escape(digtron.get_name(digtron_id)).."]" + .. minetest.formspec_escape(digtron.get_name(digtron_id)).."]" .. "field_close_on_enter[digtron_name;false]" .. "field[2.9,0.3;0.7,1;cycles;Cycles;1]" -- TODO persist, actually use - .. "button[3.2,0;1,1;test_dig;Execute]" + .. "button[3.2,0;1,1;execute;Execute]" .. "container_end[]" .. "container[0,1]" @@ -82,7 +82,7 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) .. "container_end[]" .. "container[0.5,3.2]" - .. "box[0,0;3,2;#CCCCCC]" + .. "box[0,0;3,2;#DDDDDD]" .. "label[1.3,0.825;Rotate]" .. "button[0.1,0.1;1,1;rot_counterclockwise;Widdershins]" .. "button[2.1,0.1;1,1;rot_clockwise;Clockwise]" @@ -174,7 +174,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) digtron.rotate(digtron_id, digtron.facedir_to_up(facedir), player_name) end - if fields.test_dig then + if fields.execute then digtron.execute_cycle(digtron_id, player_name) end @@ -195,7 +195,19 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end) -minetest.register_node("digtron:controller", { +-- 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 = { description = S("Digtron Control Module"), _doc_items_longdesc = nil, _doc_items_usagehelp = nil, @@ -231,6 +243,46 @@ minetest.register_node("digtron:controller", { }, }, sounds = default.node_sound_metal_defaults(), + on_blast = digtron.on_blast, +} + +minetest.register_node("digtron:controller_unassembled", combine_defs(base_def, { + _digtron_assembled_node = "digtron:controller", + + 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 meta = minetest.get_meta(pos) + meta:set_string("digtron_id", digtron_id) + meta:mark_as_private("digtron_id") + player_interacting_with_digtron_id[player_name] = {digtron_id = digtron_id} + 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, { + + 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 @@ -249,7 +301,7 @@ minetest.register_node("digtron:controller", { if stack:get_count() > 0 then minetest.add_item(pos, stack) end - -- call on_dignodes callback + -- TODO call on_dignodes callback if digtron_id ~= "" then local removed = digtron.remove_from_world(digtron_id, player_name) for _, removed_pos in ipairs(removed) do @@ -280,11 +332,6 @@ minetest.register_node("digtron:controller", { local stack_meta = itemstack:get_meta() local digtron_id = stack_meta:get_string("digtron_id") if digtron_id ~= "" then - -- Test if Digtron will fit the surroundings - -- if not, try moving it up so that the lowest y-coordinate on the Digtron is - -- at the y-coordinate of the place clicked on and test again. - -- if that fails, show ghost of Digtron and fail to place. - local target_pos local below_node = minetest.get_node(pointed_thing.under) local below_def = minetest.registered_nodes[below_node.name] @@ -293,23 +340,36 @@ minetest.register_node("digtron:controller", { 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) + 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 - digtron.build_to_world(digtron_id, nil, target_pos, player_name) + 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 + 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 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, @@ -336,29 +396,15 @@ minetest.register_node("digtron:controller", { 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 - local digtron_id = digtron.assemble(pos, player_name) - if digtron_id then - meta:set_string("digtron_id", digtron_id) - meta:mark_as_private("digtron_id") - player_interacting_with_digtron_id[player_name] = {digtron_id = digtron_id} - minetest.show_formspec(player_name, - "digtron:controller_assembled", - get_controller_assembled_formspec(digtron_id, player_name)) - end + -- TODO: error message, fix digtron else - -- initialized + local player_name = clicker:get_player_name() player_interacting_with_digtron_id[player_name] = {digtron_id = digtron_id} minetest.show_formspec(player_name, "digtron:controller_assembled", get_controller_assembled_formspec(digtron_id, player_name)) end end, - - on_timer = function(pos, elapsed) - end, - - on_blast = digtron.on_blast, -}) \ No newline at end of file +})) \ No newline at end of file diff --git a/functions.lua b/functions.lua index 4a1fa06..690c710 100644 --- a/functions.lua +++ b/functions.lua @@ -24,8 +24,6 @@ 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 -digtron.retrieve_inventory = retrieve_inventory -- used by formspecs - -------------------------------------------------------------------------------------- local create_new_id = function() @@ -58,11 +56,11 @@ end -- Name -- Not bothering with a dynamic table store for names, they're just strings with no need for serialization or deserialization -digtron.get_name = function(digtron_id) +local get_name = function(digtron_id) return mod_meta:get_string(digtron_id..":name") end -digtron.set_name = function(digtron_id, digtron_name) +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) @@ -109,8 +107,6 @@ end local persist_layout, retrieve_layout = get_table_functions("layout") local persist_pos, retrieve_pos, dispose_pos = get_table_functions("pos") -digtron.get_pos = retrieve_pos - ------------------------------------------------------------------------------------------------------- -- Layout creation helpers @@ -253,15 +249,9 @@ table.insert(dispose_callbacks, invalidate_layout_cache) -- assemble and disassemble -- Returns the id of the new Digtron record, or nil on failure -digtron.assemble = function(root_pos, player_name) - local node = minetest.get_node(root_pos) - -- TODO: a more generic test? Not needed with the more generic controller design, as far as I can tell. There's only going to be the one type of controller. - if node.name ~= "digtron:controller" 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 " .. node.name) - return nil - end +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 @@ -269,8 +259,18 @@ digtron.assemble = function(root_pos, player_name) .. " 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] = node} -- Nodes that are part of Digtron. + 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 @@ -326,9 +326,9 @@ digtron.assemble = function(root_pos, player_name) layout[relative_hash] = {meta = current_meta_table, node = node} end - digtron.set_name(digtron_id, root_meta:get_string("infotext")) 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) @@ -394,8 +394,7 @@ local get_valid_data = function(digtron_id, root_pos, hash, data, function_name) end -- Turns the Digtron back into pieces -digtron.disassemble = function(digtron_id, player_name) - local bbox = retrieve_bounding_box(digtron_id) +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 @@ -403,11 +402,8 @@ digtron.disassemble = function(digtron_id, player_name) return end - local root_meta = minetest.get_meta(root_pos) - root_meta:set_string("infotext", digtron.get_name(digtron_id)) - local layout = retrieve_layout(digtron_id) - local inv = digtron.retrieve_inventory(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 @@ -451,7 +447,16 @@ digtron.disassemble = function(digtron_id, player_name) -- Clear digtron_id, this node is no longer part of an active digtron node_meta:set_string("digtron_id", "") end - 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) @@ -468,7 +473,7 @@ end -- 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 -digtron.remove_from_world = function(digtron_id, player_name) +local remove_from_world = function(digtron_id, player_name) local layout = retrieve_layout(digtron_id) local root_pos = retrieve_pos(digtron_id) @@ -504,7 +509,7 @@ digtron.remove_from_world = function(digtron_id, player_name) end -- Tests if a Digtron can be built at the designated location -digtron.is_buildable_to = function(digtron_id, layout, root_pos, player_name, ignore_nodes, return_immediately_on_failure) +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) @@ -557,10 +562,11 @@ digtron.is_buildable_to = function(digtron_id, layout, root_pos, player_name, ig end -- Places the Digtron into the world. -digtron.build_to_world = function(digtron_id, layout, root_pos, player_name) +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 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) @@ -571,18 +577,19 @@ digtron.build_to_world = function(digtron_id, layout, root_pos, player_name) end meta:set_string("digtron_id", digtron_id) meta:mark_as_private("digtron_id") + table.insert(built_positions, node_pos) end persist_pos(digtron_id, root_pos) - return true + return built_positions end -digtron.move = function(digtron_id, dest_pos, player_name) +local move = function(digtron_id, dest_pos, player_name) local layout = retrieve_layout(digtron_id) - local permitted, succeeded, failed = digtron.is_buildable_to(digtron_id, layout, dest_pos, player_name) + local permitted, succeeded, failed = is_buildable_to(digtron_id, layout, dest_pos, player_name) if permitted then - local removed = digtron.remove_from_world(digtron_id, player_name) - digtron.build_to_world(digtron_id, layout, dest_pos, player_name) + local removed = remove_from_world(digtron_id, player_name) + build_to_world(digtron_id, layout, dest_pos, player_name) minetest.sound_play("digtron_truck", {gain = 0.5, pos=dest_pos}) for _, removed_pos in ipairs(removed) do minetest.check_for_falling(removed_pos) @@ -646,13 +653,13 @@ local rotate_layout = function(digtron_id, axis) return rotated_layout end -digtron.rotate = function(digtron_id, axis, player_name) +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 = digtron.is_buildable_to(digtron_id, rotated_layout, root_pos, player_name) + local permitted, succeeded, failed = is_buildable_to(digtron_id, rotated_layout, root_pos, player_name) if permitted then - local removed = digtron.remove_from_world(digtron_id, player_name) - digtron.build_to_world(digtron_id, rotated_layout, root_pos, player_name) + local removed = remove_from_world(digtron_id, player_name) + 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 @@ -947,7 +954,7 @@ local insert_or_eject = function(digtron_id, item_list, pos) end end -digtron.execute_cycle = function(digtron_id, player_name) +local execute_cycle = function(digtron_id, player_name) local old_root_pos = retrieve_pos(digtron_id) local root_node = minetest.get_node(old_root_pos) local root_facedir = root_node.param2 @@ -1005,7 +1012,7 @@ end -- 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. -- TODO: add protection check? -digtron.can_dig = function(pos, digger) +local can_dig = function(pos, digger) local meta = minetest.get_meta(pos) local digtron_id = meta:get_string("digtron_id") if digtron_id == "" then @@ -1025,7 +1032,7 @@ digtron.can_dig = function(pos, digger) 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) - -- May be better to do this to prevent node duplication. But we're already in bug land here so tread gently. + -- 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 @@ -1053,7 +1060,7 @@ end -- put this on all Digtron nodes. If other inventory types are added (eg, batteries) -- update this. -digtron.on_blast = function(pos, intensity) +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) @@ -1079,7 +1086,7 @@ 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. -digtron.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) +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) @@ -1115,3 +1122,29 @@ if minetest.get_modpath("creative") then end end 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 + +digtron.retrieve_inventory = retrieve_inventory -- used by formspecs + +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_cycle = execute_cycle \ No newline at end of file diff --git a/init.lua b/init.lua index 3415d18..c46c5af 100644 --- a/init.lua +++ b/init.lua @@ -1,6 +1,21 @@ digtron = {} digtron.doc = {} -- TODO: move to doc file +-- A global dictionary is used here so that other substitutions can be added easily by other mods, if necessary +digtron.builder_read_item_substitutions = { + ["default:torch_ceiling"] = "default:torch", + ["default:torch_wall"] = "default:torch", + ["default:dirt_with_grass"] = "default:dirt", + ["default:dirt_with_grass_footsteps"] = "default:dirt", + ["default:dirt_with_dry_grass"] = "default:dirt", + ["default:dirt_with_rainforest_litter"] = "default:dirt", + ["default:dirt_with_snow"] = "default:dirt", + ["default:furnace_active"] = "default:furnace", + ["farming:soil"] = "default:dirt", + ["farming:soil_wet"] = "default:dirt", + ["farming:desert_sand_soil"] = "default:desert_sand", + ["farming:desert_sand_soil_wet"] = "default:desert_sand", +} -- 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 diff --git a/nodes/node_builder.lua b/nodes/node_builder.lua index e6ba720..2962aa2 100644 --- a/nodes/node_builder.lua +++ b/nodes/node_builder.lua @@ -23,17 +23,21 @@ local get_formspec = function(pos) "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") .. "]" .. + "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 &\nSave") .. "]" .. - "tooltip[read;" .. S("Reads the facing of the block currently in the build location,\nthen saves all settings.") .. "]" .. + "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]" .. @@ -44,6 +48,19 @@ 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 @@ -52,9 +69,10 @@ local inv = minetest.create_detached_inventory("digtron:builder_item", { -- Always disallow put, but use this to read what the player *tried* adding and set the builder appropriately local item = stack:get_name() - -- Ignore unknown items - if minetest.registered_items[item] == nil then return 0 end - + 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 @@ -78,11 +96,6 @@ local inv = minetest.create_detached_inventory("digtron:builder_item", { return 0 end - local stack_def = minetest.registered_nodes[item] - if not stack_def and not digtron.whitelisted_on_place(item) then - return 0 -- don't allow craft items unless their on_place is whitelisted. - 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) @@ -97,12 +110,6 @@ local inv = minetest.create_detached_inventory("digtron:builder_item", { allow_take = function(inv, listname, index, stack, player) return 0 end, --- on_move = function(inv, from_list, from_index, to_list, to_index, count, player) --- end, --- on_take = function(inv, listname, index, stack, player) --- end, --- on_put = function(inv, listname, index, stack, player) --- end }) inv:set_size("main", 1) @@ -142,30 +149,31 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields) .. " no position was recorded.") return end - + local meta = minetest.get_meta(pos) - local period = tonumber(fields.period) - local offset = tonumber(fields.offset) - local facing = tonumber(fields.facing) - local extrusion = tonumber(fields.extrusion) local item = meta:get_string("item") + local period = tonumber(fields.period) if period and period > 0 then meta:set_int("period", math.floor(period)) else period = meta:get_int("period") end + + local offset = tonumber(fields.offset) if offset then meta:set_int("offset", math.floor(offset)) else offset = meta:get_int("offset") 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 if facing < 6 then - meta:set_int("facing", facing) + meta:set_int("facing", math.floor(facing)) -- wallmounted facings only run from 0-5 end else @@ -175,27 +183,32 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields) facing = meta:get_int("facing") end + local extrusion = tonumber(fields.extrusion) if extrusion and extrusion > 0 and extrusion <= digtron.config.maximum_extrusion then meta:set_int("extrusion", math.floor(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("facing", target_node.param2) --- end --- end + if fields.set then + --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 + local meta = minetest.get_meta(pos) + item = target_name + meta:set_string("item", item) + meta:set_int("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) @@ -208,8 +221,9 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields) end meta:set_string("infotext", S("Builder for @1\nperiod @2, offset @3, extrusion @4", item_desc, period, offset, extrusion)) - digtron.update_builder_item(pos) + minetest.show_formspec(player_name, "digtron:builder", get_formspec(pos)) + end) From 7c08241c7d4ac41f25bacee10d4bf64c4518be9d Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sat, 31 Aug 2019 16:04:12 -0600 Subject: [PATCH 41/63] add sequencer tab. Digtron doesn't yet actually use the sequence set here. --- controller.lua | 298 +++++++++++++++++++++++++++++++++++++------------ functions.lua | 14 ++- 2 files changed, 241 insertions(+), 71 deletions(-) diff --git a/controller.lua b/controller.lua index ffda5dc..de6d7f2 100644 --- a/controller.lua +++ b/controller.lua @@ -4,10 +4,150 @@ local S, NS = dofile(MP.."/intllib.lua") local listname_to_title = { - ["main"] = "Main Inventory", - ["fuel"] = "Fuel", + ["main"] = S("Main Inventory"), + ["fuel"] = S("Fuel"), } +local sequencer_commands = +{ + S("Sequence"), + S("Dig Move Build"), + S("Move Up"), + S("Move Down"), + S("Move Left"), + S("Move Right"), + S("Move Forward"), + S("Move Back"), + S("Yaw Left"), + S("Yaw Right"), + S("Pitch Up"), + S("Pitch Down"), + S("Roll Clockwise"), + S("Roll Widdershins"), +} + +local sequencer_dropdown_list = table.concat(sequencer_commands, ",") + +local sequencer_commands_reverse = {} +for i, command in ipairs(sequencer_commands) do + sequencer_commands_reverse[command] = i +end + +local create_sequence_list +create_sequence_list = function(sequence_in, list_out, root_index, x, y) + 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 + local line = "dropdown[".. x ..","..y..";1.5;sequencer_com"..index..";"..sequencer_dropdown_list..";"..val[1].. + "]field[".. x+1.75 ..",".. y+0.25 ..";1,1;sequencer_cnt"..index..";;"..val[2].."]".. + "field_close_on_enter[sequencer_cnt_"..i..";false]".. + "button[".. x+ 2.375 .. ","..y-0.0625 ..";1,1;sequencer_del"..index..";Delete]" + if val[1] == 1 then + line = line .. "button[".. x+3.25 ..","..y-0.0625 ..";1,1;sequencer_ins"..index..";Insert]" + table.insert(list_out, line) + y = y + 0.8 + y = create_sequence_list(val[3], list_out, index, x+0.5, y) + else + table.insert(list_out, line) + y = y + 0.8 + end + end + + return y +end + +-- all field prefixes in the above create_sequence_list should be of this length +local sequencer_field_length = string.len("sequencer_com:") + +local find_item = function(field, sequence) + local index_list = field:sub(sequencer_field_length+1):split(":") + local target = {[3] = sequence} + for i = 1, #index_list do + target = target[3][tonumber(index_list[i])] + 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 = {[3] = sequence} + for i = 1, #index_list-1 do + target = target[3][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[3], tonumber(index_list[#index_list])) +end + +-- recurses through sequences ensuring there are tables for them +local clean_subsequences +clean_subsequences = function(sequence) + for i, val in ipairs(sequence) do + if val[1] == 1 then + if val[3] == nil then + val[3] = {} + else + clean_subsequences(val[3]) + end + else + val[3] = nil + end + end +end + +local update_sequence = function(digtron_id, fields) + local sequence = digtron.get_sequence(digtron_id) + local delete_index = {} + local insert_index = {} + for field, value in pairs(fields) do + local command_type = field:sub(1,sequencer_field_length) + if command_type == "sequencer_com:" then + local seq_item = find_item(field, sequence) + seq_item[1] = sequencer_commands_reverse[value] + 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) + seq_item[2] = val_int + end + + --Save these to do last so as to not invalidate indices + elseif command_type == "sequencer_del:" then + table.insert(delete_index, field) + elseif command_type == "sequencer_ins:" then + table.insert(insert_index, field) + end + end + + for _, insert in ipairs(insert_index) do + local item = find_item(insert, sequence) + table.insert(item[3], {2,1}) + end + for _, delete in ipairs(delete_index) do + delete_item(delete, sequence) + end + + if fields.sequencer_insert_end then + table.insert(sequence, {2,1}) + end + + clean_subsequences(sequence) + digtron.set_sequence(digtron_id, sequence) +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 = {} @@ -21,7 +161,7 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) return "" end - local inv = digtron.retrieve_inventory(digtron_id) -- ensures the detatched inventory exists and is populated + local inv = digtron.get_inventory(digtron_id) -- ensures the detatched inventory exists and is populated -- TODO: will probably want a centralized cache for most of this, right now there's tons of redundancy if context.tabs == nil then @@ -37,19 +177,17 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) context.current_tab = 1 end - local tabs = "" - if next(context.tabs) ~= nil then - tabs = "tabheader[0,0;tab_header;Controls" - for _, tab in ipairs(context.tabs) do - tabs = tabs .. "," .. listname_to_title[tab.listname] or tab.listname - end - tabs = tabs .. ";" .. context.current_tab .. "]" + local position_and_anchor = "position[0.025,0.1]anchor[0,0]" + + local tabs = "tabheader[0,0;tab_header;Controls,Sequence" + for _, tab in ipairs(context.tabs) do + tabs = tabs .. "," .. listname_to_title[tab.listname] or tab.listname end + tabs = tabs .. ";" .. context.current_tab .. "]" local inv_tab = function(inv_list) return "size[8,9]" - .. "position[0.025,0.1]" - .. "anchor[0,0]" + .. position_and_anchor .. "container[0,0]" .. "list[detached:" .. digtron_id .. ";"..inv_list..";0,0;8,5]" -- TODO: paging system for inventory .. "container_end[]" @@ -58,9 +196,20 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) .. "listring[detached:" .. digtron_id .. ";"..inv_list.."]" end + local sequence_tab = function() + local sequence = digtron.get_sequence(digtron_id) + local list_out = {} + local y = create_sequence_list(sequence, list_out) + return "size[4.2,5]" + .. position_and_anchor + .. "container[0,0]" + .. table.concat(list_out) + .. "button[0,"..y-0.0625 ..";1,1;sequencer_insert_end;Insert]" + .. "container_end[]" + end + local controls = "size[4.2,5]" - .. "position[0.025,0.1]" - .. "anchor[0,0]" + .. position_and_anchor .. "container[0,0]" .. "button[0,0;1,1;disassemble;Disassemble]" .. "field[1.2,0.3;1.75,1;digtron_name;Digtron name;" @@ -84,8 +233,8 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) .. "container[0.5,3.2]" .. "box[0,0;3,2;#DDDDDD]" .. "label[1.3,0.825;Rotate]" - .. "button[0.1,0.1;1,1;rot_counterclockwise;Widdershins]" - .. "button[2.1,0.1;1,1;rot_clockwise;Clockwise]" + .. "button[0.1,0.1;1,1;rot_counterclockwise;Roll\nWiddershins]" + .. "button[2.1,0.1;1,1;rot_clockwise;Roll\nClockwise]" .. "button[1.1,0.1;1,1;rot_up;Pitch Up]" .. "button[1.1,1.1;1,1;rot_down;Pitch Down]" .. "button[0.1,1.1;1,1;rot_left;Yaw Left]" @@ -94,8 +243,10 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) if context.current_tab == 1 then return controls .. tabs + elseif context.current_tab == 2 then + return sequence_tab() .. tabs else - return inv_tab(context.tabs[context.current_tab - 1].listname) .. tabs + return inv_tab(context.tabs[context.current_tab - 2].listname) .. tabs end end @@ -105,34 +256,36 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) return end local player_name = player:get_player_name() + + -- Get and validate various values local context = player_interacting_with_digtron_id[player_name] if context == nil then - minetest.chat_send_all("no context") + minetest.log("error", "[Digtron] player_interacting_with_digtron_id context not found for " .. player_name) return end local digtron_id = context.digtron_id if digtron_id == nil then - minetest.chat_send_all("no id") + minetest.log("error", "[Digtron] player_interacting_with_digtron_id context had no digtron id for " .. player_name) return end - local pos = digtron.get_pos(digtron_id) if pos == nil then - minetest.chat_send_all("no pos") + 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.chat_send_all("not controller " .. node.name .. " " .. minetest.pos_to_string(pos)) - -- this happened somehow in testing, Digtron needs to be able to recover from this situation. - -- TODO catch this on_rightclick and try remapping the layout to the new position. + 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 = context.current_tab local refresh = false if fields.tab_header then local new_tab = tonumber(fields.tab_header) - if new_tab <= #(context.tabs) + 1 then + if new_tab <= #(context.tabs) + 2 then context.current_tab = new_tab refresh = true else @@ -140,52 +293,59 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end - if fields.disassemble then - local pos = digtron.disassemble(digtron_id, player_name) - minetest.close_formspec(player_name, formname) - end - - local facedir = node.param2 - -- Translation - if fields.move_forward then - digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_dir(facedir)), player_name) - elseif fields.move_back then - digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_dir(facedir), -1)), player_name) - elseif fields.move_up then - digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_up(facedir)), player_name) - elseif fields.move_down then - digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_up(facedir), -1)), player_name) - elseif fields.move_left then - digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_right(facedir), -1)), player_name) - elseif fields.move_right then - digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_right(facedir)), player_name) - -- Rotation - elseif fields.rot_counterclockwise then - digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_dir(facedir), -1), player_name) - elseif fields.rot_clockwise then - digtron.rotate(digtron_id, digtron.facedir_to_dir(facedir), player_name) - elseif fields.rot_up then - digtron.rotate(digtron_id, digtron.facedir_to_right(facedir), player_name) - elseif fields.rot_down then - digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_right(facedir), -1), player_name) - elseif fields.rot_left then - digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_up(facedir), -1), player_name) - elseif fields.rot_right then - digtron.rotate(digtron_id, digtron.facedir_to_up(facedir), player_name) - end - - if fields.execute then - digtron.execute_cycle(digtron_id, player_name) - end + if current_tab == 1 then + -- Controls + if fields.disassemble then + local pos = digtron.disassemble(digtron_id, player_name) + minetest.close_formspec(player_name, formname) + end + + local facedir = node.param2 + -- Translation + if fields.move_forward then + digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_dir(facedir)), player_name) + elseif fields.move_back then + digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_dir(facedir), -1)), player_name) + elseif fields.move_up then + digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_up(facedir)), player_name) + elseif fields.move_down then + digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_up(facedir), -1)), player_name) + elseif fields.move_left then + digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_right(facedir), -1)), player_name) + elseif fields.move_right then + digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_right(facedir)), player_name) + -- Rotation + elseif fields.rot_counterclockwise then + digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_dir(facedir), -1), player_name) + elseif fields.rot_clockwise then + digtron.rotate(digtron_id, digtron.facedir_to_dir(facedir), player_name) + elseif fields.rot_up then + digtron.rotate(digtron_id, digtron.facedir_to_right(facedir), player_name) + elseif fields.rot_down then + digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_right(facedir), -1), player_name) + elseif fields.rot_left then + digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_up(facedir), -1), player_name) + elseif fields.rot_right then + digtron.rotate(digtron_id, digtron.facedir_to_up(facedir), player_name) + end - --TODO: this isn't recording the field when using ESC to exit the formspec - 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) + if fields.execute then + digtron.execute_cycle(digtron_id, player_name) 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) + end + end + + elseif current_tab == 2 then + --Sequencer + update_sequence(digtron_id, fields) + refresh = true end if refresh then diff --git a/functions.lua b/functions.lua index 690c710..f7b294b 100644 --- a/functions.lua +++ b/functions.lua @@ -32,7 +32,7 @@ local create_new_id = function() -- 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 + end return digtron_id end @@ -91,6 +91,7 @@ local get_table_functions = function(identifier) end return current end + return nil end local dispose_func = function(digtron_id) @@ -106,6 +107,8 @@ 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") +local persist_step, retrieve_step = get_table_functions("step") -- actually just an integer, but table_functions works for that too ------------------------------------------------------------------------------------------------------- -- Layout creation helpers @@ -331,6 +334,7 @@ local assemble = function(root_pos, player_name) set_name(digtron_id, digtron_name) invalidate_layout_cache(digtron_id) persist_pos(digtron_id, root_pos) + persist_sequence(digtron_id, {{2,1}}) -- TODO find a better place to set a default like this -- 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. @@ -1138,7 +1142,13 @@ digtron.set_name = set_name digtron.get_pos = retrieve_pos digtron.get_bounding_box = retrieve_bounding_box -digtron.retrieve_inventory = retrieve_inventory -- used by formspecs +-- used by formspecs +digtron.get_inventory = retrieve_inventory +digtron.set_sequence = persist_sequence +digtron.get_sequence = retrieve_sequence +digtron.set_step = persist_step +digtron.get_step = retrieve_step + digtron.assemble = assemble digtron.disassemble = disassemble From 263aba9a46daad6acbfb8cfb848768b4db8657d3 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sat, 31 Aug 2019 17:43:39 -0600 Subject: [PATCH 42/63] fix/remove a few TODOs --- controller.lua | 62 +++++++++++++++++---------- functions.lua | 114 +++++++++++++++++++++++++++---------------------- 2 files changed, 102 insertions(+), 74 deletions(-) diff --git a/controller.lua b/controller.lua index de6d7f2..b3f54db 100644 --- a/controller.lua +++ b/controller.lua @@ -289,7 +289,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) context.current_tab = new_tab refresh = true else - --TODO error message + minetest.log("error", "[Digtron] digtron:controller_assembled formspec returned the out-of-range tab index " + .. new_tab) end end @@ -464,8 +465,12 @@ minetest.register_node("digtron:controller", combine_defs(base_def, { -- TODO call on_dignodes callback if digtron_id ~= "" then local removed = digtron.remove_from_world(digtron_id, player_name) - for _, removed_pos in ipairs(removed) do - minetest.check_for_falling(removed_pos) + 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) @@ -505,26 +510,33 @@ minetest.register_node("digtron:controller", combine_defs(base_def, { -- 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) - 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) + 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 + + 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 - - 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 + 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 @@ -557,10 +569,14 @@ minetest.register_node("digtron:controller", combine_defs(base_def, { 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 - -- TODO: error message, fix digtron + 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) else - local player_name = clicker:get_player_name() player_interacting_with_digtron_id[player_name] = {digtron_id = digtron_id} minetest.show_formspec(player_name, "digtron:controller_assembled", diff --git a/functions.lua b/functions.lua index f7b294b..d7182c5 100644 --- a/functions.lua +++ b/functions.lua @@ -9,11 +9,6 @@ local cache = {} -- mod_meta:set_string(field, "") --end - --- TODO --- return core.is_protected(pos, name) and --- not minetest.check_player_privs(name, "protection_bypass") - local modpath = minetest.get_modpath(minetest.get_current_modname()) local inventory_functions = dofile(modpath.."/inventories.lua") @@ -24,6 +19,14 @@ 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 +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 create_new_id = function() @@ -476,7 +479,7 @@ end -- 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 +-- 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) @@ -489,13 +492,13 @@ local remove_from_world = function(digtron_id, player_name) meta:set_string("digtron_id", "") end dispose_id(digtron_id) - return {} + 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 {} + return nil end local nodes_to_destroy = {} @@ -506,7 +509,6 @@ local remove_from_world = function(digtron_id, player_name) end end - -- TODO: voxelmanip might be better here? minetest.bulk_set_node(nodes_to_destroy, {name="air"}) dispose_pos(digtron_id) return nodes_to_destroy @@ -545,11 +547,10 @@ local is_buildable_to = function(digtron_id, layout, root_pos, player_name, igno local node_hash = minetest.hash_node_position(node_pos) local node = minetest.get_node(node_pos) local node_def = minetest.registered_nodes[node.name] - -- TODO: lots of testing needed here if not ( (node_def and node_def.buildable_to) or ignore_hashes[node_hash]) or - minetest.is_protected(node_pos, player_name) + 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 @@ -593,10 +594,12 @@ local move = function(digtron_id, dest_pos, player_name) 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) - build_to_world(digtron_id, layout, dest_pos, player_name) - minetest.sound_play("digtron_truck", {gain = 0.5, pos=dest_pos}) - for _, removed_pos in ipairs(removed) do - minetest.check_for_falling(removed_pos) + if removed then + build_to_world(digtron_id, layout, dest_pos, player_name) + minetest.sound_play("digtron_truck", {gain = 0.5, pos=dest_pos}) + for _, removed_pos in ipairs(removed) do + minetest.check_for_falling(removed_pos) + end end else digtron.show_buildable_nodes({}, failed) @@ -663,14 +666,16 @@ local rotate = function(digtron_id, axis, player_name) 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) - 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) + 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 end else digtron.show_buildable_nodes({}, failed) @@ -710,7 +715,7 @@ local predict_dig = function(digtron_id, player_name, controlling_coordinate) targetdef.can_dig == nil or targetdef.can_dig(target_pos, minetest.get_player_by_name(player_name)) ) and - not minetest.is_protected(target_pos, player_name) + not protection_check(target_pos, player_name) then local material_cost = 0 if digtron.config.uses_resources then @@ -844,7 +849,7 @@ local predict_build = function(digtron_id, root_pos, player_name, ignore_nodes, ignore_hashes[target_hash] or (targetdef ~= nil and targetdef.buildable_to - and not minetest.is_protected(target_pos, player_name) + and not protection_check(target_pos, player_name) ) then local item = builder_data.item @@ -976,32 +981,34 @@ local execute_cycle = function(digtron_id, player_name) -- Removing old nodes local removed = digtron.remove_from_world(digtron_id, player_name) - 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) - - -- Building new Digtron - 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) + 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) + + -- Building new Digtron + 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) - -- 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) + commit_predictive_inventory(digtron_id) 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) - - commit_predictive_inventory(digtron_id) else clear_predictive_inventory(digtron_id) digtron.show_buildable_nodes({}, failed) @@ -1009,14 +1016,19 @@ local execute_cycle = function(digtron_id, player_name) 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. --- TODO: add protection check? 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 From b1cd43cffc7b7a50f86c8938a123c59adfc8587e Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sat, 31 Aug 2019 18:45:38 -0600 Subject: [PATCH 43/63] in theory, Digtron 2.0 can now recover from total metadata write-to-map failure --- controller.lua | 47 +++++++++++++++++++++++++++++++++++------------ functions.lua | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 16 deletions(-) diff --git a/controller.lua b/controller.lua index b3f54db..ff1e6a3 100644 --- a/controller.lua +++ b/controller.lua @@ -570,17 +570,40 @@ minetest.register_node("digtron:controller", combine_defs(base_def, { local digtron_id = meta:get_string("digtron_id") local player_name = clicker:get_player_name() - if digtron_id == "" 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) - else - player_interacting_with_digtron_id[player_name] = {digtron_id = digtron_id} - minetest.show_formspec(player_name, - "digtron:controller_assembled", - get_controller_assembled_formspec(digtron_id, 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 + + player_interacting_with_digtron_id[player_name] = {digtron_id = digtron_id} + 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 +}) \ No newline at end of file diff --git a/functions.lua b/functions.lua index d7182c5..c4eba9e 100644 --- a/functions.lua +++ b/functions.lua @@ -394,6 +394,8 @@ local get_valid_data = function(digtron_id, root_pos, hash, data, function_name) .. " 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 @@ -421,7 +423,7 @@ local disassemble = function(digtron_id, player_name) -- Write metadata and inventory to in-world node at this location for hash, data in pairs(layout) do - local node_pos, node, node_meta = get_valid_data(digtron_id, root_pos, hash, data, "digtron.disassemble") + local node_pos, node, node_meta = get_valid_data(digtron_id, root_pos, hash, data, "disassemble") if node_pos then local node_inv = node_meta:get_inventory() @@ -503,7 +505,7 @@ local remove_from_world = function(digtron_id, player_name) local nodes_to_destroy = {} for hash, data in pairs(layout) do - local node_pos = get_valid_data(digtron_id, root_pos, hash, data, "digtron.remove_from_world") + local node_pos = get_valid_data(digtron_id, root_pos, hash, data, "remove_from_world") if node_pos then table.insert(nodes_to_destroy, node_pos) end @@ -1139,7 +1141,30 @@ if minetest.get_modpath("creative") then 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) + for hash, 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, root_pos, hash, data, "recover_digtron_id") + end + return true + end + end + return false +end --------------------------------------------------------------------------------------------------------------------------- -- External API @@ -1169,4 +1194,6 @@ digtron.is_buildable_to = is_buildable_to digtron.build_to_world = build_to_world digtron.move = move digtron.rotate = rotate -digtron.execute_cycle = execute_cycle \ No newline at end of file +digtron.execute_cycle = execute_cycle + +digtron.recover_digtron_id = recover_digtron_id \ No newline at end of file From 0eb3d1f3bc693f73ad48a98d432ac6150ca6d3dd Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 1 Sep 2019 12:49:15 -0600 Subject: [PATCH 44/63] add dual digger nodes, recipes --- controller.lua | 1 - init.lua | 3 +- models/digtron_dual_digger.obj | 2077 +++++++++++++++++++++++++ models/digtron_dual_digger_static.obj | 659 ++++++++ nodes/node_digger.lua | 118 +- nodes/recipes.lua | 248 +++ 6 files changed, 3075 insertions(+), 31 deletions(-) create mode 100644 models/digtron_dual_digger.obj create mode 100644 models/digtron_dual_digger_static.obj create mode 100644 nodes/recipes.lua diff --git a/controller.lua b/controller.lua index ff1e6a3..b9d4802 100644 --- a/controller.lua +++ b/controller.lua @@ -433,7 +433,6 @@ minetest.register_node("digtron:controller_unassembled", combine_defs(base_def, })) minetest.register_node("digtron:controller", combine_defs(base_def, { - tiles = { "digtron_plate.png^[transformR90", "digtron_plate.png^[transformR270", diff --git a/init.lua b/init.lua index c46c5af..f773a43 100644 --- a/init.lua +++ b/init.lua @@ -50,4 +50,5 @@ 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") \ No newline at end of file +dofile(modpath.."/nodes/node_builder.lua") +dofile(modpath.."/nodes/recipes.lua") \ No newline at end of file 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_digger.lua b/nodes/node_digger.lua index 903234d..cb9a289 100644 --- a/nodes/node_digger.lua +++ b/nodes/node_digger.lua @@ -35,6 +35,27 @@ local update_infotext = function(meta) meta:set_string("infotext", S("Digger\nperiod @1, offset @2", period, offset)) 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 + }, +} + minetest.register_node("digtron:digger", { description = S("Digtron Digger"), _doc_items_longdesc = nil, @@ -56,22 +77,8 @@ minetest.register_node("digtron:digger", { }, { name = "digtron_plate.png^digtron_motor.png", backface_culling = true }, }, - collision_box = { - 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 - }, - }, - selection_box = { - 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 - }, - }, + collision_box = single_drill_nodebox, + selection_box = single_drill_nodebox, paramtype2 = "facedir", paramtype = "light", groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 3}, @@ -94,22 +101,75 @@ minetest.register_node("digtron:digger_static",{ { name = "digtron_drill_head_animated.png", backface_culling = true }, { name = "digtron_plate.png^digtron_motor.png", backface_culling = true }, }, - collision_box = { - 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 + collision_box = single_drill_nodebox, + selection_box = single_drill_nodebox, + paramtype2 = "facedir", + paramtype = "light", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 3}, + is_ground_content = false, + sounds = default.node_sound_metal_defaults(), + can_dig = digtron.can_dig, + 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 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, +}) + +minetest.register_node("digtron:dual_digger", { + description = S("Digtron Dual Digger"), + _doc_items_longdesc = nil, + _doc_items_usagehelp = nil, + _digtron_disassembled_node = "digtron: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 }, }, - selection_box = { - 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 - }, + collision_box = dual_drill_nodebox, + selection_box = dual_drill_nodebox, + paramtype2 = "facedir", + paramtype = "light", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 3}, + is_ground_content = false, + sounds = default.node_sound_metal_defaults(), + can_dig = digtron.can_dig, + on_blast = digtron.on_blast, +}) + +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 = 3}, diff --git a/nodes/recipes.lua b/nodes/recipes.lua new file mode 100644 index 0000000..b55873e --- /dev/null +++ b/nodes/recipes.lua @@ -0,0 +1,248 @@ +-- internationalization boilerplate +local MP = minetest.get_modpath(minetest.get_current_modname()) +local S, NS = dofile(MP.."/intllib.lua") + +minetest.register_craftitem("digtron:digtron_core", { + description = S("Digtron Core"), + inventory_image = "digtron_core.png", + _doc_items_longdesc = digtron.doc.core_longdesc, + _doc_items_usagehelp = digtron.doc.core_usagehelp, +}) + +minetest.register_craft({ + output = "digtron:digtron_core", + recipe = { + {"","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:builder", + recipe = { + {"","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",""}, + {"","",""} + } +}) + +minetest.register_craft({ + output = "digtron:digger_static", + recipe = { + {"","default:diamond",""}, + {"default:diamond","digtron:digtron_core","default:diamond"}, + {"","default:diamond",""} + } +}) + +--minetest.register_craft({ +-- output = "digtron:soft_digger_static", +-- recipe = { +-- {"","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",""}, + {"","",""} + } +}) + +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 + +minetest.register_craft({ + output = "digtron:combined_storage", + recipe = { + {"","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"} +-- } +--}) + +--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"} + } +}) + +minetest.register_craft({ + output = "digtron:panel", + recipe = { + {"","",""}, + {"","digtron:digtron_core",""}, + {"","default:steel_ingot",""} + } +}) + +minetest.register_craft({ + output = "digtron:edge_panel", + recipe = { + {"","",""}, + {"","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"} + } +}) + +-- For swapping digger types + +--minetest.register_craft({ +-- output = "digtron:dual_soft_digger", +-- type = "shapeless", +-- recipe = {"digtron:soft_digger", "digtron:soft_digger"}, +--}) +minetest.register_craft({ + output = "digtron:dual_digger_static", + type = "shapeless", + recipe = {"digtron:digger_static", "digtron:digger_static"}, +}) +--minetest.register_craft({ +-- output = "digtron:soft_digger 2", +-- recipe = { +-- {"digtron:dual_soft_digger"}, +-- } +--}) +minetest.register_craft({ + output = "digtron:digger_static 2", + 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"}, + } +}) +minetest.register_craft({ + output = "digtron:digtron_core", + recipe = { + {"digtron:edge_panel"}, + } +}) + +minetest.register_craft({ + output = "digtron:digtron_core", + recipe = { + {"digtron:inventory"}, + } +}) + +minetest.register_craft({ + output = "digtron:digtron_core", + recipe = { + {"digtron:fuelstore"}, + } +}) + +minetest.register_craft({ + output = "digtron:digtron_core", + recipe = { + {"digtron:combined_storage"}, + } +}) + +minetest.register_craft({ + output = "digtron:digtron_core", + recipe = { + {"digtron:light"}, + } +}) \ No newline at end of file From d74363610d44fdc0a0ce12e9e5ebd857d62607c4 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 1 Sep 2019 14:25:46 -0600 Subject: [PATCH 45/63] make dual diggers function dually --- functions.lua | 120 +++++++++++++++++++++++++----------------- geometry.lua | 17 ++++++ init.lua | 15 +++++- nodes/node_digger.lua | 8 +-- 4 files changed, 106 insertions(+), 54 deletions(-) diff --git a/functions.lua b/functions.lua index c4eba9e..7372a78 100644 --- a/functions.lua +++ b/functions.lua @@ -189,19 +189,34 @@ local refresh_adjacent = function(digtron_id) end end - if minetest.get_item_group(data.node.name, "digtron") == 3 then + local digtron_group = minetest.get_item_group(data.node.name, "digtron") + + if digtron_group == 3 or digtron_group == 10 then + local dir_hashes = {} local dir_hash = digtron.facedir_to_dir_hash(data.node.param2) local potential_target = hash + dir_hash -- pointed at this hash if layout[potential_target] == nil then -- not pointed at another Digtron node + table.insert(dir_hashes, dir_hash) + end + + if digtron_group == 10 then + dir_hash = digtron.facedir_to_down_hash(data.node.param2) + potential_target = hash + dir_hash -- pointed at this hash + if layout[potential_target] == nil then -- not pointed at another Digtron node + table.insert(dir_hashes, dir_hash) + end + end + + if #dir_hashes > 0 then local fields = data.meta.fields adjacent_to_diggers[hash] = { period = tonumber(fields.period) or 1, offset = tonumber(fields.offset) or 0, - dir_hash = dir_hash, + dir_hashes = dir_hashes, } - end + end end - if minetest.get_item_group(data.node.name, "digtron") == 4 then + if digtron_group == 4 then local dir_hash = digtron.facedir_to_dir_hash(data.node.param2) local potential_target = hash + dir_hash if layout[potential_target] == nil then @@ -700,54 +715,61 @@ local predict_dig = function(digtron_id, player_name, controlling_coordinate) local leftovers = {} local dug_positions = {} local cost = 0 + local dug_hashes = {} -- to ensure the same node isn't dug twice - for target_hash, digger_data in pairs(retrieve_all_digger_targets(digtron_id)) do - local target_pos = vector.add(minetest.get_position_from_hash(target_hash + digger_data.dir_hash), root_pos) - 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 - target_name ~= "air" and -- TODO: generalise this somehow for liquids and other undiggables - 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) - then - local material_cost = 0 - if digtron.config.uses_resources then - 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 - end - cost = cost + material_cost - - 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) + for digger_hash, digger_data in pairs(retrieve_all_digger_targets(digtron_id)) do + for _, dir_hash in ipairs(digger_data.dir_hashes) do + local target_hash = digger_hash + dir_hash + if not dug_hashes[target_hash] then + local target_pos = vector.add(minetest.get_position_from_hash(target_hash), root_pos) + 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 + target_name ~= "air" and -- TODO: generalise this somehow for liquids and other undiggables + 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) + then + local material_cost = 0 + if digtron.config.uses_resources then + 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 + end + cost = cost + material_cost + + 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 - table.insert(dug_positions, target_pos) end end diff --git a/geometry.lua b/geometry.lua index 82ca5a0..0b09e02 100644 --- a/geometry.lua +++ b/geometry.lua @@ -31,6 +31,15 @@ local facedir_to_up_map = { 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, @@ -52,6 +61,12 @@ 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 @@ -138,6 +153,8 @@ 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 diff --git a/init.lua b/init.lua index f773a43..29531a5 100644 --- a/init.lua +++ b/init.lua @@ -51,4 +51,17 @@ 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/recipes.lua") \ No newline at end of file +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 +-- 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 +-- 10 - dual-headed digger \ No newline at end of file diff --git a/nodes/node_digger.lua b/nodes/node_digger.lua index cb9a289..f717343 100644 --- a/nodes/node_digger.lua +++ b/nodes/node_digger.lua @@ -81,7 +81,7 @@ minetest.register_node("digtron:digger", { selection_box = single_drill_nodebox, paramtype2 = "facedir", paramtype = "light", - groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 3}, + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 3, not_in_creative_inventory = 1}, is_ground_content = false, sounds = default.node_sound_metal_defaults(), can_dig = digtron.can_dig, @@ -127,7 +127,7 @@ minetest.register_node("digtron:dual_digger", { description = S("Digtron Dual Digger"), _doc_items_longdesc = nil, _doc_items_usagehelp = nil, - _digtron_disassembled_node = "digtron:digger_static", + _digtron_disassembled_node = "digtron:dual_digger_static", drops = "digtron:dual_digger_static", drawtype = "mesh", mesh = "digtron_dual_digger.obj", @@ -148,7 +148,7 @@ minetest.register_node("digtron:dual_digger", { selection_box = dual_drill_nodebox, paramtype2 = "facedir", paramtype = "light", - groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 3}, + 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, @@ -172,7 +172,7 @@ minetest.register_node("digtron:dual_digger_static",{ selection_box = dual_drill_nodebox, paramtype2 = "facedir", paramtype = "light", - groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 3}, + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 10}, is_ground_content = false, sounds = default.node_sound_metal_defaults(), can_dig = digtron.can_dig, From e717f09cb390cc7246030b5742adc0e65149e78a Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 1 Sep 2019 19:03:37 -0600 Subject: [PATCH 46/63] stair digging is now possible, if a bit hacky. --- controller.lua | 28 +++-- functions.lua | 65 ++++++++-- geometry.lua | 2 +- locale/template.pot | 276 ++++++++++++++++++++++++++++++++++++++++++ locale/update.bat | 6 + nodes/node_digger.lua | 46 +++---- 6 files changed, 377 insertions(+), 46 deletions(-) create mode 100644 locale/template.pot create mode 100644 locale/update.bat diff --git a/controller.lua b/controller.lua index b9d4802..91d03c1 100644 --- a/controller.lua +++ b/controller.lua @@ -12,6 +12,7 @@ local sequencer_commands = { S("Sequence"), S("Dig Move Build"), + S("Dig Move Down"), S("Move Up"), S("Move Down"), S("Move Left"), @@ -217,6 +218,7 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) .. "field_close_on_enter[digtron_name;false]" .. "field[2.9,0.3;0.7,1;cycles;Cycles;1]" -- TODO persist, actually use .. "button[3.2,0;1,1;execute;Execute]" + .. "button[3.7,0;1,1;execute_down;Dig Down]" .. "container_end[]" .. "container[0,1]" @@ -331,7 +333,11 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end if fields.execute then - digtron.execute_cycle(digtron_id, player_name) + digtron.execute_dig_move_build_cycle(digtron_id, player_name) + end + + if fields.execute_down then + digtron.execute_dig_move_build_cycle(digtron_id, player_name, true) end if fields.key_enter_field == "digtron_name" or fields.digtron_name then @@ -369,7 +375,6 @@ local combine_defs = function(base_def, override_content) end local base_def = { - description = S("Digtron Control Module"), _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 @@ -378,15 +383,6 @@ local base_def = { 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", @@ -408,7 +404,16 @@ local base_def = { } 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) @@ -433,6 +438,7 @@ minetest.register_node("digtron:controller_unassembled", combine_defs(base_def, })) minetest.register_node("digtron:controller", combine_defs(base_def, { + description = S("Digtron Assembly"), tiles = { "digtron_plate.png^[transformR90", "digtron_plate.png^[transformR270", diff --git a/functions.lua b/functions.lua index 7372a78..e268f45 100644 --- a/functions.lua +++ b/functions.lua @@ -2,6 +2,11 @@ local mod_meta = digtron.mod_meta local cache = {} +-- internationalization boilerplate +local MP = minetest.get_modpath(minetest.get_current_modname()) +local S, NS = dofile(MP.."/intllib.lua") + + --minetest.debug(dump(mod_meta:to_table())) -- Wipes mod_meta @@ -60,7 +65,12 @@ end -- 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) - return mod_meta:get_string(digtron_id..":name") + 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) @@ -974,6 +984,8 @@ local get_controlling_coordinate = function(facedir) 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 @@ -987,20 +999,55 @@ local insert_or_eject = function(digtron_id, item_list, pos) end end -local execute_cycle = function(digtron_id, player_name) +-- 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 = predict_dig(digtron_id, player_name, controlling_coordinate) - local new_root_pos = vector.add(old_root_pos, digtron.facedir_to_dir(root_facedir)) + 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) - -- TODO: convert nodes_to_dig into a hash map here and pass that in to reduce duplication? 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 = predict_build(digtron_id, new_root_pos, player_name, nodes_to_dig, controlling_coordinate) + 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 buildable_to and next(missing_items) == nil then + 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))) + 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 a problem, 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}) + else digtron.fake_player:update(old_root_pos, player_name) -- Removing old nodes @@ -1033,10 +1080,6 @@ local execute_cycle = function(digtron_id, player_name) commit_predictive_inventory(digtron_id) end - else - clear_predictive_inventory(digtron_id) - digtron.show_buildable_nodes({}, failed) - minetest.sound_play("digtron_squeal", {gain = 0.5, pos=new_root_pos}) end end @@ -1216,6 +1259,6 @@ digtron.is_buildable_to = is_buildable_to digtron.build_to_world = build_to_world digtron.move = move digtron.rotate = rotate -digtron.execute_cycle = execute_cycle +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 index 0b09e02..203470b 100644 --- a/geometry.lua +++ b/geometry.lua @@ -147,7 +147,7 @@ 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 +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 diff --git a/locale/template.pot b/locale/template.pot new file mode 100644 index 0000000..49e49d1 --- /dev/null +++ b/locale/template.pot @@ -0,0 +1,276 @@ +# 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-09-01 18:19-0600\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" + +#: digtron\controller.lua:7 +msgid "Main Inventory" +msgstr "" + +#: digtron\controller.lua:8 +msgid "Fuel" +msgstr "" + +#: digtron\controller.lua:13 +msgid "Sequence" +msgstr "" + +#: digtron\controller.lua:14 +msgid "Dig Move Build" +msgstr "" + +#: digtron\controller.lua:15 +msgid "Dig Move Down" +msgstr "" + +#: digtron\controller.lua:16 +msgid "Move Up" +msgstr "" + +#: digtron\controller.lua:17 +msgid "Move Down" +msgstr "" + +#: digtron\controller.lua:18 +msgid "Move Left" +msgstr "" + +#: digtron\controller.lua:19 +msgid "Move Right" +msgstr "" + +#: digtron\controller.lua:20 +msgid "Move Forward" +msgstr "" + +#: digtron\controller.lua:21 +msgid "Move Back" +msgstr "" + +#: digtron\controller.lua:22 +msgid "Yaw Left" +msgstr "" + +#: digtron\controller.lua:23 +msgid "Yaw Right" +msgstr "" + +#: digtron\controller.lua:24 +msgid "Pitch Up" +msgstr "" + +#: digtron\controller.lua:25 +msgid "Pitch Down" +msgstr "" + +#: digtron\controller.lua:26 +msgid "Roll Clockwise" +msgstr "" + +#: digtron\controller.lua:27 +msgid "Roll Widdershins" +msgstr "" + +#: digtron\controller.lua:402 +msgid "Digtron Control Module" +msgstr "" + +#: digtron\controller.lua:436 +msgid "Digtron Assembly" +msgstr "" + +#: digtron\functions.lua:65 +msgid "Unnamed Digtron" +msgstr "" + +#: digtron\functions.lua:1012 +msgid "@1 at @2 has encountered an obstacle." +msgstr "" + +#: digtron\functions.lua:1023 +msgid "@1 at @2 requires @3 to execute its next build cycle." +msgstr "" + +#: digtron\nodes\node_builder.lua:25 +msgid "Extrusion" +msgstr "" + +#: digtron\nodes\node_builder.lua:27 +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 "" + +#: digtron\nodes\node_builder.lua:28 +#: digtron\nodes\node_digger.lua:20 +msgid "Periodicity" +msgstr "" + +#: digtron\nodes\node_builder.lua:30 +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 "" + +#: digtron\nodes\node_builder.lua:31 +#: digtron\nodes\node_digger.lua:23 +msgid "Offset" +msgstr "" + +#: digtron\nodes\node_builder.lua:33 +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 "" + +#: digtron\nodes\node_builder.lua:34 +#: digtron\nodes\node_digger.lua:26 +msgid "" +"Save &\n" +"Show" +msgstr "" + +#: digtron\nodes\node_builder.lua:35 +msgid "" +"Saves settings, closes interface, and shows the locations this builder will " +"build to in-world." +msgstr "" + +#: digtron\nodes\node_builder.lua:36 +msgid "Facing" +msgstr "" + +#: digtron\nodes\node_builder.lua:38 +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 "" + +#: digtron\nodes\node_builder.lua:39 +msgid "Read" +msgstr "" + +#: digtron\nodes\node_builder.lua:40 +msgid "Reads the facing of the block currently in the build location." +msgstr "" + +#: digtron\nodes\node_builder.lua:130 +#: digtron\nodes\node_storage.lua:73 +#: digtron\nodes\node_storage.lua:178 +#: digtron\nodes\node_storage.lua:305 +msgid "This Digtron is active, interact with it via the controller node." +msgstr "" + +#: digtron\nodes\node_builder.lua:223 +msgid "" +"Builder for @1\n" +"period @2, offset @3, extrusion @4" +msgstr "" + +#: digtron\nodes\node_builder.lua:232 +msgid "Digtron Builder Module" +msgstr "" + +#: digtron\nodes\node_digger.lua:22 +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 "" + +#: digtron\nodes\node_digger.lua:25 +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 "" + +#: digtron\nodes\node_digger.lua:27 +msgid "Saves settings" +msgstr "" + +#: digtron\nodes\node_digger.lua:35 +msgid "" +"Digger\n" +"period @1, offset @2" +msgstr "" + +#: digtron\nodes\node_digger.lua:71 +#: digtron\nodes\node_digger.lua:103 +msgid "Digtron Digger" +msgstr "" + +#: digtron\nodes\node_digger.lua:128 +#: digtron\nodes\node_digger.lua:160 +msgid "Digtron Dual Digger" +msgstr "" + +#: digtron\nodes\node_misc.lua:7 +msgid "Digtron Structure" +msgstr "" + +#: digtron\nodes\node_misc.lua:41 +msgid "Digtron Light" +msgstr "" + +#: digtron\nodes\node_misc.lua:64 +msgid "Digtron Panel" +msgstr "" + +#: digtron\nodes\node_misc.lua:88 +msgid "Digtron Edge Panel" +msgstr "" + +#: digtron\nodes\node_misc.lua:117 +msgid "Digtron Corner Panel" +msgstr "" + +#: digtron\nodes\node_pipeworks_interface.lua:85 +msgid "Digtron Inventory Interface" +msgstr "" + +#: digtron\nodes\node_storage.lua:9 +#: digtron\nodes\node_storage.lua:213 +msgid "Inventory items" +msgstr "" + +#: digtron\nodes\node_storage.lua:20 +msgid "Digtron Inventory Storage" +msgstr "" + +#: digtron\nodes\node_storage.lua:102 +#: digtron\nodes\node_storage.lua:215 +msgid "Fuel items" +msgstr "" + +#: digtron\nodes\node_storage.lua:113 +msgid "Digtron Fuel Storage" +msgstr "" + +#: digtron\nodes\node_storage.lua:226 +msgid "Digtron Combined Storage" +msgstr "" + +#: digtron\nodes\recipes.lua:6 +msgid "Digtron Core" +msgstr "" diff --git a/locale/update.bat b/locale/update.bat new file mode 100644 index 0000000..e87d44c --- /dev/null +++ b/locale/update.bat @@ -0,0 +1,6 @@ +@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/nodes/node_digger.lua b/nodes/node_digger.lua index f717343..cc3f3ee 100644 --- a/nodes/node_digger.lua +++ b/nodes/node_digger.lua @@ -2,7 +2,7 @@ local MP = minetest.get_modpath(minetest.get_current_modname()) local S, NS = dofile(MP.."/intllib.lua") --- TODO: make global +-- TODO: make global, is used by builders too local player_interacting_with_digtron_pos = {} local get_formspec = function(pos, player_name) @@ -56,6 +56,24 @@ local dual_drill_nodebox = { }, } +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 + minetest.register_node("digtron:digger", { description = S("Digtron Digger"), _doc_items_longdesc = nil, @@ -86,6 +104,7 @@ minetest.register_node("digtron:digger", { 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",{ @@ -110,17 +129,7 @@ minetest.register_node("digtron:digger_static",{ sounds = default.node_sound_metal_defaults(), can_dig = digtron.can_dig, 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 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, + on_rightclick = digger_on_rightclick, }) minetest.register_node("digtron:dual_digger", { @@ -153,6 +162,7 @@ minetest.register_node("digtron:dual_digger", { 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",{ @@ -177,17 +187,7 @@ minetest.register_node("digtron:dual_digger_static",{ sounds = default.node_sound_metal_defaults(), can_dig = digtron.can_dig, 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 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, + on_rightclick = digger_on_rightclick, }) minetest.register_on_player_receive_fields(function(player, formname, fields) From 1fa10d81ba25b52d904d45b786d384b99955caef Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 1 Sep 2019 23:33:52 -0600 Subject: [PATCH 47/63] make soft diggers work --- functions.lua | 69 +++++++++++----- init.lua | 6 +- nodes/node_digger.lua | 184 +++++++++++++++++++++++++++++++++++------- nodes/recipes.lua | 38 ++++----- 4 files changed, 224 insertions(+), 73 deletions(-) diff --git a/functions.lua b/functions.lua index e268f45..93d7441 100644 --- a/functions.lua +++ b/functions.lua @@ -201,7 +201,9 @@ local refresh_adjacent = function(digtron_id) local digtron_group = minetest.get_item_group(data.node.name, "digtron") - if digtron_group == 3 or digtron_group == 10 then + -- Diggers + if digtron_group >= 10 and digtron_group <= 13 then + -- All diggers target the node directly in front of them local dir_hashes = {} local dir_hash = digtron.facedir_to_dir_hash(data.node.param2) local potential_target = hash + dir_hash -- pointed at this hash @@ -209,7 +211,8 @@ local refresh_adjacent = function(digtron_id) table.insert(dir_hashes, dir_hash) end - if digtron_group == 10 then + -- 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 layout[potential_target] == nil then -- not pointed at another Digtron node @@ -217,15 +220,24 @@ local refresh_adjacent = function(digtron_id) 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 + if #dir_hashes > 0 then local fields = data.meta.fields adjacent_to_diggers[hash] = { period = tonumber(fields.period) or 1, offset = tonumber(fields.offset) or 0, dir_hashes = dir_hashes, + 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 @@ -713,6 +725,38 @@ 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) @@ -747,28 +791,11 @@ local predict_dig = function(digtron_id, player_name, controlling_coordinate) 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 - local material_cost = 0 if digtron.config.uses_resources then - 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 + cost = cost + get_material_cost(target_name) end - cost = cost + material_cost - local drops = minetest.get_node_drops(target_name, "") for _, drop in ipairs(drops) do local leftover = predictive_inv:add_item("main", ItemStack(drop)) diff --git a/init.lua b/init.lua index 29531a5..83c6cf3 100644 --- a/init.lua +++ b/init.lua @@ -57,11 +57,13 @@ 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 -- 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 --- 10 - dual-headed digger \ No newline at end of file +-- 10 digger +-- 11 dual-headed digger +-- 12 soft digger +-- 13 dual-headed soft digger \ No newline at end of file diff --git a/nodes/node_digger.lua b/nodes/node_digger.lua index cc3f3ee..e986ab5 100644 --- a/nodes/node_digger.lua +++ b/nodes/node_digger.lua @@ -35,6 +35,33 @@ local update_infotext = function(meta) 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 = { @@ -74,6 +101,9 @@ local assembled_digger_on_rightclick = function(pos, node, clicker, itemstack, p 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, @@ -99,7 +129,7 @@ minetest.register_node("digtron:digger", { selection_box = single_drill_nodebox, paramtype2 = "facedir", paramtype = "light", - groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 3, not_in_creative_inventory = 1}, + 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, @@ -124,14 +154,15 @@ minetest.register_node("digtron:digger_static",{ selection_box = single_drill_nodebox, paramtype2 = "facedir", paramtype = "light", - groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 3}, + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, is_ground_content = false, sounds = default.node_sound_metal_defaults(), - can_dig = digtron.can_dig, - on_blast = digtron.on_blast, on_rightclick = digger_on_rightclick, }) +---------------------------------------------------------------------------------------------- +-- Dual regular digger + minetest.register_node("digtron:dual_digger", { description = S("Digtron Dual Digger"), _doc_items_longdesc = nil, @@ -157,7 +188,7 @@ minetest.register_node("digtron:dual_digger", { selection_box = dual_drill_nodebox, paramtype2 = "facedir", paramtype = "light", - groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 10, not_in_creative_inventory = 1}, + 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, @@ -182,36 +213,127 @@ minetest.register_node("digtron:dual_digger_static",{ selection_box = dual_drill_nodebox, paramtype2 = "facedir", paramtype = "light", - groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 10}, + 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, }) -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) diff --git a/nodes/recipes.lua b/nodes/recipes.lua index b55873e..91274a5 100644 --- a/nodes/recipes.lua +++ b/nodes/recipes.lua @@ -54,14 +54,14 @@ minetest.register_craft({ } }) ---minetest.register_craft({ --- output = "digtron:soft_digger_static", --- recipe = { --- {"","default:steel_ingot",""}, --- {"default:steel_ingot","digtron:digtron_core","default:steel_ingot"}, --- {"","default:steel_ingot",""} --- } ---}) +minetest.register_craft({ + output = "digtron:soft_digger_static", + recipe = { + {"","default:steel_ingot",""}, + {"default:steel_ingot","digtron:digtron_core","default:steel_ingot"}, + {"","default:steel_ingot",""} + } +}) minetest.register_craft({ output = "digtron:inventory", @@ -169,22 +169,22 @@ minetest.register_craft({ -- For swapping digger types ---minetest.register_craft({ --- output = "digtron:dual_soft_digger", --- type = "shapeless", --- recipe = {"digtron:soft_digger", "digtron:soft_digger"}, ---}) +minetest.register_craft({ + output = "digtron:dual_soft_digger_static", + type = "shapeless", + recipe = {"digtron:soft_digger_static", "digtron:soft_digger_static"}, +}) minetest.register_craft({ output = "digtron:dual_digger_static", type = "shapeless", recipe = {"digtron:digger_static", "digtron:digger_static"}, }) ---minetest.register_craft({ --- output = "digtron:soft_digger 2", --- recipe = { --- {"digtron:dual_soft_digger"}, --- } ---}) +minetest.register_craft({ + output = "digtron:soft_digger_static 2", + recipe = { + {"digtron:dual_soft_digger_static"}, + } +}) minetest.register_craft({ output = "digtron:digger_static 2", recipe = { From 63a7f6aaaa7fde9dac90c675b03e91e120a6ac61 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Mon, 2 Sep 2019 14:28:13 -0600 Subject: [PATCH 48/63] implement duplicator --- functions.lua | 54 ++++++-- init.lua | 1 + nodes/node_duplicator.lua | 284 ++++++++++++++++++++++++++++++++++++++ nodes/recipes.lua | 22 +-- 4 files changed, 340 insertions(+), 21 deletions(-) create mode 100644 nodes/node_duplicator.lua diff --git a/functions.lua b/functions.lua index 93d7441..f4c4228 100644 --- a/functions.lua +++ b/functions.lua @@ -32,6 +32,18 @@ local protection_check = function(pos, player_name) 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() @@ -126,6 +138,33 @@ local persist_step, retrieve_step = get_table_functions("step") -- actually just ------------------------------------------------------------------------------------------------------- -- Layout creation helpers +digtron.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_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) @@ -346,7 +385,6 @@ local assemble = function(root_pos, player_name) return nil end -- Process inventories specially - -- fuel and main get added to corresponding detached inventory lists for listname, items in pairs(current_meta_table.inventory) do local count = #items -- increase the corresponding detached inventory size @@ -651,18 +689,6 @@ end ------------------------------------------------------------------------ -- Rotation -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 rotate_layout = function(digtron_id, axis) local layout = retrieve_layout(digtron_id) local axis_hash = minetest.hash_node_position(axis) @@ -1278,6 +1304,8 @@ digtron.get_sequence = retrieve_sequence digtron.set_step = persist_step digtron.get_step = retrieve_step +-- Used by duplicator +digtron.get_layout = retrieve_layout digtron.assemble = assemble digtron.disassemble = disassemble diff --git a/init.lua b/init.lua index 83c6cf3..35f150a 100644 --- a/init.lua +++ b/init.lua @@ -51,6 +51,7 @@ 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") diff --git a/nodes/node_duplicator.lua b/nodes/node_duplicator.lua new file mode 100644 index 0000000..7034c24 --- /dev/null +++ b/nodes/node_duplicator.lua @@ -0,0 +1,284 @@ +-- internationalization boilerplate +local MP = minetest.get_modpath(minetest.get_current_modname()) +local S, NS = dofile(MP.."/intllib.lua") + +local strip_digtron_prefix = function(desc) + -- Having "Digtron " prefixing every description is annoying + local prefix = "Digtron " + if desc:sub(1,#prefix) == prefix then + desc = desc:sub(#prefix+1) + end + return desc +end + +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 hash, data in pairs(layout) do + local item = data.node.name + local item_def = minetest.registered_items[item] + if item_def._digtron_disassembled_node then + item = item_def._digtron_disassembled_node + item_def = minetest.registered_items[item] + end + local desc = strip_digtron_prefix(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 + + local main_list = inv:get_list("main") + for _, itemstack in ipairs(main_list) do + if not itemstack:is_empty() then + local desc = strip_digtron_prefix(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", { + description = S("Digtron Duplicator"), + _doc_items_longdesc = digtron.doc.duplicator_longdesc, + _doc_items_usagehelp = digtron.doc.duplicator_usagehelp, + groups = {cracky = 3, oddly_breakable_by_hand=3}, + sounds = digtron.metal_sounds, + tiles = {"digtron_plate.png^(digtron_axel_side.png^[transformR90)", + "digtron_plate.png^(digtron_axel_side.png^[transformR270)", + "digtron_plate.png^digtron_axel_side.png", + "digtron_plate.png^(digtron_axel_side.png^[transformR180)", + "digtron_plate.png^digtron_builder.png", + "digtron_plate.png", + }, + paramtype = "light", + paramtype2= "facedir", + is_ground_content = false, + drawtype="nodebox", + node_box = { + type = "fixed", + fixed = { + {-0.5, 0.3125, 0.3125, 0.5, 0.5, 0.5}, -- FrontFrame_top + {-0.5, -0.5, 0.3125, 0.5, -0.3125, 0.5}, -- FrontFrame_bottom + {0.3125, -0.3125, 0.3125, 0.5, 0.3125, 0.5}, -- FrontFrame_right + {-0.5, -0.3125, 0.3125, -0.3125, 0.3125, 0.5}, -- FrontFrame_left + {-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 + {-0.4375, -0.4375, -0.4375, 0.4375, 0.4375, 0.3125}, -- Body + {-0.5, -0.3125, -0.5, -0.3125, 0.3125, -0.3125}, -- backframe_vertical + {0.3125, -0.3125, -0.5, 0.5, 0.3125, -0.3125}, -- backframe_left + {-0.5, 0.3125, -0.5, 0.5, 0.5, -0.3125}, -- backframe_top + {-0.5, -0.5, -0.5, 0.5, -0.3125, -0.3125}, -- backframe_bottom + {-0.0625, -0.0625, -0.5625, 0.0625, 0.0625, -0.4375}, -- back_probe + }, + }, + selection_box = { + type = "regular" + }, + + on_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_string("formspec", get_formspec(pos)) + local inv = meta:get_inventory() + 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") and inv:is_empty("template") and inv:is_empty("copy") + end, + + allow_metadata_inventory_put = function(pos, listname, index, stack, player) + 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() + 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 meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + + 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 + + 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 + + -- deduct nodes from duplicator inventory + 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 + + local new_digtron = digtron.duplicate(digtron_id) + inv:set_stack("copy", 1, new_digtron) + + minetest.sound_play("digtron_machine_assemble", {gain=1.0, pos=pos}) + end + end, + +}) diff --git a/nodes/recipes.lua b/nodes/recipes.lua index 91274a5..8ea09d7 100644 --- a/nodes/recipes.lua +++ b/nodes/recipes.lua @@ -111,14 +111,14 @@ minetest.register_craft({ } }) ---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"} --- } ---}) +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"} + } +}) --minetest.register_craft({ -- output = "digtron:inventory_ejector", @@ -174,17 +174,20 @@ minetest.register_craft({ type = "shapeless", recipe = {"digtron:soft_digger_static", "digtron:soft_digger_static"}, }) + minetest.register_craft({ output = "digtron:dual_digger_static", type = "shapeless", recipe = {"digtron:digger_static", "digtron:digger_static"}, }) + minetest.register_craft({ output = "digtron:soft_digger_static 2", recipe = { {"digtron:dual_soft_digger_static"}, } }) + minetest.register_craft({ output = "digtron:digger_static 2", recipe = { @@ -200,18 +203,21 @@ minetest.register_craft({ {"digtron:structure"}, } }) + minetest.register_craft({ output = "digtron:digtron_core", recipe = { {"digtron:panel"}, } }) + minetest.register_craft({ output = "digtron:digtron_core", recipe = { {"digtron:corner_panel"}, } }) + minetest.register_craft({ output = "digtron:digtron_core", recipe = { From ce6f05c336ef6d64e03e589dd938ba24ee796ddb Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Mon, 2 Sep 2019 20:17:51 -0600 Subject: [PATCH 49/63] digtrons can do hp damage now --- config.lua | 4 -- functions.lua | 105 +++++++++++++++++++++++++++++++++++++++++++------- geometry.lua | 3 +- 3 files changed, 93 insertions(+), 19 deletions(-) diff --git a/config.lua b/config.lua index 7e50163..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") diff --git a/functions.lua b/functions.lua index f4c4228..ce9318a 100644 --- a/functions.lua +++ b/functions.lua @@ -14,8 +14,63 @@ local S, NS = dofile(MP.."/intllib.lua") -- mod_meta:set_string(field, "") --end -local modpath = minetest.get_modpath(minetest.get_current_modname()) +local damage_hp = digtron.config.damage_hp +-- see predict_dig for how punch_data gets calculated +local damage_creatures = function(root_pos, punch_data, items_dropped) + local target_pos = punch_data[2] + local objects = minetest.env:get_objects_inside_radius(target_pos, 1.0) + if objects ~= nil then + local source_pos = vector.add(minetest.get_position_from_hash(punch_data[1]), root_pos) + 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 + lua_entity:add_velocity(dir) + 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.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, 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 @@ -24,6 +79,9 @@ 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 @@ -796,6 +854,10 @@ local predict_dig = function(digtron_id, player_name, controlling_coordinate) 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_hash, digger_data in pairs(retrieve_all_digger_targets(digtron_id)) do for _, dir_hash in ipairs(digger_data.dir_hashes) do @@ -807,7 +869,6 @@ local predict_dig = function(digtron_id, player_name, controlling_coordinate) local targetdef = minetest.registered_nodes[target_name] if (target_pos[controlling_coordinate] + digger_data.offset) % digger_data.period == 0 and -- test periodicity and offset - target_name ~= "air" and -- TODO: generalise this somehow for liquids and other undiggables 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 @@ -819,24 +880,32 @@ local predict_dig = function(digtron_id, player_name, controlling_coordinate) not protection_check(target_pos, player_name) and (not digger_data.soft or is_soft_material(target_name)) then - 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) + if punches_thrown then + -- storing digger_hash rather than converting it into a vector because + -- in most cases there won't be something to punch and that calculation can be skipped + -- convert to digger_pos by adding root_pos + table.insert(punches_thrown, {digger_hash, 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 - table.insert(dug_positions, target_pos) - dug_hashes[target_hash] = true end end end end - return leftovers, dug_positions, cost + return leftovers, dug_positions, cost, punches_thrown end -- Removes nodes and records node info so execute_dug_callbacks can be called later @@ -1059,7 +1128,7 @@ local execute_dig_move_build_cycle = function(digtron_id, player_name, dig_down) local root_facedir = root_node.param2 local controlling_coordinate = get_controlling_coordinate(root_facedir) - local dig_leftovers, nodes_to_dig, dig_cost = predict_dig(digtron_id, player_name, controlling_coordinate) + 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 @@ -1109,6 +1178,13 @@ local execute_dig_move_build_cycle = function(digtron_id, player_name, dig_down) 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(old_root_pos, punch_data, items_dropped) + end + end + -- Building new Digtron digtron.build_to_world(digtron_id, layout, new_root_pos, player_name) minetest.sound_play("digtron_construction", {gain = 0.5, pos=new_root_pos}) @@ -1130,6 +1206,7 @@ local execute_dig_move_build_cycle = function(digtron_id, player_name, dig_down) -- 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 diff --git a/geometry.lua b/geometry.lua index 203470b..e441eb5 100644 --- a/geometry.lua +++ b/geometry.lua @@ -8,8 +8,9 @@ local cardinal_dirs = { } -- 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) - minetest.hash_node_position({x = 0, y = 0, z = 0}) + cardinal_dirs_hash[i] = minetest.hash_node_position(dir) - origin_hash end -- Mapping from facedir value to index in cardinal_dirs. From a998fd9d98f38be7dd7400b0f3005686cc8eb2cc Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Mon, 2 Sep 2019 20:26:27 -0600 Subject: [PATCH 50/63] tidy recipes --- nodes/recipes.lua | 186 ++++++++++++++++++++++------------------------ 1 file changed, 88 insertions(+), 98 deletions(-) diff --git a/nodes/recipes.lua b/nodes/recipes.lua index 8ea09d7..1ff70a3 100644 --- a/nodes/recipes.lua +++ b/nodes/recipes.lua @@ -12,73 +12,73 @@ 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",""} - } + {"","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_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_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",""}, - {"","",""} - } + {"","default:furnace",""}, + {"","digtron:digtron_core",""}, + {"","",""} + } }) --if minetest.get_modpath("technic") then @@ -86,47 +86,47 @@ minetest.register_craft({ -- minetest.register_craft({ -- output = "digtron:battery_holder", -- recipe = { --- {"","default:chest",""}, --- {"","digtron:digtron_core",""}, --- {"","default:steel_ingot",""} --- } +-- {"","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",""} --- } +-- {"","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",""} - } + {"","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",""} --- } +-- {"default:steel_ingot","default:steel_ingot","default:steel_ingot"}, +-- {"","digtron:digtron_core",""}, +-- {"","default:steel_ingot",""} +-- } --}) -- Structural @@ -134,37 +134,37 @@ minetest.register_craft({ 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 @@ -183,72 +183,62 @@ minetest.register_craft({ minetest.register_craft({ output = "digtron:soft_digger_static 2", - recipe = { - {"digtron:dual_soft_digger_static"}, - } + type = "shapeless", + recipe = {"digtron:dual_soft_digger_static"}, }) minetest.register_craft({ output = "digtron:digger_static 2", - recipe = { - {"digtron:dual_digger_static"}, - } + 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"}, - } + type = "shapeless", + recipe = {"digtron:structure"}, }) minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:panel"}, - } + type = "shapeless", + recipe = {"digtron:panel"}, }) minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:corner_panel"}, - } + type = "shapeless", + recipe = {"digtron:corner_panel"}, }) minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:edge_panel"}, - } + type = "shapeless", + recipe = {"digtron:edge_panel"}, }) minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:inventory"}, - } + type = "shapeless", + recipe = {"digtron:inventory"}, }) minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:fuelstore"}, - } + type = "shapeless", + recipe = {"digtron:fuelstore"}, }) minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:combined_storage"}, - } + type = "shapeless", + recipe = {"digtron:combined_storage"}, }) minetest.register_craft({ output = "digtron:digtron_core", - recipe = { - {"digtron:light"}, - } + type = "shapeless", + recipe = {"digtron:light"}, }) \ No newline at end of file From 772bcdd0f3d73f1f9d0d5fa0134746e9c274ed10 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Mon, 2 Sep 2019 22:28:41 -0600 Subject: [PATCH 51/63] add paging to inventory tabs --- controller.lua | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/controller.lua b/controller.lua index 91d03c1..95afe67 100644 --- a/controller.lua +++ b/controller.lua @@ -173,6 +173,7 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) tab_type = "inventory", listname = listname, size = #contents, + pages = math.floor(#contents/(8*4)) + 1, current_page = 1}) end context.current_tab = 1 @@ -186,12 +187,24 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) end tabs = tabs .. ";" .. context.current_tab .. "]" - local inv_tab = function(inv_list) + local inv_tab = function(inv_tab_context) + local inv_list = inv_tab_context.listname + local pages = inv_tab_context.pages + local current_page = inv_tab_context.current_page + 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;Page " .. 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,5]" -- TODO: paging system for inventory + .. "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.."]" @@ -248,7 +261,8 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) elseif context.current_tab == 2 then return sequence_tab() .. tabs else - return inv_tab(context.tabs[context.current_tab - 2].listname) .. tabs + local inv_tab_context = context.tabs[context.current_tab - 2] + return inv_tab(inv_tab_context) .. tabs end end @@ -353,6 +367,20 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) --Sequencer update_sequence(digtron_id, fields) refresh = true + else -- inventory tabs + local tab_context = context.tabs[current_tab - 2] + if fields.page_forward then + if tab_context.current_page < tab_context.pages then + tab_context.current_page = tab_context.current_page + 1 + refresh = true + end + end + if fields.page_back then + if tab_context.current_page > 1 then + tab_context.current_page = tab_context.current_page - 1 + refresh = true + end + end end if refresh then From ace967dab002b62f0b5e10c1497c9e7651575461 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Tue, 3 Sep 2019 00:47:06 -0600 Subject: [PATCH 52/63] make sequencer data structure more robust --- controller.lua | 189 +++++++++++++++++++++++++++++-------------------- functions.lua | 2 +- 2 files changed, 114 insertions(+), 77 deletions(-) diff --git a/controller.lua b/controller.lua index 95afe67..7dac24d 100644 --- a/controller.lua +++ b/controller.lua @@ -8,32 +8,64 @@ local listname_to_title = ["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 = { - S("Sequence"), - S("Dig Move Build"), - S("Dig Move Down"), - S("Move Up"), - S("Move Down"), - S("Move Left"), - S("Move Right"), - S("Move Forward"), - S("Move Back"), - S("Yaw Left"), - S("Yaw Right"), - S("Pitch Up"), - S("Pitch Down"), - S("Roll Clockwise"), - S("Roll Widdershins"), + 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"), } -local sequencer_dropdown_list = table.concat(sequencer_commands, ",") +-- 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 i, command in ipairs(sequencer_commands) do - sequencer_commands_reverse[command] = i +for cmd, command in pairs(sequencer_commands) do + sequencer_commands_reverse[command] = cmd end + +-- Manipulating sequences + +-- 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) root_index = root_index or "" @@ -42,15 +74,17 @@ create_sequence_list = function(sequence_in, list_out, root_index, x, y) for i, val in ipairs(sequence_in) do local index = root_index .. ":" .. i - local line = "dropdown[".. x ..","..y..";1.5;sequencer_com"..index..";"..sequencer_dropdown_list..";"..val[1].. - "]field[".. x+1.75 ..",".. y+0.25 ..";1,1;sequencer_cnt"..index..";;"..val[2].."]".. + local line = "dropdown[".. x ..","..y..";1.5;sequencer_com"..index..";"..sequencer_dropdown_list..";" + ..sequencer_dropdown_order_reverse[val.cmd].."]field[" + .. x+1.75 ..",".. y+0.25 ..";1,1;sequencer_cnt"..index..";;"..val.cnt.."]".. "field_close_on_enter[sequencer_cnt_"..i..";false]".. - "button[".. x+ 2.375 .. ","..y-0.0625 ..";1,1;sequencer_del"..index..";Delete]" - if val[1] == 1 then - line = line .. "button[".. x+3.25 ..","..y-0.0625 ..";1,1;sequencer_ins"..index..";Insert]" + "button[".. x+ 2.375 .. ","..y-0.0625 ..";1,1;sequencer_del"..index..";"..S("Delete").."]" + if val.cmd == "seq" then + line = line .. "button[".. x+3.25 ..","..y-0.0625 ..";1,1;sequencer_ins"..index..";"..S("Insert").."]" table.insert(list_out, line) y = y + 0.8 - y = create_sequence_list(val[3], list_out, index, x+0.5, y) + -- Recurse into sub-sequence + y = create_sequence_list(val.seq, list_out, index, x+0.5, y) else table.insert(list_out, line) y = y + 0.8 @@ -65,9 +99,9 @@ local sequencer_field_length = string.len("sequencer_com:") local find_item = function(field, sequence) local index_list = field:sub(sequencer_field_length+1):split(":") - local target = {[3] = sequence} + local target = {seq = sequence} for i = 1, #index_list do - target = target[3][tonumber(index_list[i])] + target = target.seq[tonumber(index_list[i])] if target == nil then minetest.log("error", "[Digtron] find_item failed to find a sequence item.") return nil @@ -78,77 +112,80 @@ end local delete_item = function(field, sequence) local index_list = field:sub(sequencer_field_length+1):split(":") - local target = {[3] = sequence} + local target = {seq = sequence} for i = 1, #index_list-1 do - target = target[3][tonumber(index_list[i])] + 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[3], tonumber(index_list[#index_list])) + table.remove(target.seq, tonumber(index_list[#index_list])) end --- recurses through sequences ensuring there are tables for them +-- recurses through sequences ensuring there are tables for seq commands local clean_subsequences clean_subsequences = function(sequence) for i, val in ipairs(sequence) do - if val[1] == 1 then - if val[3] == nil then - val[3] = {} + if val.cmd == "seq" then + if val.seq == nil then + val.seq = {} else - clean_subsequences(val[3]) + clean_subsequences(val.seq) end else - val[3] = nil + val.seq = nil end end end local update_sequence = function(digtron_id, fields) local sequence = digtron.get_sequence(digtron_id) - local delete_index = {} - local insert_index = {} + local delete_field = nil + local insert_field = nil for field, value in pairs(fields) do local command_type = field:sub(1,sequencer_field_length) if command_type == "sequencer_com:" then local seq_item = find_item(field, sequence) - seq_item[1] = sequencer_commands_reverse[value] + seq_item.cmd = sequencer_commands_reverse[value] 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) - seq_item[2] = val_int + seq_item.cnt = val_int 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 - table.insert(delete_index, field) + delete_field = field elseif command_type == "sequencer_ins:" then - table.insert(insert_index, field) + insert_field = field end end - for _, insert in ipairs(insert_index) do - local item = find_item(insert, sequence) - table.insert(item[3], {2,1}) + 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 - for _, delete in ipairs(delete_index) do - delete_item(delete, sequence) + + if insert_field then + local item = find_item(insert_field, sequence) + table.insert(item.seq, {cmd="dmb",cnt=1}) + elseif delete_field then + delete_item(delete_field, sequence) end if fields.sequencer_insert_end then - table.insert(sequence, {2,1}) + table.insert(sequence, {cmd="dmb",cnt=1}) end - clean_subsequences(sequence) + clean_subsequences(sequence) -- if commands were changed to or away from "seq", ensure they have a .seq member digtron.set_sequence(digtron_id, sequence) 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 = {} @@ -181,7 +218,7 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) local position_and_anchor = "position[0.025,0.1]anchor[0,0]" - local tabs = "tabheader[0,0;tab_header;Controls,Sequence" + local tabs = "tabheader[0,0;tab_header;"..S("Controls")..","..S("Sequence") for _, tab in ipairs(context.tabs) do tabs = tabs .. "," .. listname_to_title[tab.listname] or tab.listname end @@ -195,7 +232,7 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) local paging_controls = "" if pages > 1 then paging_controls = "button[0,0;1,1;page_back;<<]" - .. "label[1.125,0.25;Page " .. current_page .. "/" .. pages .. "]" + .. "label[1.125,0.25;"..S("Page @1/@2", current_page, pages) .. "]" .. "button[2,0;1,1;page_forward;>>]" end @@ -217,43 +254,43 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) return "size[4.2,5]" .. position_and_anchor .. "container[0,0]" - .. table.concat(list_out) - .. "button[0,"..y-0.0625 ..";1,1;sequencer_insert_end;Insert]" - .. "container_end[]" + .. table.concat(list_out) + .. "button[0,"..y-0.0625 ..";1,1;sequencer_insert_end;"..S("New\nCommand").."]" + .. "container_end[]" end local controls = "size[4.2,5]" .. position_and_anchor .. "container[0,0]" - .. "button[0,0;1,1;disassemble;Disassemble]" - .. "field[1.2,0.3;1.75,1;digtron_name;Digtron name;" + .. "button[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;Cycles;1]" -- TODO persist, actually use - .. "button[3.2,0;1,1;execute;Execute]" - .. "button[3.7,0;1,1;execute_down;Dig Down]" + .. "field[2.9,0.3;0.7,1;cycles;"..S("Cycles")..";1]" -- TODO persist, actually use + .. "button[3.2,0;1,1;execute;"..S("Execute").."]" + .. "button[3.7,0;1,1;execute_down;Dig Down]" -- TODO: for testing purposes only, remove once sequences actually work .. "container_end[]" .. "container[0,1]" .. "box[0,0;4,2;#DDDDDD]" - .. "label[1.8,0.825;Move]" - .. "button[1.1,0.1;1,1;move_up;Up]" - .. "button[1.1,1.1;1,1;move_down;Down]" - .. "button[2.1,0.1;1,1;move_forward;Forward]" - .. "button[2.1,1.1;1,1;move_back;Back]" - .. "button[0.1,0.6;1,1;move_left;Left]" - .. "button[3.1,0.6;1,1;move_right;Right]" + .. "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;Rotate]" - .. "button[0.1,0.1;1,1;rot_counterclockwise;Roll\nWiddershins]" - .. "button[2.1,0.1;1,1;rot_clockwise;Roll\nClockwise]" - .. "button[1.1,0.1;1,1;rot_up;Pitch Up]" - .. "button[1.1,1.1;1,1;rot_down;Pitch Down]" - .. "button[0.1,1.1;1,1;rot_left;Yaw Left]" - .. "button[2.1,1.1;1,1;rot_right;Yaw Right]" + .. "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[]" if context.current_tab == 1 then @@ -466,7 +503,7 @@ minetest.register_node("digtron:controller_unassembled", combine_defs(base_def, })) minetest.register_node("digtron:controller", combine_defs(base_def, { - description = S("Digtron Assembly"), + description = S("Digtron Assembly"), -- Will normally be obscured by the player-set Digtron name tiles = { "digtron_plate.png^[transformR90", "digtron_plate.png^[transformR270", diff --git a/functions.lua b/functions.lua index ce9318a..f8c2293 100644 --- a/functions.lua +++ b/functions.lua @@ -470,7 +470,7 @@ local assemble = function(root_pos, player_name) set_name(digtron_id, digtron_name) invalidate_layout_cache(digtron_id) persist_pos(digtron_id, root_pos) - persist_sequence(digtron_id, {{2,1}}) -- TODO find a better place to set a default like this + persist_sequence(digtron_id, {{cmd="dmb",cnt=1}}) -- TODO find a better place to set a default like this -- 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. From fe5979a1ab472cf96c20cec89328a0c1e3deda48 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sat, 7 Sep 2019 21:48:41 -0600 Subject: [PATCH 53/63] sequencer can now advance through the sequence. Still not actually hooked up to effect commands though --- controller.lua | 411 +++++++++++++++++++++++++++++++++---------------- functions.lua | 2 +- 2 files changed, 283 insertions(+), 130 deletions(-) diff --git a/controller.lua b/controller.lua index 7dac24d..73025a3 100644 --- a/controller.lua +++ b/controller.lua @@ -2,6 +2,8 @@ local MP = minetest.get_modpath(minetest.get_current_modname()) local S, NS = dofile(MP.."/intllib.lua") +local position_and_anchor = "position[0.025,0.1]anchor[0,0]" + local listname_to_title = { ["main"] = S("Main Inventory"), @@ -56,8 +58,14 @@ 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 +------------------------------------------------------------------------------------------------------------------- +-- Sequence tab formspec -- Recursively builds a formspec representation. Dropdowns and buttons are indexed with : delimiters, eg: --:1 @@ -68,42 +76,150 @@ end --: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 - local line = "dropdown[".. x ..","..y..";1.5;sequencer_com"..index..";"..sequencer_dropdown_list..";" - ..sequencer_dropdown_order_reverse[val.cmd].."]field[" - .. x+1.75 ..",".. y+0.25 ..";1,1;sequencer_cnt"..index..";;"..val.cnt.."]".. - "field_close_on_enter[sequencer_cnt_"..i..";false]".. - "button[".. x+ 2.375 .. ","..y-0.0625 ..";1,1;sequencer_del"..index..";"..S("Delete").."]" + 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 - line = line .. "button[".. x+3.25 ..","..y-0.0625 ..";1,1;sequencer_ins"..index..";"..S("Insert").."]" - table.insert(list_out, line) - y = y + 0.8 + 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.5, y) + y = create_sequence_list(val.seq, list_out, index, x+0.25, y) else - table.insert(list_out, line) - y = y + 0.8 + 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,6.75]" + .. 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 + table.insert(list_out, + "label[0.8,0.35;" .. S("@1 left", sequence.cur) .."]" + .. "button[1.5,0.1;1,0.5;execute;"..S("Execute").."]" -- TODO pause + .. "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 + +----------------------------------------------------------------------------------------- +--- Maniupulating 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 -- TODO whoops! Didn't reset a sequence or something + 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 + found = true + elseif command.cmd == "seq" then + local subsequence_result = decrement_sequence(command, target_item) + 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 and reset its constituents + 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 + +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 = {seq = sequence} + local target = sequence for i = 1, #index_list do - target = target.seq[tonumber(index_list[i])] + 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.") + --minetest.log("error", "[Digtron] find_item failed to find a sequence item.") return nil end end @@ -112,7 +228,7 @@ end local delete_item = function(field, sequence) local index_list = field:sub(sequencer_field_length+1):split(":") - local target = {seq = sequence} + local target = sequence for i = 1, #index_list-1 do target = target.seq[tonumber(index_list[i])] if target == nil then @@ -126,12 +242,12 @@ end -- recurses through sequences ensuring there are tables for seq commands local clean_subsequences clean_subsequences = function(sequence) - for i, val in ipairs(sequence) do + for i, val in ipairs(sequence.seq) do if val.cmd == "seq" then if val.seq == nil then val.seq = {} else - clean_subsequences(val.seq) + clean_subsequences(val) end else val.seq = nil @@ -139,21 +255,29 @@ clean_subsequences = function(sequence) end end +-- Handles returned fields for the sequence tab local update_sequence = function(digtron_id, fields) local sequence = digtron.get_sequence(digtron_id) local delete_field = nil - local insert_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) - seq_item.cmd = sequencer_commands_reverse[value] + 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) - seq_item.cnt = val_int + 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 @@ -173,19 +297,145 @@ local update_sequence = function(digtron_id, fields) if insert_field then local item = find_item(insert_field, sequence) - table.insert(item.seq, {cmd="dmb",cnt=1}) + -- 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 - table.insert(sequence, {cmd="dmb",cnt=1}) + -- 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 + local target = find_next_item_to_execute(sequence) + local decrement_result = decrement_sequence(sequence, target) + if decrement_result == "decrement_parent" then + sequence.cur = sequence.cur - 1 + reset_sequence(sequence) + end end - clean_subsequences(sequence) -- if commands were changed to or away from "seq", ensure they have a .seq member + if fields.reset then + 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 controls_tab = function(digtron_id) + local sequence = digtron.get_sequence(digtron_id) + return "size[4.2,5]" + .. position_and_anchor + .. "container[0,0]" + .. "button[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")..";" .. sequence.cnt .."]" + .. "label[3.2,0.3;" .. S("@1 left", sequence.cur) .."]" + .. "button[3.5,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, player_name, formname, facedir, fields) + local refresh = false + + if fields.disassemble then + local pos = digtron.disassemble(digtron_id, player_name) + minetest.close_formspec(player_name, formname) + end + + -- Translation + if fields.move_forward then + digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_dir(facedir)), player_name) + elseif fields.move_back then + digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_dir(facedir), -1)), player_name) + elseif fields.move_up then + digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_up(facedir)), player_name) + elseif fields.move_down then + digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_up(facedir), -1)), player_name) + elseif fields.move_left then + digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_right(facedir), -1)), player_name) + elseif fields.move_right then + digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_right(facedir)), player_name) + -- Rotation + elseif fields.rot_counterclockwise then + digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_dir(facedir), -1), player_name) + elseif fields.rot_clockwise then + digtron.rotate(digtron_id, digtron.facedir_to_dir(facedir), player_name) + elseif fields.rot_up then + digtron.rotate(digtron_id, digtron.facedir_to_right(facedir), player_name) + elseif fields.rot_down then + digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_right(facedir), -1), player_name) + elseif fields.rot_left then + digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_up(facedir), -1), player_name) + elseif fields.rot_right then + digtron.rotate(digtron_id, digtron.facedir_to_up(facedir), player_name) + end + + if fields.execute then + digtron.execute_dig_move_build_cycle(digtron_id, 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 + +------------------------------------------------------------------------------------------------------ + + -- 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 = {} @@ -216,8 +466,6 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) context.current_tab = 1 end - local position_and_anchor = "position[0.025,0.1]anchor[0,0]" - local tabs = "tabheader[0,0;tab_header;"..S("Controls")..","..S("Sequence") for _, tab in ipairs(context.tabs) do tabs = tabs .. "," .. listname_to_title[tab.listname] or tab.listname @@ -246,57 +494,11 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) .. "listring[current_player;main]" .. "listring[detached:" .. digtron_id .. ";"..inv_list.."]" end - - local sequence_tab = function() - local sequence = digtron.get_sequence(digtron_id) - local list_out = {} - local y = create_sequence_list(sequence, list_out) - return "size[4.2,5]" - .. position_and_anchor - .. "container[0,0]" - .. table.concat(list_out) - .. "button[0,"..y-0.0625 ..";1,1;sequencer_insert_end;"..S("New\nCommand").."]" - .. "container_end[]" - end - - local controls = "size[4.2,5]" - .. position_and_anchor - .. "container[0,0]" - .. "button[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")..";1]" -- TODO persist, actually use - .. "button[3.2,0;1,1;execute;"..S("Execute").."]" - .. "button[3.7,0;1,1;execute_down;Dig Down]" -- TODO: for testing purposes only, remove once sequences actually work - .. "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[]" - + if context.current_tab == 1 then - return controls .. tabs + return controls_tab(digtron_id) .. tabs elseif context.current_tab == 2 then - return sequence_tab() .. tabs + return sequence_tab(digtron_id) .. tabs else local inv_tab_context = context.tabs[context.current_tab - 2] return inv_tab(inv_tab_context) .. tabs @@ -349,57 +551,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if current_tab == 1 then -- Controls - if fields.disassemble then - local pos = digtron.disassemble(digtron_id, player_name) - minetest.close_formspec(player_name, formname) - end - - local facedir = node.param2 - -- Translation - if fields.move_forward then - digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_dir(facedir)), player_name) - elseif fields.move_back then - digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_dir(facedir), -1)), player_name) - elseif fields.move_up then - digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_up(facedir)), player_name) - elseif fields.move_down then - digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_up(facedir), -1)), player_name) - elseif fields.move_left then - digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_right(facedir), -1)), player_name) - elseif fields.move_right then - digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_right(facedir)), player_name) - -- Rotation - elseif fields.rot_counterclockwise then - digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_dir(facedir), -1), player_name) - elseif fields.rot_clockwise then - digtron.rotate(digtron_id, digtron.facedir_to_dir(facedir), player_name) - elseif fields.rot_up then - digtron.rotate(digtron_id, digtron.facedir_to_right(facedir), player_name) - elseif fields.rot_down then - digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_right(facedir), -1), player_name) - elseif fields.rot_left then - digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_up(facedir), -1), player_name) - elseif fields.rot_right then - digtron.rotate(digtron_id, digtron.facedir_to_up(facedir), player_name) - end - - if fields.execute then - digtron.execute_dig_move_build_cycle(digtron_id, player_name) - end - - if fields.execute_down then - digtron.execute_dig_move_build_cycle(digtron_id, player_name, 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) - end - end - + refresh = update_controls(digtron_id, player_name, formname, node.param2, fields) + elseif current_tab == 2 then --Sequencer update_sequence(digtron_id, fields) diff --git a/functions.lua b/functions.lua index f8c2293..9d28a5d 100644 --- a/functions.lua +++ b/functions.lua @@ -470,7 +470,7 @@ local assemble = function(root_pos, player_name) set_name(digtron_id, digtron_name) invalidate_layout_cache(digtron_id) persist_pos(digtron_id, root_pos) - persist_sequence(digtron_id, {{cmd="dmb",cnt=1}}) -- TODO find a better place to set a default like this + 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. From 873236667d1539243775cd786e61b218d6d27644 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 8 Sep 2019 00:20:54 -0600 Subject: [PATCH 54/63] command UI now lets multiple cycles be requested. --- controller.lua | 284 +++++++++++++++++++++++++++++++++---------------- functions.lua | 11 +- 2 files changed, 200 insertions(+), 95 deletions(-) diff --git a/controller.lua b/controller.lua index 73025a3..3d677b1 100644 --- a/controller.lua +++ b/controller.lua @@ -10,6 +10,8 @@ local listname_to_title = ["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 @@ -64,77 +66,6 @@ digtron.default_sequence = function() return {cmd="seq", cnt=1, cur=1, seq={{cmd="dmb", cnt=1, cur=1}}} 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,6.75]" - .. 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 - table.insert(list_out, - "label[0.8,0.35;" .. S("@1 left", sequence.cur) .."]" - .. "button[1.5,0.1;1,0.5;execute;"..S("Execute").."]" -- TODO pause - .. "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 - ----------------------------------------------------------------------------------------- --- Maniupulating sequences @@ -153,7 +84,7 @@ local find_next_item_to_execute = function(sequence) break end end - if not found then return nil end -- TODO whoops! Didn't reset a sequence or something + if not found then return nil end -- Sequence is finished end end end @@ -255,6 +186,172 @@ clean_subsequences = function(sequence) 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 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) + minetest.chat_send_all("Seq!") -- TODO + return true + end +} + +local 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) + local new_count = data.count - 1 + 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,6.75]" + .. 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 + table.insert(list_out, + "label[0.8,0.35;" .. S("@1 left", sequence.cur) .."]" + .. "button[1.5,0.1;1,0.5;execute;"..S("Execute").."]" -- TODO pause + .. "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) local sequence = digtron.get_sequence(digtron_id) @@ -341,8 +438,9 @@ end ------------------------------------------------------------------------------------------------------- -- Controls tab +local temp_cycles_cache = {} -- TODO + local controls_tab = function(digtron_id) - local sequence = digtron.get_sequence(digtron_id) return "size[4.2,5]" .. position_and_anchor .. "container[0,0]" @@ -350,9 +448,9 @@ local controls_tab = function(digtron_id) .. "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")..";" .. sequence.cnt .."]" - .. "label[3.2,0.3;" .. S("@1 left", sequence.cur) .."]" - .. "button[3.5,0;1,1;execute;"..S("Execute").."]" + .. "field[2.9,0.3;0.7,1;cycles;"..S("Cycles")..";".. (temp_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]" @@ -376,10 +474,9 @@ local controls_tab = function(digtron_id) .. "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, player_name, formname, facedir, fields) +local update_controls = function(digtron_id, pos, player_name, formname, facedir, fields) local refresh = false if fields.disassemble then @@ -387,36 +484,39 @@ local update_controls = function(digtron_id, player_name, formname, facedir, fie minetest.close_formspec(player_name, formname) end + local cycles = math.max(math.floor(tonumber(fields.cycles) or 1), 1) + temp_cycles_cache[digtron_id] = cycles + -- Translation if fields.move_forward then - digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_dir(facedir)), player_name) + start_command(digtron_id, "mfw", cycles, player_name) elseif fields.move_back then - digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_dir(facedir), -1)), player_name) + start_command(digtron_id, "mbk", cycles, player_name) elseif fields.move_up then - digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_up(facedir)), player_name) + start_command(digtron_id, "mup", cycles, player_name) elseif fields.move_down then - digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_up(facedir), -1)), player_name) + start_command(digtron_id, "mdn", cycles, player_name) elseif fields.move_left then - digtron.move(digtron_id, vector.add(pos, vector.multiply(digtron.facedir_to_right(facedir), -1)), player_name) + start_command(digtron_id, "mlt", cycles, player_name) elseif fields.move_right then - digtron.move(digtron_id, vector.add(pos, digtron.facedir_to_right(facedir)), player_name) + start_command(digtron_id, "mrt", cycles, player_name) -- Rotation elseif fields.rot_counterclockwise then - digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_dir(facedir), -1), player_name) + start_command(digtron_id, "rcc", cycles, player_name) elseif fields.rot_clockwise then - digtron.rotate(digtron_id, digtron.facedir_to_dir(facedir), player_name) + start_command(digtron_id, "rcl", cycles, player_name) elseif fields.rot_up then - digtron.rotate(digtron_id, digtron.facedir_to_right(facedir), player_name) + start_command(digtron_id, "rup", cycles, player_name) elseif fields.rot_down then - digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_right(facedir), -1), player_name) + start_command(digtron_id, "rdn", cycles, player_name) elseif fields.rot_left then - digtron.rotate(digtron_id, vector.multiply(digtron.facedir_to_up(facedir), -1), player_name) + start_command(digtron_id, "rlt", cycles, player_name) elseif fields.rot_right then - digtron.rotate(digtron_id, digtron.facedir_to_up(facedir), player_name) + start_command(digtron_id, "rrt", cycles, player_name) end if fields.execute then - digtron.execute_dig_move_build_cycle(digtron_id, player_name) + start_command(digtron_id, "dmb", cycles, player_name) refresh = true end @@ -551,7 +651,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if current_tab == 1 then -- Controls - refresh = update_controls(digtron_id, player_name, formname, node.param2, fields) + refresh = update_controls(digtron_id, pos, player_name, formname, node.param2, fields) elseif current_tab == 2 then --Sequencer diff --git a/functions.lua b/functions.lua index 9d28a5d..abdb239 100644 --- a/functions.lua +++ b/functions.lua @@ -736,14 +736,14 @@ local move = function(digtron_id, dest_pos, player_name) 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 @@ -800,9 +800,11 @@ local rotate = function(digtron_id, axis, player_name) 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=root_pos}) + return false end end @@ -1155,12 +1157,13 @@ local execute_dig_move_build_cycle = function(digtron_id, player_name, dig_down) 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 a problem, but don't crash if it does happen somehow + 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) @@ -1169,6 +1172,7 @@ local execute_dig_move_build_cycle = function(digtron_id, player_name, dig_down) 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) @@ -1210,6 +1214,7 @@ local execute_dig_move_build_cycle = function(digtron_id, player_name, dig_down) commit_predictive_inventory(digtron_id) end + return true end end From f83f5cba1104588dc6679275184c92c0f0fce0a3 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 8 Sep 2019 00:23:28 -0600 Subject: [PATCH 55/63] remove need to pass formname around --- controller.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/controller.lua b/controller.lua index 3d677b1..a903a6f 100644 --- a/controller.lua +++ b/controller.lua @@ -444,7 +444,7 @@ local controls_tab = function(digtron_id) return "size[4.2,5]" .. position_and_anchor .. "container[0,0]" - .. "button[0,0;1,1;disassemble;"..S("Disassemble").."]" + .. "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]" @@ -476,12 +476,11 @@ local controls_tab = function(digtron_id) .. "container_end[]" end -local update_controls = function(digtron_id, pos, player_name, formname, facedir, fields) +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) - minetest.close_formspec(player_name, formname) end local cycles = math.max(math.floor(tonumber(fields.cycles) or 1), 1) @@ -651,7 +650,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if current_tab == 1 then -- Controls - refresh = update_controls(digtron_id, pos, player_name, formname, node.param2, fields) + refresh = update_controls(digtron_id, pos, player_name, node.param2, fields) elseif current_tab == 2 then --Sequencer From 61be57ed4513f50fee617f7e3142fbf803d074c1 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 8 Sep 2019 00:46:54 -0600 Subject: [PATCH 56/63] give builder items a little love --- controller.lua | 6 +++--- entities.lua | 23 ++++++++++++++++++++++- functions.lua | 3 --- nodes/node_builder.lua | 1 + 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/controller.lua b/controller.lua index a903a6f..2e32c2b 100644 --- a/controller.lua +++ b/controller.lua @@ -14,10 +14,10 @@ local listname_to_title = -- 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 +-- 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 = +local sequencer_commands = { seq = S("Sequence"), dmb = S("Dig Move Build"), @@ -37,7 +37,7 @@ local sequencer_commands = } -- This sets the order in which they'll be shown on the dropdown menu -local sequencer_dropdown_order = +local sequencer_dropdown_order = { "seq", "dmb", "dmd", "mup", "mdn", "mlt", "mrt", "mfw", "mbk", "rlt", "rrt", "rup", "rdn", "rcl", "rcc" } diff --git a/entities.lua b/entities.lua index 5b1fe03..b2b9819 100644 --- a/entities.lua +++ b/entities.lua @@ -158,6 +158,7 @@ digtron.remove_builder_item = function(pos) end end +-- Used by unassembled builders digtron.update_builder_item = function(pos) local node = minetest.get_node(pos) if minetest.get_node_group(node.name, "digtron") ~= 4 then @@ -169,7 +170,27 @@ digtron.update_builder_item = function(pos) local item = meta:get_string("item") if item ~= "" then digtron.create_builder_item = item - minetest.add_entity(target_pos,"digtron:builder_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 hash, data in pairs(layout) do + local node = data.node + if minetest.get_node_group(node.name, "digtron") == 4 then + local item = data.meta.fields.item + local pos = vector.add(minetest.get_position_from_hash(hash), 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 diff --git a/functions.lua b/functions.lua index abdb239..d4f730c 100644 --- a/functions.lua +++ b/functions.lua @@ -191,7 +191,6 @@ 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") -local persist_step, retrieve_step = get_table_functions("step") -- actually just an integer, but table_functions works for that too ------------------------------------------------------------------------------------------------------- -- Layout creation helpers @@ -1383,8 +1382,6 @@ digtron.get_bounding_box = retrieve_bounding_box digtron.get_inventory = retrieve_inventory digtron.set_sequence = persist_sequence digtron.get_sequence = retrieve_sequence -digtron.set_step = persist_step -digtron.get_step = retrieve_step -- Used by duplicator digtron.get_layout = retrieve_layout diff --git a/nodes/node_builder.lua b/nodes/node_builder.lua index 2962aa2..ad25d74 100644 --- a/nodes/node_builder.lua +++ b/nodes/node_builder.lua @@ -128,6 +128,7 @@ local builder_on_rightclick = function(pos, node, clicker, itemstack, pointed_th if digtron_id ~= "" then minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=player_name}) minetest.chat_send_player(player_name, S("This Digtron is active, interact with it via the controller node.")) + digtron.update_builder_items(digtron_id) return end From 926f72286e5022d468b2776585c9ebb876341bbf Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 8 Sep 2019 05:36:50 -0600 Subject: [PATCH 57/63] more loopable engine sound, supporting sequences better --- controller.lua | 75 +++++++++++++++++++++++++++++--------- functions.lua | 2 +- sounds/digtron_engine.ogg | Bin 0 -> 24867 bytes sounds/license.txt | 1 + 4 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 sounds/digtron_engine.ogg diff --git a/controller.lua b/controller.lua index 2e32c2b..7098a49 100644 --- a/controller.lua +++ b/controller.lua @@ -205,6 +205,9 @@ 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, @@ -235,12 +238,26 @@ local command_functions = { 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) - minetest.chat_send_all("Seq!") -- TODO - return true + 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 } -local execute_command = function(digtron_id, command, player_name) +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 @@ -266,7 +283,14 @@ minetest.register_globalstep(function(dtime) 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) - local new_count = data.count - 1 + 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 @@ -353,7 +377,7 @@ local sequence_tab = function(digtron_id) end -- Handles returned fields for the sequence tab -local update_sequence = function(digtron_id, fields) +local update_sequence = function(digtron_id, fields, player_name) local sequence = digtron.get_sequence(digtron_id) local delete_field = nil local insert_field = nil @@ -418,15 +442,11 @@ local update_sequence = function(digtron_id, fields) end if fields.execute and sequence.cur > 0 then - local target = find_next_item_to_execute(sequence) - local decrement_result = decrement_sequence(sequence, target) - if decrement_result == "decrement_parent" then - sequence.cur = sequence.cur - 1 - reset_sequence(sequence) - end + start_command(digtron_id, "seq", 1, player_name) end if fields.reset then + cancel_command(digtron_id) reset_sequence(sequence) sequence.cur = sequence.cnt end @@ -438,7 +458,7 @@ end ------------------------------------------------------------------------------------------------------- -- Controls tab -local temp_cycles_cache = {} -- TODO +local cycles_cache = {} -- TODO something's not saving right here local controls_tab = function(digtron_id) return "size[4.2,5]" @@ -448,7 +468,7 @@ local controls_tab = function(digtron_id) .. "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")..";".. (temp_cycles_cache[digtron_id] or 1) .."]" + .. "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[]" @@ -484,7 +504,7 @@ local update_controls = function(digtron_id, pos, player_name, facedir, fields) end local cycles = math.max(math.floor(tonumber(fields.cycles) or 1), 1) - temp_cycles_cache[digtron_id] = cycles + cycles_cache[digtron_id] = cycles -- Translation if fields.move_forward then @@ -534,10 +554,15 @@ 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 player_opening_formspec = function(digtron_id, player_name) + local context = player_interacting_with_digtron_id[player_name] or {} + context.digtron_id = digtron_id + context.open = true + player_interacting_with_digtron_id[player_name] = context +end local get_controller_assembled_formspec = function(digtron_id, player_name) local context = player_interacting_with_digtron_id[player_name] @@ -604,6 +629,16 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) end end +refresh_open_formspec = function(digtron_id) + for player_name, context in pairs(player_interacting_with_digtron_id) do + if context.open and context.digtron_id == digtron_id 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 @@ -654,7 +689,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) elseif current_tab == 2 then --Sequencer - update_sequence(digtron_id, fields) + update_sequence(digtron_id, fields, player_name) refresh = true else -- inventory tabs local tab_context = context.tabs[current_tab - 2] @@ -672,6 +707,10 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end + if fields.quit then + context.open = false + end + if refresh then minetest.show_formspec(player_name, "digtron:controller_assembled", @@ -746,7 +785,7 @@ minetest.register_node("digtron:controller_unassembled", combine_defs(base_def, local meta = minetest.get_meta(pos) meta:set_string("digtron_id", digtron_id) meta:mark_as_private("digtron_id") - player_interacting_with_digtron_id[player_name] = {digtron_id = digtron_id} + player_opening_formspec(digtron_id, player_name) minetest.show_formspec(player_name, "digtron:controller_assembled", get_controller_assembled_formspec(digtron_id, player_name)) @@ -904,7 +943,7 @@ minetest.register_node("digtron:controller", combine_defs(base_def, { end end - player_interacting_with_digtron_id[player_name] = {digtron_id = digtron_id} + player_opening_formspec(digtron_id, player_name) minetest.show_formspec(player_name, "digtron:controller_assembled", get_controller_assembled_formspec(digtron_id, player_name)) diff --git a/functions.lua b/functions.lua index d4f730c..f923023 100644 --- a/functions.lua +++ b/functions.lua @@ -730,7 +730,7 @@ local move = function(digtron_id, dest_pos, player_name) 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_truck", {gain = 0.5, pos=dest_pos}) + 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 diff --git a/sounds/digtron_engine.ogg b/sounds/digtron_engine.ogg new file mode 100644 index 0000000000000000000000000000000000000000..996cc8c7d8e33043caf94bd7d312535e31888071 GIT binary patch literal 24867 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⁢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 literal 0 HcmV?d00001 diff --git a/sounds/license.txt b/sounds/license.txt index 33e9200..fd4df56 100644 --- a/sounds/license.txt +++ b/sounds/license.txt @@ -12,6 +12,7 @@ digtron_whirr.ogg - https://www.freesound.org/people/daveincamas/sounds/25034/ - 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: From 8f70b982fda8572fb6504729cd989a2cb04cdadf Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 8 Sep 2019 13:02:10 -0600 Subject: [PATCH 58/63] fix a bug with building into freshly-dug spots --- controller.lua | 5 ++++- functions.lua | 8 +++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/controller.lua b/controller.lua index 7098a49..b040e7c 100644 --- a/controller.lua +++ b/controller.lua @@ -557,6 +557,7 @@ 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 = {} +-- Call this when the player opens a formspec to initialize these values local player_opening_formspec = function(digtron_id, player_name) local context = player_interacting_with_digtron_id[player_name] or {} context.digtron_id = digtron_id @@ -629,9 +630,11 @@ local get_controller_assembled_formspec = function(digtron_id, player_name) 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) for player_name, context in pairs(player_interacting_with_digtron_id) do - if context.open and context.digtron_id == digtron_id then + if context.open and context.digtron_id == digtron_id and context.current_tab == 2 then minetest.show_formspec(player_name, "digtron:controller_assembled", get_controller_assembled_formspec(digtron_id, player_name)) diff --git a/functions.lua b/functions.lua index f923023..2bcf56c 100644 --- a/functions.lua +++ b/functions.lua @@ -44,7 +44,9 @@ local damage_creatures = function(root_pos, punch_data, items_dropped) lua_entity.itemstring = "" obj:remove() else - lua_entity:add_velocity(dir) + 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 @@ -992,6 +994,7 @@ local predict_build = function(digtron_id, root_pos, player_name, ignore_nodes, local periodicity_permitted = nil for i = 1, builder_data.extrusion do local target_pos = vector.add(minetest.get_position_from_hash(target_hash + i * dir_hash), 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 @@ -1003,7 +1006,7 @@ local predict_build = function(digtron_id, root_pos, player_name, ignore_nodes, local target_name = target_node.name local targetdef = minetest.registered_nodes[target_name] if - ignore_hashes[target_hash] or + ignore_hashes[test_hash] or (targetdef ~= nil and targetdef.buildable_to and not protection_check(target_pos, player_name) @@ -1031,7 +1034,6 @@ local predict_build = function(digtron_id, root_pos, player_name, ignore_nodes, end end end - return missing_items, built_nodes, cost end From b568fffdc82031af5c853afd0163692ac0d852f5 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 8 Sep 2019 14:42:22 -0600 Subject: [PATCH 59/63] fix bug in rotate, wasn't invalidating adjacent node cache --- functions.lua | 1 + inventories.lua | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/functions.lua b/functions.lua index 2bcf56c..213189f 100644 --- a/functions.lua +++ b/functions.lua @@ -800,6 +800,7 @@ local rotate = function(digtron_id, axis, player_name) for _, removed_pos in ipairs(removed) do minetest.check_for_falling(removed_pos) end + invalidate_layout_cache(digtron_id) end return true else diff --git a/inventories.lua b/inventories.lua index b902a7b..7704620 100644 --- a/inventories.lua +++ b/inventories.lua @@ -113,7 +113,8 @@ local get_predictive_inventory = function(digtron_id) local existing = predictive_inventory[digtron_id] if existing then return existing end - local predictive_inv = minetest.create_detached_inventory("digtron_predictive_"..digtron_id, detached_inventory_callbacks) + -- 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) From d79b22673b98205b1d5d21ce908a10a2cfa52460 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 8 Sep 2019 19:25:29 -0600 Subject: [PATCH 60/63] rearrange context cache to play nice with players using multiple digtrons --- controller.lua | 148 ++++++++++++++++++++++++++----------------------- 1 file changed, 79 insertions(+), 69 deletions(-) diff --git a/controller.lua b/controller.lua index b040e7c..8bd1c6f 100644 --- a/controller.lua +++ b/controller.lua @@ -552,89 +552,102 @@ local update_controls = function(digtron_id, pos, player_name, facedir, fields) 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 = {} --- Call this when the player opens a formspec to initialize these values -local player_opening_formspec = function(digtron_id, player_name) - local context = player_interacting_with_digtron_id[player_name] or {} - context.digtron_id = digtron_id - context.open = true - player_interacting_with_digtron_id[player_name] = context +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 context = player_interacting_with_digtron_id[player_name] - if context == nil or context.digtron_id ~= digtron_id then - minetest.log("error", "[Digtron] get_controller_assembled_formspec was called for Digtron " - ..digtron_id .. " by " .. player_name .. " but there was no context recorded or the context was" - .." for the wrong digtron. This shouldn't be possible.") - return "" - end - + 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 - -- TODO: will probably want a centralized cache for most of this, right now there's tons of redundancy - if context.tabs == nil then - context.tabs = {} + if digtron_context.tabs == nil then + digtron_context.tabs = {} local lists = inv:get_lists() for listname, contents in pairs(lists) do - table.insert(context.tabs, { + table.insert(digtron_context.tabs, { tab_type = "inventory", listname = listname, size = #contents, - pages = math.floor(#contents/(8*4)) + 1, - current_page = 1}) + 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 - context.current_tab = 1 end local tabs = "tabheader[0,0;tab_header;"..S("Controls")..","..S("Sequence") - for _, tab in ipairs(context.tabs) do + for _, tab in ipairs(digtron_context.tabs) do tabs = tabs .. "," .. listname_to_title[tab.listname] or tab.listname end - tabs = tabs .. ";" .. context.current_tab .. "]" + tabs = tabs .. ";" .. player_context.current_tab .. "]" - local inv_tab = function(inv_tab_context) - local inv_list = inv_tab_context.listname - local pages = inv_tab_context.pages - local current_page = inv_tab_context.current_page - 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 - - if context.current_tab == 1 then + if player_context.current_tab == 1 then return controls_tab(digtron_id) .. tabs - elseif context.current_tab == 2 then + elseif player_context.current_tab == 2 then return sequence_tab(digtron_id) .. tabs else - local inv_tab_context = context.tabs[context.current_tab - 2] - return inv_tab(inv_tab_context) .. tabs + 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) - for player_name, context in pairs(player_interacting_with_digtron_id) do - if context.open and context.digtron_id == digtron_id and context.current_tab == 2 then + 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)) @@ -650,16 +663,11 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local player_name = player:get_player_name() -- Get and validate various values - local context = player_interacting_with_digtron_id[player_name] - if context == nil then + 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 digtron_id = context.digtron_id - if digtron_id == nil then - minetest.log("error", "[Digtron] player_interacting_with_digtron_id context had no digtron id 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) @@ -673,12 +681,12 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) return end - local current_tab = context.current_tab + 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 <= #(context.tabs) + 2 then - context.current_tab = new_tab + 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 " @@ -695,23 +703,24 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) update_sequence(digtron_id, fields, player_name) refresh = true else -- inventory tabs - local tab_context = context.tabs[current_tab - 2] + 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 tab_context.current_page < tab_context.pages then - tab_context.current_page = tab_context.current_page + 1 + 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 tab_context.current_page > 1 then - tab_context.current_page = tab_context.current_page - 1 + if current_page > 1 then + player_context.inv_page[tab_context.listname] = current_page - 1 refresh = true end end end if fields.quit then - context.open = false + player_context.open = false end if refresh then @@ -946,7 +955,8 @@ minetest.register_node("digtron:controller", combine_defs(base_def, { end end - player_opening_formspec(digtron_id, player_name) + 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)) From 1e776defd5378c30b7774a1022cfe02bba9b4618 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sat, 22 Feb 2020 13:51:41 -0700 Subject: [PATCH 61/63] update to client-side translation system --- controller.lua | 17 +- depends.txt | 3 - functions.lua | 4 +- i18n.py | 418 +++++++++++++++++++++++++++++ init.lua | 5 +- intllib.lua | 45 ---- locale/template.pot | 276 ------------------- locale/template.txt | 149 ++++++++++ locale/update.bat | 6 - mod.conf | 2 +- nodes/node_builder.lua | 4 +- nodes/node_digger.lua | 4 +- nodes/node_duplicator.lua | 17 +- nodes/node_misc.lua | 4 +- nodes/node_pipeworks_interface.lua | 134 +++++++++ nodes/node_storage.lua | 4 +- nodes/recipes.lua | 4 +- 17 files changed, 723 insertions(+), 373 deletions(-) delete mode 100644 depends.txt create mode 100644 i18n.py delete mode 100644 intllib.lua delete mode 100644 locale/template.pot create mode 100644 locale/template.txt delete mode 100644 locale/update.bat create mode 100644 nodes/node_pipeworks_interface.lua diff --git a/controller.lua b/controller.lua index 8bd1c6f..af9b931 100644 --- a/controller.lua +++ b/controller.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 local position_and_anchor = "position[0.025,0.1]anchor[0,0]" @@ -100,13 +98,13 @@ decrement_sequence = function(sequence, target_item) -- return without decrementing its parent return "found" elseif command == target_item then - target_item.cur = target_item.cur - 1 - found = true + 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) + 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 and reset its constituents + -- 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 @@ -123,6 +121,7 @@ decrement_sequence = function(sequence, target_item) 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 @@ -350,7 +349,7 @@ end local sequence_tab = function(digtron_id) local sequence = digtron.get_sequence(digtron_id) - local list_out = {"size[5.75,6.75]" + local list_out = {"size[5.75,12]" .. position_and_anchor .. "real_coordinates[true]" @@ -797,7 +796,7 @@ minetest.register_node("digtron:controller_unassembled", combine_defs(base_def, local meta = minetest.get_meta(pos) meta:set_string("digtron_id", digtron_id) meta:mark_as_private("digtron_id") - player_opening_formspec(digtron_id, player_name) + get_context(digtron_id, player_name) minetest.show_formspec(player_name, "digtron:controller_assembled", get_controller_assembled_formspec(digtron_id, player_name)) diff --git a/depends.txt b/depends.txt deleted file mode 100644 index d5c409f..0000000 --- a/depends.txt +++ /dev/null @@ -1,3 +0,0 @@ -default -intllib? -creative? \ No newline at end of file diff --git a/functions.lua b/functions.lua index 213189f..e24393b 100644 --- a/functions.lua +++ b/functions.lua @@ -2,9 +2,7 @@ local mod_meta = digtron.mod_meta local cache = {} --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") +local S = digtron.S --minetest.debug(dump(mod_meta:to_table())) 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 35f150a..f7d2ab2 100644 --- a/init.lua +++ b/init.lua @@ -1,6 +1,9 @@ digtron = {} digtron.doc = {} -- TODO: move to doc file +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 = { ["default:torch_ceiling"] = "default:torch", @@ -36,7 +39,7 @@ digtron.builder_on_place_prefixes = { digtron.mod_meta = minetest.get_mod_storage() -local modpath = minetest.get_modpath(minetest.get_current_modname()) +local modpath = minetest.get_modpath(modname) dofile(modpath.."/config.lua") dofile(modpath.."/class_fakeplayer.lua") 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/locale/template.pot b/locale/template.pot deleted file mode 100644 index 49e49d1..0000000 --- a/locale/template.pot +++ /dev/null @@ -1,276 +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-09-01 18:19-0600\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" - -#: digtron\controller.lua:7 -msgid "Main Inventory" -msgstr "" - -#: digtron\controller.lua:8 -msgid "Fuel" -msgstr "" - -#: digtron\controller.lua:13 -msgid "Sequence" -msgstr "" - -#: digtron\controller.lua:14 -msgid "Dig Move Build" -msgstr "" - -#: digtron\controller.lua:15 -msgid "Dig Move Down" -msgstr "" - -#: digtron\controller.lua:16 -msgid "Move Up" -msgstr "" - -#: digtron\controller.lua:17 -msgid "Move Down" -msgstr "" - -#: digtron\controller.lua:18 -msgid "Move Left" -msgstr "" - -#: digtron\controller.lua:19 -msgid "Move Right" -msgstr "" - -#: digtron\controller.lua:20 -msgid "Move Forward" -msgstr "" - -#: digtron\controller.lua:21 -msgid "Move Back" -msgstr "" - -#: digtron\controller.lua:22 -msgid "Yaw Left" -msgstr "" - -#: digtron\controller.lua:23 -msgid "Yaw Right" -msgstr "" - -#: digtron\controller.lua:24 -msgid "Pitch Up" -msgstr "" - -#: digtron\controller.lua:25 -msgid "Pitch Down" -msgstr "" - -#: digtron\controller.lua:26 -msgid "Roll Clockwise" -msgstr "" - -#: digtron\controller.lua:27 -msgid "Roll Widdershins" -msgstr "" - -#: digtron\controller.lua:402 -msgid "Digtron Control Module" -msgstr "" - -#: digtron\controller.lua:436 -msgid "Digtron Assembly" -msgstr "" - -#: digtron\functions.lua:65 -msgid "Unnamed Digtron" -msgstr "" - -#: digtron\functions.lua:1012 -msgid "@1 at @2 has encountered an obstacle." -msgstr "" - -#: digtron\functions.lua:1023 -msgid "@1 at @2 requires @3 to execute its next build cycle." -msgstr "" - -#: digtron\nodes\node_builder.lua:25 -msgid "Extrusion" -msgstr "" - -#: digtron\nodes\node_builder.lua:27 -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 "" - -#: digtron\nodes\node_builder.lua:28 -#: digtron\nodes\node_digger.lua:20 -msgid "Periodicity" -msgstr "" - -#: digtron\nodes\node_builder.lua:30 -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 "" - -#: digtron\nodes\node_builder.lua:31 -#: digtron\nodes\node_digger.lua:23 -msgid "Offset" -msgstr "" - -#: digtron\nodes\node_builder.lua:33 -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 "" - -#: digtron\nodes\node_builder.lua:34 -#: digtron\nodes\node_digger.lua:26 -msgid "" -"Save &\n" -"Show" -msgstr "" - -#: digtron\nodes\node_builder.lua:35 -msgid "" -"Saves settings, closes interface, and shows the locations this builder will " -"build to in-world." -msgstr "" - -#: digtron\nodes\node_builder.lua:36 -msgid "Facing" -msgstr "" - -#: digtron\nodes\node_builder.lua:38 -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 "" - -#: digtron\nodes\node_builder.lua:39 -msgid "Read" -msgstr "" - -#: digtron\nodes\node_builder.lua:40 -msgid "Reads the facing of the block currently in the build location." -msgstr "" - -#: digtron\nodes\node_builder.lua:130 -#: digtron\nodes\node_storage.lua:73 -#: digtron\nodes\node_storage.lua:178 -#: digtron\nodes\node_storage.lua:305 -msgid "This Digtron is active, interact with it via the controller node." -msgstr "" - -#: digtron\nodes\node_builder.lua:223 -msgid "" -"Builder for @1\n" -"period @2, offset @3, extrusion @4" -msgstr "" - -#: digtron\nodes\node_builder.lua:232 -msgid "Digtron Builder Module" -msgstr "" - -#: digtron\nodes\node_digger.lua:22 -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 "" - -#: digtron\nodes\node_digger.lua:25 -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 "" - -#: digtron\nodes\node_digger.lua:27 -msgid "Saves settings" -msgstr "" - -#: digtron\nodes\node_digger.lua:35 -msgid "" -"Digger\n" -"period @1, offset @2" -msgstr "" - -#: digtron\nodes\node_digger.lua:71 -#: digtron\nodes\node_digger.lua:103 -msgid "Digtron Digger" -msgstr "" - -#: digtron\nodes\node_digger.lua:128 -#: digtron\nodes\node_digger.lua:160 -msgid "Digtron Dual Digger" -msgstr "" - -#: digtron\nodes\node_misc.lua:7 -msgid "Digtron Structure" -msgstr "" - -#: digtron\nodes\node_misc.lua:41 -msgid "Digtron Light" -msgstr "" - -#: digtron\nodes\node_misc.lua:64 -msgid "Digtron Panel" -msgstr "" - -#: digtron\nodes\node_misc.lua:88 -msgid "Digtron Edge Panel" -msgstr "" - -#: digtron\nodes\node_misc.lua:117 -msgid "Digtron Corner Panel" -msgstr "" - -#: digtron\nodes\node_pipeworks_interface.lua:85 -msgid "Digtron Inventory Interface" -msgstr "" - -#: digtron\nodes\node_storage.lua:9 -#: digtron\nodes\node_storage.lua:213 -msgid "Inventory items" -msgstr "" - -#: digtron\nodes\node_storage.lua:20 -msgid "Digtron Inventory Storage" -msgstr "" - -#: digtron\nodes\node_storage.lua:102 -#: digtron\nodes\node_storage.lua:215 -msgid "Fuel items" -msgstr "" - -#: digtron\nodes\node_storage.lua:113 -msgid "Digtron Fuel Storage" -msgstr "" - -#: digtron\nodes\node_storage.lua:226 -msgid "Digtron Combined Storage" -msgstr "" - -#: digtron\nodes\recipes.lua:6 -msgid "Digtron Core" -msgstr "" diff --git a/locale/template.txt b/locale/template.txt new file mode 100644 index 0000000..bb72b47 --- /dev/null +++ b/locale/template.txt @@ -0,0 +1,149 @@ +# 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= +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 1e351e6..6dd7671 100644 --- a/mod.conf +++ b/mod.conf @@ -6,4 +6,4 @@ license = MIT, LGPL 2.1 or later forum = https://forum.minetest.net/viewtopic.php?t=16295 version = 2.0 depends = default -optional_depends = intllib, creative \ No newline at end of file +optional_depends = creative, pipeworks \ No newline at end of file diff --git a/nodes/node_builder.lua b/nodes/node_builder.lua index ad25d74..95eb4d8 100644 --- a/nodes/node_builder.lua +++ b/nodes/node_builder.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 -- Note: builders go in group 4 diff --git a/nodes/node_digger.lua b/nodes/node_digger.lua index e986ab5..001ae53 100644 --- a/nodes/node_digger.lua +++ b/nodes/node_digger.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 -- TODO: make global, is used by builders too local player_interacting_with_digtron_pos = {} diff --git a/nodes/node_duplicator.lua b/nodes/node_duplicator.lua index 7034c24..97690cb 100644 --- a/nodes/node_duplicator.lua +++ b/nodes/node_duplicator.lua @@ -1,15 +1,4 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - -local strip_digtron_prefix = function(desc) - -- Having "Digtron " prefixing every description is annoying - local prefix = "Digtron " - if desc:sub(1,#prefix) == prefix then - desc = desc:sub(#prefix+1) - end - return desc -end +local S = digtron.S local get_manifest = function(pos) local manifest = {} @@ -27,7 +16,7 @@ local get_manifest = function(pos) item = item_def._digtron_disassembled_node item_def = minetest.registered_items[item] end - local desc = strip_digtron_prefix(item_def.description) + local desc = item_def.description local entry = manifest[desc] if entry == nil then entry = {item = item} @@ -43,7 +32,7 @@ local get_manifest = function(pos) local main_list = inv:get_list("main") for _, itemstack in ipairs(main_list) do if not itemstack:is_empty() then - local desc = strip_digtron_prefix(itemstack:get_definition().description) + local desc = itemstack:get_definition().description local entry = manifest[desc] if entry == nil then entry = {item = item} diff --git a/nodes/node_misc.lua b/nodes/node_misc.lua index 9ae346d..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", { 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_storage.lua b/nodes/node_storage.lua index 1bd5dae..a0b4ffe 100644 --- a/nodes/node_storage.lua +++ b/nodes/node_storage.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 --local pipeworks_path = minetest.get_modpath("pipeworks") diff --git a/nodes/recipes.lua b/nodes/recipes.lua index 1ff70a3..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"), From 6745446fcd64d7c8e5bd00c28128105e43de1de2 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 23 Feb 2020 23:39:01 -0700 Subject: [PATCH 62/63] fix name preservation, add stop button --- controller.lua | 30 +++++++++++++++--------------- functions.lua | 10 +++++++--- locale/template.txt | 1 + 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/controller.lua b/controller.lua index af9b931..e124b05 100644 --- a/controller.lua +++ b/controller.lua @@ -65,7 +65,7 @@ digtron.default_sequence = function() end ----------------------------------------------------------------------------------------- ---- Maniupulating sequences +--- 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) @@ -360,9 +360,14 @@ local sequence_tab = function(digtron_id) 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[1.5,0.1;1,0.5;execute;"..S("Execute").."]" -- TODO pause .. "button[2.5,0.1;1,0.5;reset;"..S("Reset").."]" .. "container_end[]" .. "container[0.2,1]" @@ -444,6 +449,10 @@ local update_sequence = function(digtron_id, fields, player_name) 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) @@ -828,7 +837,7 @@ minetest.register_node("digtron:controller", combine_defs(base_def, { 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_string("description", meta:get_string("infotext")) + 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 @@ -892,6 +901,9 @@ minetest.register_node("digtron:controller", combine_defs(base_def, { 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. @@ -918,18 +930,6 @@ minetest.register_node("digtron:controller", combine_defs(base_def, { end end, - after_place_node = function(pos, placer, itemstack, pointed_thing) - local stack_meta = itemstack:get_meta() - local title = stack_meta:get_string("description") - local digtron_id = stack_meta:get_string("digtron_id") - - local meta = minetest.get_meta(pos) - - meta:set_string("infotext", title) - meta:set_string("digtron_id", digtron_id) - meta:mark_as_private("digtron_id") - 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 diff --git a/functions.lua b/functions.lua index e24393b..221f254 100644 --- a/functions.lua +++ b/functions.lua @@ -16,7 +16,7 @@ local damage_hp = digtron.config.damage_hp -- see predict_dig for how punch_data gets calculated local damage_creatures = function(root_pos, punch_data, items_dropped) local target_pos = punch_data[2] - local objects = minetest.env:get_objects_inside_radius(target_pos, 1.0) + local objects = minetest.get_objects_inside_radius(target_pos, 1.0) if objects ~= nil then local source_pos = vector.add(minetest.get_position_from_hash(punch_data[1]), root_pos) for _, obj in ipairs(objects) do @@ -52,7 +52,7 @@ local damage_creatures = function(root_pos, punch_data, items_dropped) 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) + 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 @@ -422,7 +422,8 @@ local assemble = function(root_pos, player_name) local layout = {} for hash, node in pairs(digtron_nodes) do - local relative_hash = minetest.hash_node_position(vector.subtract(minetest.get_position_from_hash(hash), root_pos)) + local pos = minetest.get_position_from_hash(hash) + local relative_hash = minetest.hash_node_position(vector.subtract(pos, root_pos)) local current_meta if hash == root_hash then @@ -462,6 +463,9 @@ local assemble = function(root_pos, player_name) node.param1 = nil -- we don't care about param1, wipe it to save space layout[relative_hash] = {meta = current_meta_table, node = node} + local meta = minetest.get_meta(pos) + -- track this so that we can interact with individual node settings in the assembled digtron + meta:set_string("digtron_relative_hash", relative_hash) end persist_inventory(digtron_id) diff --git a/locale/template.txt b/locale/template.txt index bb72b47..08963c5 100644 --- a/locale/template.txt +++ b/locale/template.txt @@ -40,6 +40,7 @@ Roll@nClockwise= Roll@nWiddershins= Rotate= Sequence= +Stop= Up= Yaw Left= Yaw Right= From 3c0fbca66facb9a39df4b8051bca537d7e7f1c64 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sun, 1 Mar 2020 23:52:24 -0700 Subject: [PATCH 63/63] make it possible to edit builders that are part of an assembled digtron Should make it possible to edit other components too, later on. --- controller.lua | 9 +- entities.lua | 12 +- functions.lua | 229 ++++++++++++++++++++++++-------------- init.lua | 1 - nodes/node_builder.lua | 121 +++++++++++++------- nodes/node_duplicator.lua | 5 +- 6 files changed, 243 insertions(+), 134 deletions(-) diff --git a/controller.lua b/controller.lua index e124b05..d86dc20 100644 --- a/controller.lua +++ b/controller.lua @@ -802,10 +802,8 @@ minetest.register_node("digtron:controller_unassembled", combine_defs(base_def, local player_name = clicker:get_player_name() local digtron_id = digtron.assemble(pos, player_name) if digtron_id then - local meta = minetest.get_meta(pos) - meta:set_string("digtron_id", digtron_id) - meta:mark_as_private("digtron_id") - get_context(digtron_id, player_name) + 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)) @@ -833,10 +831,12 @@ minetest.register_node("digtron:controller", combine_defs(base_def, { 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) @@ -863,6 +863,7 @@ minetest.register_node("digtron:controller", combine_defs(base_def, { 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 diff --git a/entities.lua b/entities.lua index b2b9819..b4a938d 100644 --- a/entities.lua +++ b/entities.lua @@ -161,7 +161,7 @@ end -- Used by unassembled builders digtron.update_builder_item = function(pos) local node = minetest.get_node(pos) - if minetest.get_node_group(node.name, "digtron") ~= 4 then + if minetest.get_item_group(node.name, "digtron") ~= 4 then return end local target_pos = vector.add(pos, minetest.facedir_to_dir(node.param2)) @@ -179,11 +179,11 @@ digtron.update_builder_items = function(digtron_id) local layout = digtron.get_layout(digtron_id) local root_pos = digtron.get_pos(digtron_id) - for hash, data in pairs(layout) do + for layout_node_id, data in pairs(layout) do local node = data.node - if minetest.get_node_group(node.name, "digtron") == 4 then + if minetest.get_item_group(node.name, "digtron") == 4 then local item = data.meta.fields.item - local pos = vector.add(minetest.get_position_from_hash(hash), root_pos) + 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 @@ -210,12 +210,12 @@ 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 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_node_group(node.name, "digtron") == 4 then + 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 diff --git a/functions.lua b/functions.lua index 221f254..a0cd131 100644 --- a/functions.lua +++ b/functions.lua @@ -4,21 +4,36 @@ local cache = {} local S = digtron.S - ---minetest.debug(dump(mod_meta:to_table())) - --- Wipes mod_meta ---for field, value in pairs(mod_meta:to_table().fields) do --- mod_meta:set_string(field, "") ---end +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(root_pos, punch_data, items_dropped) +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 = vector.add(minetest.get_position_from_hash(punch_data[1]), root_pos) + 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 @@ -195,7 +210,7 @@ local persist_sequence, retrieve_sequence = get_table_functions("sequence") ------------------------------------------------------------------------------------------------------- -- Layout creation helpers -digtron.duplicate = function(digtron_id) +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) @@ -218,6 +233,7 @@ digtron.duplicate = function(digtron_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 @@ -268,10 +284,10 @@ local retrieve_bounding_box = function(digtron_id) 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 hash, data in pairs(layout) do - update_bounding_box(bbox, minetest.get_position_from_hash(hash)) + for layout_node_id, data in pairs(layout) do + update_bounding_box(bbox, data.pos) end cache_bounding_box[digtron_id] = bbox return bbox @@ -283,14 +299,24 @@ 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 = {} - for hash, data in pairs(layout) do + + 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 layout[potential_adjacent] == nil then + if not all_digtron_node_hashes[potential_adjacent] then adjacent[potential_adjacent] = true end end @@ -300,19 +326,19 @@ local refresh_adjacent = function(digtron_id) -- Diggers if digtron_group >= 10 and digtron_group <= 13 then -- All diggers target the node directly in front of them - local dir_hashes = {} + local targets = {} local dir_hash = digtron.facedir_to_dir_hash(data.node.param2) local potential_target = hash + dir_hash -- pointed at this hash - if layout[potential_target] == nil then -- not pointed at another Digtron node - table.insert(dir_hashes, dir_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 layout[potential_target] == nil then -- not pointed at another Digtron node - table.insert(dir_hashes, dir_hash) + if not all_digtron_node_hashes[potential_target] then + table.insert(targets, minetest.get_position_from_hash(potential_target)) end end @@ -322,35 +348,40 @@ local refresh_adjacent = function(digtron_id) soft = true end - if #dir_hashes > 0 then - local fields = data.meta.fields - adjacent_to_diggers[hash] = { + 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, - dir_hashes = dir_hashes, + targets = targets, soft = soft, - } - end + }) + 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 layout[potential_target] == nil then + 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. - adjacent_to_builders[hash] = { + -- 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_hash = dir_hash, -- Record in table form, it'll be more convenient for use later - } + 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 @@ -423,7 +454,7 @@ local assemble = function(root_pos, player_name) for hash, node in pairs(digtron_nodes) do local pos = minetest.get_position_from_hash(hash) - local relative_hash = minetest.hash_node_position(vector.subtract(pos, root_pos)) + local relative_pos = vector.subtract(pos, root_pos) local current_meta if hash == root_hash then @@ -455,17 +486,26 @@ local assemble = function(root_pos, player_name) 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 - layout[relative_hash] = {meta = current_meta_table, node = node} + table.insert(layout, {meta = current_meta_table, node = node, pos = relative_pos}) local meta = minetest.get_meta(pos) - -- track this so that we can interact with individual node settings in the assembled digtron - meta:set_string("digtron_relative_hash", relative_hash) + 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) @@ -476,13 +516,14 @@ local assemble = function(root_pos, player_name) 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. - for hash, node in pairs(digtron_nodes) do + -- 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 hash == root_hash then + 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(minetest.get_position_from_hash(hash)) + node_meta = minetest.get_meta(node_pos) end local inv = node_meta:get_inventory() @@ -492,8 +533,11 @@ local assemble = function(root_pos, player_name) 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) @@ -503,30 +547,48 @@ local assemble = function(root_pos, player_name) 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, root_pos, hash, data, function_name) - local node_pos = vector.add(minetest.get_position_from_hash(hash), root_pos) +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") - - if data.node.name ~= node.name then - minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with one of ".. digtron_id .. "'s " - .. data.node.name .. "s at " .. minetest.pos_to_string(node_pos) .. " but the node at that location was of type " - .. node.name) + 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", "[Digtron] " .. function_name .. " tried interacting with ".. digtron_id .. "'s " - .. data.node.name .. " at " .. minetest.pos_to_string(node_pos) + 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", "[Digtron] " .. function_name .. " tried interacting with ".. digtron_id .. "'s " - .. data.node.name .. " at " .. minetest.pos_to_string(node_pos) + 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.") @@ -558,8 +620,8 @@ local disassemble = function(digtron_id, player_name) end -- Write metadata and inventory to in-world node at this location - for hash, data in pairs(layout) do - local node_pos, node, node_meta = get_valid_data(digtron_id, root_pos, hash, data, "disassemble") + 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() @@ -640,8 +702,8 @@ local remove_from_world = function(digtron_id, player_name) end local nodes_to_destroy = {} - for hash, data in pairs(layout) do - local node_pos = get_valid_data(digtron_id, root_pos, hash, data, "remove_from_world") + 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 @@ -664,8 +726,8 @@ local is_buildable_to = function(digtron_id, layout, root_pos, player_name, igno local ignore_hashes = {} if old_root_pos then - for hash, _ in pairs(old_layout) do - local old_hash = minetest.hash_node_position(vector.add(minetest.get_position_from_hash(hash), old_root_pos)) + 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 @@ -710,16 +772,20 @@ local build_to_world = function(digtron_id, layout, root_pos, player_name) layout = retrieve_layout(digtron_id) end local built_positions = {} - for hash, data in pairs(layout) do + 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(minetest.get_position_from_hash(hash), root_pos) + 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) @@ -754,7 +820,7 @@ 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 hash, data in pairs(layout) do + for layout_node_id, data in pairs(layout) do local duplicate_data = deep_copy(data) -- Facings local node_name = duplicate_data.node.name @@ -777,10 +843,9 @@ local rotate_layout = function(digtron_id, axis) end -- Position - local pos = minetest.get_position_from_hash(hash) - pos = digtron.rotate_pos(axis_hash, pos) - local new_hash = minetest.hash_node_position(pos) - rotated_layout[new_hash] = duplicate_data + local pos = data.pos + duplicate_data.pos = digtron.rotate_pos(axis_hash, pos) + rotated_layout[layout_node_id] = duplicate_data end return rotated_layout @@ -865,11 +930,11 @@ local predict_dig = function(digtron_id, player_name, controlling_coordinate) punches_thrown = {} end - for digger_hash, digger_data in pairs(retrieve_all_digger_targets(digtron_id)) do - for _, dir_hash in ipairs(digger_data.dir_hashes) do - local target_hash = digger_hash + dir_hash + 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_pos = vector.add(minetest.get_position_from_hash(target_hash), root_pos) local target_node = minetest.get_node(target_pos) local target_name = target_node.name local targetdef = minetest.registered_nodes[target_name] @@ -887,10 +952,7 @@ local predict_dig = function(digtron_id, player_name, controlling_coordinate) and (not digger_data.soft or is_soft_material(target_name)) then if punches_thrown then - -- storing digger_hash rather than converting it into a vector because - -- in most cases there won't be something to punch and that calculation can be skipped - -- convert to digger_pos by adding root_pos - table.insert(punches_thrown, {digger_hash, target_pos}) + 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 @@ -992,11 +1054,11 @@ local predict_build = function(digtron_id, root_pos, player_name, ignore_nodes, local built_nodes = {} local cost = 0 - for target_hash, builder_data in pairs(retrieve_all_builder_targets(digtron_id)) do - local dir_hash = builder_data.dir_hash + 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(minetest.get_position_from_hash(target_hash + i * dir_hash), root_pos) + 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 @@ -1189,12 +1251,12 @@ local execute_dig_move_build_cycle = function(digtron_id, player_name, dig_down) local items_dropped = {} if punches_thrown then for _, punch_data in ipairs(punches_thrown) do - damage_creatures(old_root_pos, punch_data, items_dropped) + damage_creatures(punch_data, items_dropped) end end -- Building new Digtron - digtron.build_to_world(digtron_id, layout, new_root_pos, player_name) + 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) @@ -1360,9 +1422,10 @@ local recover_digtron_id = function(root_pos) .."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) - for hash, data in pairs(layout) do + 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, root_pos, hash, data, "recover_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 @@ -1388,8 +1451,12 @@ digtron.get_inventory = retrieve_inventory digtron.set_sequence = persist_sequence digtron.get_sequence = retrieve_sequence --- Used by duplicator 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 diff --git a/init.lua b/init.lua index f7d2ab2..2d7f8a3 100644 --- a/init.lua +++ b/init.lua @@ -57,7 +57,6 @@ 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. diff --git a/nodes/node_builder.lua b/nodes/node_builder.lua index 95eb4d8..c8a4bc5 100644 --- a/nodes/node_builder.lua +++ b/nodes/node_builder.lua @@ -6,15 +6,13 @@ local S = digtron.S -- TODO make this global local player_interacting_with_builder_pos = {} -local get_formspec = function(pos) - local meta = minetest.get_meta(pos) - - local period = meta:get_int("period") +local get_formspec = function(meta_fields) + local period = tonumber(meta_fields.period) if period < 1 then period = 1 end - local offset = meta:get_int("offset") - local extrusion = meta:get_int("extrusion") - local facing = meta:get_int("facing") - local item_name = meta:get_string("item") + 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 .. "]".. @@ -87,22 +85,40 @@ local inv = minetest.create_detached_inventory("digtron:builder_item", { local meta = minetest.get_meta(pos) local digtron_id = meta:get_string("digtron_id") - if digtron_id ~= "" then - minetest.log("warning", "[Digtron] builder detached inventory had player " .. player_name - .. " attempt to set " .. item .. " at " .. minetest.pos_to_string(pos) .. - " but the builder node at that location was already assembled into " .. digtron_id) - return 0 + 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(pos)) - + + minetest.show_formspec(player_name, "digtron:builder", get_formspec(layout_fields)) return 0 end, allow_take = function(inv, listname, index, stack, player) @@ -123,17 +139,19 @@ local builder_on_rightclick = function(pos, node, clicker, itemstack, pointed_th local meta = minetest.get_meta(pos) local digtron_id = meta:get_string("digtron_id") - if digtron_id ~= "" then - minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=player_name}) - minetest.chat_send_player(player_name, S("This Digtron is active, interact with it via the controller node.")) - digtron.update_builder_items(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(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) @@ -150,47 +168,62 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields) end local meta = minetest.get_meta(pos) - - local item = meta:get_string("item") + 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:set_int("period", math.floor(period)) + meta_fields.period = math.floor(period) else - period = meta:get_int("period") + period = tonumber(meta_fields.period) or 1 end local offset = tonumber(fields.offset) if offset then - meta:set_int("offset", math.floor(offset)) + meta_fields.offset = math.floor(offset) else - offset = meta:get_int("offset") + 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:set_int("facing", math.floor(facing)) - -- wallmounted facings only run from 0-5 + meta_fields.facing = math.floor(facing) end else - meta:set_int("facing", math.floor(facing)) + meta_fields.facing = math.floor(facing) end else - facing = meta:get_int("facing") + 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:set_int("extrusion", math.floor(extrusion)) + meta_fields.extrusion = math.floor(extrusion) else - extrusion = meta:get_int("extrusion") + extrusion = tonumber(meta_fields.extrusion) or 1 end if fields.set then - --digtron.show_offset_markers(pos, offset, period) + --TODO digtron.show_offset_markers(pos, offset, period) end if fields.read then @@ -202,10 +235,9 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields) target_name = digtron.builder_read_item_substitutions[target_name] end if target_name ~= "air" and is_item_allowed(target_name) then - local meta = minetest.get_meta(pos) item = target_name - meta:set_string("item", item) - meta:set_int("facing", target_node.param2) + meta_fields.item = item + meta_fields.facing = target_node.param2 end end @@ -214,14 +246,21 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields) end local item_def = minetest.registered_items[item] - local item_desc = "Nothing" + local item_desc = S("Nothing") if item_def then item_desc = item_def.description end - meta:set_string("infotext", S("Builder for @1\nperiod @2, offset @3, extrusion @4", item_desc, period, offset, extrusion)) + 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(pos)) + minetest.show_formspec(player_name, "digtron:builder", get_formspec(meta_fields)) end) diff --git a/nodes/node_duplicator.lua b/nodes/node_duplicator.lua index 97690cb..2949cb2 100644 --- a/nodes/node_duplicator.lua +++ b/nodes/node_duplicator.lua @@ -1,5 +1,6 @@ local S = digtron.S +-- 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) @@ -9,9 +10,11 @@ local get_manifest = function(pos) local digtron_id = stack_meta:get_string("digtron_id") if digtron_id ~= "" then local layout = digtron.get_layout(digtron_id) - for hash, data in pairs(layout) do + 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]