2017-02-05 07:08:38 +01:00
-- internationalization boilerplate
local MP = minetest.get_modpath ( minetest.get_current_modname ( ) )
local S , NS = dofile ( MP .. " /intllib.lua " )
2017-01-07 17:52:39 +01:00
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
2017-01-09 05:34:27 +01:00
--Performs various tests on a layout to play warning noises and see if Digtron can move at all.
2017-01-22 19:06:37 +01:00
local function neighbour_test ( layout , status_text , dir )
2019-01-14 00:02:41 +01:00
if layout.ignore_touching == true then
-- if the digtron array touches unloaded nodes, too dangerous to do anything in that situation. Abort.
2017-01-09 05:34:27 +01:00
minetest.sound_play ( " buzzer " , { gain = 0.25 , pos = layout.controller } )
2017-02-05 07:08:38 +01:00
return S ( " Digtron is adjacent to unloaded nodes. " ) .. " \n " .. status_text , 1
2017-01-09 05:34:27 +01:00
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
2017-09-12 08:20:52 +02:00
if dir and dir.y ~= - 1 and layout.traction * digtron.config . traction_factor < table.getn ( layout.all ) then
2017-01-22 19:06:37 +01:00
-- digtrons can't fly, though they can fall
2017-01-09 05:34:27 +01:00
minetest.sound_play ( " squeal " , { gain = 1.0 , pos = layout.controller } )
2017-09-20 07:44:59 +02:00
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 )
2017-01-09 05:34:27 +01:00
.. status_text , 2
end
return status_text , 0
end
2017-01-09 06:19:49 +01:00
-- Checks if a player is within a layout's extents.
local function move_player_test ( layout , player )
local player_pos = player : getpos ( )
2019-01-13 11:02:02 +01:00
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
2017-01-09 06:19:49 +01:00
return true
end
return false
end
2019-01-05 06:06:12 +01:00
local node_inventory_table = { type = " node " } -- a reusable parameter for get_inventory calls, set the pos parameter before using.
2017-01-28 03:40:10 +01:00
local function test_stop_block ( pos , items )
2019-01-05 06:06:12 +01:00
node_inventory_table.pos = pos
local inv = minetest.get_inventory ( node_inventory_table )
2017-01-28 03:40:10 +01:00
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
2017-01-08 01:00:15 +01:00
-- 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
2017-01-09 05:34:27 +01:00
digtron.execute_dig_cycle = function ( pos , clicker )
2017-01-07 17:52:39 +01:00
local meta = minetest.get_meta ( pos )
2017-01-22 19:06:37 +01:00
local facing = minetest.get_node ( pos ) . param2
local dir = minetest.facedir_to_dir ( facing )
2017-01-07 17:52:39 +01:00
local fuel_burning = meta : get_float ( " fuel_burning " ) -- get amount of burned fuel left over from last cycle
2017-10-13 06:53:30 +02:00
local status_text = S ( " Heat remaining in controller furnace: @1 " , math.floor ( math.max ( 0 , fuel_burning ) ) )
2017-10-10 21:02:35 +02:00
local exhaust = meta : get_int ( " on_coal " )
2017-01-07 17:52:39 +01:00
2017-01-10 08:29:09 +01:00
local layout = DigtronLayout.create ( pos , clicker )
2017-01-09 05:34:27 +01:00
2017-01-22 19:06:37 +01:00
local status_text , return_code = neighbour_test ( layout , status_text , dir )
2017-01-09 05:34:27 +01:00
if return_code ~= 0 then
return pos , status_text , return_code
2017-01-07 17:52:39 +01:00
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.
2019-01-05 06:06:12 +01:00
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
2017-01-07 17:52:39 +01:00
end
2019-01-05 06:06:12 +01:00
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 ) )
2017-01-07 17:52:39 +01:00
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
2017-01-22 19:06:37 +01:00
local newpos = vector.add ( location.pos , dir )
2017-01-09 07:52:36 +01:00
if not digtron.can_move_to ( newpos , layout.protected , layout.nodes_dug ) then
2017-01-07 17:52:39 +01:00
can_move = false
end
end
2017-01-28 03:40:10 +01:00
if test_stop_block ( pos , items_dropped ) then
can_move = false
end
2017-01-07 17:52:39 +01:00
if not can_move then
2017-09-12 08:20:52 +02:00
-- mark this node as waiting, will clear this flag in digtron.config.cycle_time seconds
2017-01-07 17:52:39 +01:00
minetest.get_meta ( pos ) : set_string ( " waiting " , " true " )
2017-09-12 08:20:52 +02:00
minetest.get_node_timer ( pos ) : start ( digtron.config . cycle_time )
2017-01-07 17:52:39 +01:00
minetest.sound_play ( " squeal " , { gain = 1.0 , pos = pos } )
minetest.sound_play ( " buzzer " , { gain = 0.5 , pos = pos } )
2017-02-05 07:08:38 +01:00
return pos , S ( " Digtron is obstructed. " ) .. " \n " .. status_text , 3 --Abort, don't dig and don't build.
2017-01-07 17:52:39 +01:00
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
2017-09-11 02:58:25 +02:00
local test_build_return_items = nil
local failed_to_find = nil
2017-01-07 17:52:39 +01:00
local test_items = { }
local test_fuel_items = { }
2019-01-05 06:06:12 +01:00
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 ) )
2017-01-07 17:52:39 +01:00
end
end
end
local test_fuel_needed = test_build_fuel_cost + digging_fuel_cost - fuel_burning
local test_fuel_burned = 0
2017-10-12 11:06:24 +02:00
local power_from_cables = 0
if minetest.get_modpath ( " technic " ) then
2019-01-05 06:06:12 +01:00
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
2017-10-12 11:06:24 +02:00
end
2019-01-05 06:06:12 +01:00
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
2017-10-12 11:06:24 +02:00
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
2017-10-10 21:02:35 +02:00
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
2017-01-07 17:52:39 +01:00
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 } )
2017-02-05 07:08:38 +01:00
return pos , S ( " Digtron needs more fuel. " ) , 4 -- abort, don't dig and don't build.
2017-01-07 17:52:39 +01:00
end
if not can_build then
minetest.get_meta ( pos ) : set_string ( " waiting " , " true " )
2017-09-12 08:20:52 +02:00
minetest.get_node_timer ( pos ) : start ( digtron.config . cycle_time )
2017-01-07 17:52:39 +01:00
local return_string = nil
2017-01-08 01:00:15 +01:00
local return_code = 5
2017-01-07 17:52:39 +01:00
if test_build_return_code == 3 then
minetest.sound_play ( " honk " , { gain = 0.5 , pos = pos } ) -- A builder is not configured
2017-02-05 07:08:38 +01:00
return_string = S ( " Digtron connected to at least one builder with no output material assigned. " ) .. " \n "
2017-01-08 01:00:15 +01:00
return_code = 6
2017-01-07 17:52:39 +01:00
elseif test_build_return_code == 2 then
minetest.sound_play ( " dingding " , { gain = 1.0 , pos = pos } ) -- Insufficient inventory
2017-09-20 07:44:59 +02:00
return_string = S ( " Digtron has insufficient building materials. Needed: @1 " , failed_to_find : get_name ( ) ) .. " \n "
2017-01-08 01:00:15 +01:00
return_code = 7
2017-01-07 17:52:39 +01:00
end
2017-01-08 01:00:15 +01:00
return pos , return_string .. status_text , return_code --Abort, don't dig and don't build.
2017-01-07 17:52:39 +01:00
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.
2017-01-09 06:19:49 +01:00
local move_player = move_player_test ( layout , clicker )
2017-01-07 17:52:39 +01:00
-- damage the weak flesh
2019-01-05 06:06:12 +01:00
if digtron.config . damage_hp > 0 and layout.diggers ~= nil then
2017-01-07 17:52:39 +01:00
for k , location in pairs ( layout.diggers ) do
2017-01-09 07:52:36 +01:00
local target = minetest.get_node ( location.pos )
2017-01-07 17:52:39 +01:00
local targetdef = minetest.registered_nodes [ target.name ]
if targetdef.damage_creatures ~= nil then
2019-01-05 06:06:12 +01:00
targetdef.damage_creatures ( clicker , location.pos , controlling_coordinate , items_dropped )
2017-01-07 17:52:39 +01:00
end
end
end
--move the array
2017-01-22 19:06:37 +01:00
layout : move_layout_image ( dir )
2018-01-07 21:38:25 +01:00
if not layout : write_layout_image ( clicker ) then
return pos , " unrecoverable write_layout_image error " , 1
end
2017-01-07 17:52:39 +01:00
local oldpos = { x = pos.x , y = pos.y , z = pos.z }
2017-01-22 19:06:37 +01:00
pos = vector.add ( pos , dir )
2017-01-07 17:52:39 +01:00
meta = minetest.get_meta ( pos )
if move_player then
2017-01-22 19:06:37 +01:00
clicker : moveto ( vector.add ( dir , clicker : getpos ( ) ) , true )
2017-01-07 17:52:39 +01:00
end
2017-01-12 08:50:17 +01:00
-- 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
2018-12-24 03:06:49 +01:00
digtron.award_item_dug ( items_dropped , clicker ) -- Achievements mod hook
2017-01-09 07:52:36 +01:00
2017-01-07 17:52:39 +01:00
local building_fuel_cost = 0
local strange_failure = false
-- execute_build on all digtron components that have one
2019-01-05 06:06:12 +01:00
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 ) )
2017-01-07 17:52:39 +01:00
end
end
end
2017-10-22 06:29:18 +02:00
2019-01-05 06:06:12 +01:00
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
2019-01-09 08:20:02 +01:00
targetdef.execute_eject ( location.pos , target , clicker , layout )
2019-01-05 06:06:12 +01:00
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
2017-10-22 06:29:18 +02:00
end
end
2017-01-07 17:52:39 +01:00
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 } )
2017-02-05 07:08:38 +01:00
status_text = S ( " Digtron unexpectedly failed to execute one or more build operations, likely due to an inventory error. " ) .. " \n "
2017-01-07 17:52:39 +01:00
end
2017-10-12 11:06:24 +02:00
local total_fuel_cost = math.max ( digging_fuel_cost + building_fuel_cost - power_from_cables , 0 )
2017-10-10 21:02:35 +02:00
-- actually burn the fuel needed
2017-10-12 11:06:24 +02:00
fuel_burning = fuel_burning - total_fuel_cost
2017-10-11 04:55:29 +02:00
if digtron.config . particle_effects and exhaust == 1 then
2017-10-12 11:06:24 +02:00
table.insert ( particle_systems , burn_smoke ( pos , total_fuel_cost ) )
2017-01-07 17:52:39 +01:00
end
if fuel_burning < 0 then
2017-10-10 21:02:35 +02:00
-- 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
2017-01-07 17:52:39 +01:00
end
2017-10-10 21:02:35 +02:00
2017-01-07 17:52:39 +01:00
meta : set_float ( " fuel_burning " , fuel_burning )
2017-10-10 21:02:35 +02:00
meta : set_int ( " on_coal " , exhaust )
2017-10-13 06:53:30 +02:00
status_text = status_text .. S ( " Heat remaining in controller furnace: @1 " , math.floor ( math.max ( 0 , fuel_burning ) ) )
2017-01-07 17:52:39 +01:00
-- 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.
2017-01-09 07:52:36 +01:00
local node_to_dig , whether_to_dig = layout.nodes_dug : pop ( )
2017-01-07 17:52:39 +01:00
while node_to_dig ~= nil do
if whether_to_dig == true then
2017-09-20 07:44:59 +02:00
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 ) )
2017-01-07 17:52:39 +01:00
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 } )
2017-01-09 07:52:36 +01:00
node_to_dig , whether_to_dig = layout.nodes_dug : pop ( )
2017-01-07 17:52:39 +01:00
end
2017-01-08 01:00:15 +01:00
return pos , status_text , 0
2017-01-07 17:52:39 +01:00
end
2017-01-09 05:34:27 +01:00
-- 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 )
2017-01-10 08:29:09 +01:00
local layout = DigtronLayout.create ( pos , clicker )
2017-01-09 05:34:27 +01:00
local status_text = " "
2017-01-22 19:06:37 +01:00
local status_text , return_code = neighbour_test ( layout , status_text , nil ) -- skip traction check for pusher by passing nil for direction
2017-01-09 05:34:27 +01:00
if return_code ~= 0 then
return pos , status_text , return_code
end
local facing = minetest.get_node ( pos ) . param2
2017-01-22 19:06:37 +01:00
local dir = minetest.facedir_to_dir ( facing )
2017-01-09 05:34:27 +01:00
local controlling_coordinate = digtron.get_controlling_coordinate ( pos , facing )
2017-01-09 07:52:36 +01:00
-- 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
2017-01-22 19:06:37 +01:00
layout : move_layout_image ( dir )
2017-01-15 00:58:37 +01:00
if not layout : can_write_layout_image ( ) then
2017-09-12 08:20:52 +02:00
-- mark this node as waiting, will clear this flag in digtron.config.cycle_time seconds
2017-01-09 05:34:27 +01:00
minetest.get_meta ( pos ) : set_string ( " waiting " , " true " )
2017-09-12 08:20:52 +02:00
minetest.get_node_timer ( pos ) : start ( digtron.config . cycle_time )
2017-01-09 05:34:27 +01:00
minetest.sound_play ( " squeal " , { gain = 1.0 , pos = pos } )
minetest.sound_play ( " buzzer " , { gain = 0.5 , pos = pos } )
2017-02-05 07:08:38 +01:00
return pos , S ( " Digtron is obstructed. " ) .. " \n " .. status_text , 3 --Abort, don't dig and don't build.
2017-01-09 05:34:27 +01:00
end
minetest.sound_play ( " truck " , { gain = 1.0 , pos = pos } )
--move the array
2018-01-07 21:38:25 +01:00
if not layout : write_layout_image ( clicker ) then
return pos , " unrecoverable write_layout_image error " , 1
end
2017-01-22 19:06:37 +01:00
pos = vector.add ( pos , dir )
2017-01-09 05:34:27 +01:00
if move_player then
2017-01-22 19:06:37 +01:00
clicker : moveto ( vector.add ( clicker : getpos ( ) , dir ) , true )
2017-01-09 05:34:27 +01:00
end
return pos , " " , 0
2017-01-15 00:58:37 +01:00
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 )
2017-01-22 19:06:37 +01:00
local facing = minetest.get_node ( pos ) . param2
local dir = digtron.facedir_to_down_dir ( facing )
2017-01-15 00:58:37 +01:00
local fuel_burning = meta : get_float ( " fuel_burning " ) -- get amount of burned fuel left over from last cycle
2017-10-13 06:53:30 +02:00
local status_text = S ( " Heat remaining in controller furnace: @1 " , math.floor ( math.max ( 0 , fuel_burning ) ) )
2017-10-10 21:02:35 +02:00
local exhaust = meta : get_int ( " on_coal " )
2017-01-15 00:58:37 +01:00
local layout = DigtronLayout.create ( pos , clicker )
2017-01-22 19:06:37 +01:00
local status_text , return_code = neighbour_test ( layout , status_text , dir )
2017-01-15 00:58:37 +01:00
if return_code ~= 0 then
return pos , status_text , return_code
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.
2019-01-05 06:06:12 +01:00
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
2017-01-15 00:58:37 +01:00
end
2019-01-05 06:06:12 +01:00
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 ) )
2017-01-15 00:58:37 +01:00
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
2017-01-22 19:06:37 +01:00
local newpos = vector.add ( location.pos , dir )
2017-01-15 00:58:37 +01:00
if not digtron.can_move_to ( newpos , layout.protected , layout.nodes_dug ) then
can_move = false
end
end
2017-01-28 03:40:10 +01:00
if test_stop_block ( pos , items_dropped ) then
can_move = false
end
2017-01-15 00:58:37 +01:00
if not can_move then
2017-09-12 08:20:52 +02:00
-- mark this node as waiting, will clear this flag in digtron.config.cycle_time seconds
2017-01-15 00:58:37 +01:00
minetest.get_meta ( pos ) : set_string ( " waiting " , " true " )
2017-09-12 08:20:52 +02:00
minetest.get_node_timer ( pos ) : start ( digtron.config . cycle_time )
2017-01-15 00:58:37 +01:00
minetest.sound_play ( " squeal " , { gain = 1.0 , pos = pos } )
minetest.sound_play ( " buzzer " , { gain = 0.5 , pos = pos } )
2017-02-05 07:08:38 +01:00
return pos , S ( " Digtron is obstructed. " ) .. " \n " .. status_text , 3 --Abort, don't dig and don't build.
2017-01-15 00:58:37 +01:00
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
2019-01-05 06:06:12 +01:00
if digtron.config . damage_hp > 0 and layout.diggers ~= nil then
2017-01-15 00:58:37 +01:00
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
2019-03-17 10:32:35 +01:00
targetdef.damage_creatures ( clicker , location.pos , controlling_coordinate , items_dropped )
2017-01-15 00:58:37 +01:00
end
end
end
--move the array
layout : move_layout_image ( digtron.facedir_to_down_dir ( facing ) )
2018-01-07 21:38:25 +01:00
if not layout : write_layout_image ( clicker ) then
return pos , " unrecoverable write_layout_image error " , 1
end
2017-01-15 00:58:37 +01:00
local oldpos = { x = pos.x , y = pos.y , z = pos.z }
2017-01-22 19:06:37 +01:00
pos = vector.add ( pos , dir )
2017-01-15 00:58:37 +01:00
meta = minetest.get_meta ( pos )
if move_player then
2017-01-22 19:06:37 +01:00
clicker : moveto ( vector.add ( clicker : getpos ( ) , dir ) , true )
2017-01-15 00:58:37 +01:00
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
2018-12-24 03:06:49 +01:00
digtron.award_item_dug ( items_dropped , clicker ) -- Achievements mod hook
2017-01-19 07:42:36 +01:00
2017-01-15 00:58:37 +01:00
local status_text = " "
2017-10-10 21:02:35 +02:00
-- actually burn the fuel needed
2017-01-15 00:58:37 +01:00
fuel_burning = fuel_burning - digging_fuel_cost
2017-10-11 04:55:29 +02:00
if digtron.config . particle_effects and exhaust == 1 then
2017-01-15 00:58:37 +01:00
table.insert ( particle_systems , burn_smoke ( pos , digging_fuel_cost ) )
end
if fuel_burning < 0 then
2017-10-10 21:02:35 +02:00
-- 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
2017-01-15 00:58:37 +01:00
end
2017-10-10 21:02:35 +02:00
2017-01-15 00:58:37 +01:00
meta : set_float ( " fuel_burning " , fuel_burning )
2017-10-10 21:02:35 +02:00
meta : set_int ( " on_coal " , exhaust )
2017-10-13 06:53:30 +02:00
status_text = status_text .. S ( " Heat remaining in controller furnace: @1 " , math.floor ( math.max ( 0 , fuel_burning ) ) )
2017-01-15 00:58:37 +01:00
-- 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
2017-09-20 07:44:59 +02:00
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 ) )
2017-01-15 00:58:37 +01:00
minetest.remove_node ( node_to_dig )
end
node_to_dig , whether_to_dig = layout.nodes_dug : pop ( )
end
return pos , status_text , 0
2018-12-14 08:48:41 +01:00
end