diff --git a/README.txt b/README.txt index fe02ac4..f7217dd 100644 --- a/README.txt +++ b/README.txt @@ -34,12 +34,19 @@ Detailed module guide Control Module -------------- -Right-click on this module to make the digging machine go. The digging machine will go in the direction that the control module is oriented. +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. 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). If you're standing within the digging machine's volume, or in a node adjacent to it, you will be pulled along with the machine when it moves. +Automatic Control Module +-------------- + +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. + +The digging machine will go in the direction that the control module is oriented. + Pusher Module ------------- @@ -160,12 +167,18 @@ Builder heads: [mese fragment, core , mese fragment] [ , mese fragment, ] -Controller heads: +Controller: [ , mese crystal, ] [mese crystal, core , mese crystal] [ , mese crystal, ] +Automatic Controller: + +[mese crystal, mese crystal, mese crystal] +[mese crystal, core , mese crystal] +[mese crystal, mese crystal, mese crystal] + Inventory modules: [chest,] diff --git a/entities.lua b/entities.lua index 8ad4dcf..ae997af 100644 --- a/entities.lua +++ b/entities.lua @@ -66,6 +66,13 @@ minetest.register_entity("digtron:builder_item", { on_activate = function(self, staticdata) local props = self.object:get_properties() if staticdata ~= nil and staticdata ~= "" then + local pos = self.object:getpos() + local node = minetest.get_node(pos) + if minetest.get_node_group(node.name, "digtron") ~= 4 then + -- We were reactivated without a builder node on our location, self-destruct + self.object:remove() + return + end props.textures = {staticdata} self.object:set_properties(props) elseif digtron.create_builder_item ~= nil then diff --git a/node_controllers.lua b/node_controllers.lua index 8c06607..ff5f20b 100644 --- a/node_controllers.lua +++ b/node_controllers.lua @@ -1,21 +1,21 @@ -digtron.execute_cycle = function(pos, node, clicker) +-- returns newpos, status string +local execute_cycle = function(pos, clicker) 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 pos, nil - end - + local fuel_burning = meta:get_float("fuel_burning") -- get amount of burned fuel left over from last cycle + local status_text = string.format("Heat remaining in controller furnace: %d", fuel_burning) + local layout = digtron.get_all_digtron_neighbours(pos, clicker) if layout.all == nil then -- get_all_digtron_neighbours returns nil if the digtron array touches unloaded nodes, too dangerous to do anything in that situation. Abort. minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - return pos, "Digtron is adjacent to unloaded nodes." + return pos, "Digtron is adjacent to unloaded nodes.\n" .. status_text end if layout.traction * digtron.traction_factor < table.getn(layout.all) then -- digtrons can't fly minetest.sound_play("squeal", {gain=1.0, pos=pos}) - return pos, string.format("Digtron has %d nodes but only enough traction to move %d nodes.", table.getn(layout.all), layout.traction * digtron.traction_factor) + return pos, string.format("Digtron has %d nodes but only enough traction to move %d nodes.\n", table.getn(layout.all), layout.traction * digtron.traction_factor) + .. status_text end local facing = minetest.get_node(pos).param2 @@ -69,7 +69,7 @@ digtron.execute_cycle = function(pos, node, clicker) ) minetest.sound_play("squeal", {gain=1.0, pos=pos}) minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - return pos, "Digtron is obstructed." --Abort, don't dig and don't build. + return pos, "Digtron is obstructed.\n" .. status_text --Abort, don't dig and don't build. end ---------------------------------------------------------------------------------------------------------------------- @@ -103,7 +103,6 @@ digtron.execute_cycle = function(pos, node, clicker) end end - local fuel_burning = meta:get_float("fuel_burning") -- get amount of burned fuel left over from last cycle local test_fuel_needed = test_build_fuel_cost + digging_fuel_cost - fuel_burning local test_fuel_burned = 0 if test_fuel_needed > 0 then @@ -117,7 +116,7 @@ digtron.execute_cycle = function(pos, node, clicker) if test_fuel_needed > fuel_burning + test_fuel_burned then minetest.sound_play("buzzer", {gain=0.5, pos=pos}) - return pos, "Digtron needs more fuel" -- abort, don't dig and don't build. + return pos, "Digtron needs more fuel." -- abort, don't dig and don't build. end if not can_build then @@ -130,13 +129,13 @@ digtron.execute_cycle = function(pos, node, clicker) local return_string = nil if test_build_return_code == 3 then minetest.sound_play("honk", {gain=0.5, pos=pos}) -- A builder is not configured - return_string = "Digtron connected to at least one builder node that hasn't had an output material assigned." + return_string = "Digtron connected to at least one builder node that hasn't had an output material assigned.\n" elseif test_build_return_code == 2 then minetest.sound_play("dingding", {gain=1.0, pos=pos}) -- Insufficient inventory - return_string = string.format("Digtron has insufficient materials in inventory to execute all build operations.\nNeeded: %s", + return_string = string.format("Digtron has insufficient materials in inventory to execute all build operations.\nNeeded: %s\n", test_build_return_item:get_name()) end - return pos, return_string --Abort, don't dig and don't build. + return pos, return_string .. status_text --Abort, don't dig and don't build. end ---------------------------------------------------------------------------------------------------------------------- @@ -166,15 +165,7 @@ digtron.execute_cycle = function(pos, node, clicker) if move_player then clicker:moveto(digtron.find_new_pos(player_pos, facing), true) end - - -- Start the delay before digtron can run again. Do this after moving the array or pos will be wrong. - minetest.get_meta(pos):set_string("waiting", "true") - minetest.after(digtron.cycle_time, - function (pos) - minetest.get_meta(pos):set_string("waiting", nil) - end, pos - ) - + local building_fuel_cost = 0 local strange_failure = false -- execute_build on all digtron components that have one @@ -273,14 +264,142 @@ minetest.register_node("digtron:controller", { end, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - newpos, status = digtron.execute_cycle(pos, node, clicker) + 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 + + newpos, status = execute_cycle(pos, clicker) + + meta = minetest.get_meta(newpos) if status ~= nil then - local meta = minetest.get_meta(newpos) meta:set_string("infotext", status) end + + -- Start the delay before digtron can run again. Do this after moving the array or pos will be wrong. + minetest.get_meta(newpos):set_string("waiting", "true") + minetest.after(digtron.cycle_time, + function (pos) + minetest.get_meta(pos):set_string("waiting", nil) + end, newpos + ) end, }) +--------------------------------------------------------------------------------------------------------------- + +local auto_formspec = "size[4.5,1]" .. + default.gui_bg .. + default.gui_bg_img .. + default.gui_slots .. + "field[0.5,0.8;1,0.1;offset;Cycles;${offset}]" .. + "tooltip[offset;When triggered, this controller will try to run for the given number of cycles. The cycle count will decrement as it runs, so if it gets halted by a problem you can fix the problem and restart.]" .. + "field[1.5,0.8;1,0.1;period;Period;${period}]" .. + "tooltip[period;Number of seconds to wait between each cycle]" .. + "button_exit[2.2,0.5;1,0.1;set;Set]" .. + "tooltip[set;Saves the cycle setting without starting the controller running]" .. + "button_exit[3.2,0.5;1,0.1;execute;Set &\nExecute]" .. + "tooltip[execute;Begins executing the given number of cycles]" + +-- Needed to make this global so that it could recurse into minetest.after +digtron.auto_cycle = function(pos) + 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 newpos, status = execute_cycle(pos, player) + + local cycle = 0 + if vector.equals(pos, newpos) then + cycle = meta:get_int("offset") + status = status .. string.format("\nCycles remaining: %d\nHalted!", cycle) + meta:set_string("infotext", status) + meta:set_string("formspec", auto_formspec) + return + end + + meta = minetest.get_meta(newpos) + cycle = meta:get_int("offset") - 1 + meta:set_int("offset", cycle) + status = status .. string.format("\nCycles remaining: %d", cycle) + meta:set_string("infotext", status) + + if cycle > 0 then + minetest.after(math.max(digtron.cycle_time, meta:get_int("period")), digtron.auto_cycle, newpos) + else + meta:set_string("formspec", auto_formspec) + end +end + +-- 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:auto_controller", { + description = "Digtron Automatic Control Unit", + groups = {cracky = 3, oddly_breakable_by_hand = 3, digtron = 1}, + drop = "digtron:auto_controller", + sounds = default.node_sound_metal_defaults(), + paramtype2= "facedir", + -- Aims in the +Z direction by default + tiles = { + "digtron_plate.png^[transformR90^[colorize:#88000030", + "digtron_plate.png^[transformR270^[colorize:#88000030", + "digtron_plate.png^[colorize:#88000030", + "digtron_plate.png^[transformR180^[colorize:#88000030", + "digtron_plate.png^[colorize:#88000030", + "digtron_control.png^[colorize:#88000030", + }, + + drawtype = "nodebox", + paramtype = "light", + 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", "Heat remaining in controller furnace: 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", 1) + meta:set_int("offset", 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) + + if period and period > 0 then + meta:set_int("period", math.floor(period)) + end + + if offset and offset >= 0 then + meta:set_int("offset", math.floor(offset)) + if sender:is_player() and offset > 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) + digtron.auto_cycle(pos) + end + end + 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") .. "\nInterrupted!") + 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", { diff --git a/node_diggers.lua b/node_diggers.lua index b82e765..0de5e2e 100644 --- a/node_diggers.lua +++ b/node_diggers.lua @@ -27,6 +27,18 @@ local intermittent_on_construct = function(pos) meta:set_int("offset", 0) end +local intermittent_on_receive_fields = function(pos, formname, fields, sender) + 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 +end, + -- Digs out nodes that are "in front" of the digger head. minetest.register_node("digtron:digger", { description = "Digger Head", @@ -108,17 +120,7 @@ minetest.register_node("digtron:intermittent_digger", { on_construct = intermittent_on_construct, - on_receive_fields = function(pos, formname, fields, sender) - 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(tonumber(fields.period))) - end - if offset then - meta:set_int("offset", math.floor(tonumber(fields.offset))) - end - end, + on_receive_fields = intermittent_on_receive_fields, -- returns fuel_cost, item_produced execute_dig = function(pos, protected_nodes, nodes_dug, controlling_coordinate) @@ -225,7 +227,9 @@ minetest.register_node("digtron:intermittent_soft_digger", { }, on_construct = intermittent_on_construct, - + + on_receive_fields = intermittent_on_receive_fields, + execute_dig = function(pos, protected_nodes, nodes_dug, controlling_coordinate) local facing = minetest.get_node(pos).param2 local digpos = digtron.find_new_pos(pos, facing) diff --git a/recipes.lua b/recipes.lua index 1d0955b..6f5fead 100644 --- a/recipes.lua +++ b/recipes.lua @@ -21,6 +21,15 @@ minetest.register_craft({ } }) +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 = { diff --git a/util.lua b/util.lua index b80fd05..73c72dc 100644 --- a/util.lua +++ b/util.lua @@ -99,6 +99,8 @@ digtron.move_node = function(pos, newpos) newinv:set_list("main", list) newmeta:set_string("formspec", oldformspec) + newmeta:set_string("triggering_player", oldmeta:get_string("triggering_player")) -- for auto-controllers + newmeta:set_int("offset", oldmeta:get_int("offset")) newmeta:set_int("period", oldmeta:get_int("period")) newmeta:set_int("build_facing", oldmeta:get_int("build_facing"))