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 " )
2016-12-31 07:38:18 +01:00
-- Note: builders go in group 4 and have both test_build and execute_build methods.
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-09-19 09:05:55 +02:00
local displace_due_to_help_button = 1.0
2017-09-11 07:24:09 +02:00
if minetest.get_modpath ( " doc " ) then
2017-09-19 09:05:55 +02:00
displace_due_to_help_button = 0.0
end
2017-10-22 06:29:18 +02:00
local builder_formspec_string =
2017-09-19 09:05:55 +02:00
" size[8,5.2] " ..
2017-02-02 04:32:58 +01:00
default.gui_bg ..
default.gui_bg_img ..
default.gui_slots ..
2017-09-19 09:05:55 +02:00
" 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}] " ..
2017-09-12 08:20:52 +02:00
" tooltip[extrusion; " .. S ( " 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. " , digtron.config . maximum_extrusion ) .. " ] " ..
2017-09-19 09:05:55 +02:00
" field[ " .. tostring ( displace_due_to_help_button + 2.3 ) .. " ,0.8;1,0.1;period; " .. S ( " Periodicity " ) .. " ;${period}] " ..
2017-02-05 07:08:38 +01:00
" tooltip[period; " .. S ( " 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. " ) .. " ] " ..
2017-09-19 09:05:55 +02:00
" field[ " .. tostring ( displace_due_to_help_button + 3.3 ) .. " ,0.8;1,0.1;offset; " .. S ( " Offset " ) .. " ;${offset}] " ..
2017-02-05 07:08:38 +01:00
" tooltip[offset; " .. S ( " 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. " ) .. " ] " ..
2017-09-19 09:05:55 +02:00
" button_exit[ " .. tostring ( displace_due_to_help_button + 4.0 ) .. " ,0.5;1,0.1;set; " .. S ( " Save & \n Show " ) .. " ] " ..
2017-02-05 07:08:38 +01:00
" tooltip[set; " .. S ( " Saves settings " ) .. " ] " ..
2017-09-19 09:05:55 +02:00
" field[ " .. tostring ( displace_due_to_help_button + 5.3 ) .. " ,0.8;1,0.1;build_facing; " .. S ( " Facing " ) .. " ;${build_facing}] " ..
2017-02-05 07:08:38 +01:00
" tooltip[build_facing; " .. S ( " 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. " ) .. " ] " ..
2017-09-19 09:05:55 +02:00
" button_exit[ " .. tostring ( displace_due_to_help_button + 6.0 ) .. " ,0.5;1,0.1;read; " .. S ( " Read & \n Save " ) .. " ] " ..
2017-02-05 07:08:38 +01:00
" tooltip[read; " .. S ( " Reads the facing of the block currently in the build location, \n then saves all settings. " ) .. " ] " ..
2017-02-02 04:32:58 +01:00
" 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] "
2017-09-19 09:05:55 +02:00
if minetest.get_modpath ( " doc " ) then
2017-10-22 06:29:18 +02:00
builder_formspec_string = builder_formspec_string ..
2017-09-19 09:05:55 +02:00
" button_exit[7.0,0.5;1,0.1;help; " .. S ( " Help " ) .. " ] " ..
" tooltip[help; " .. S ( " Show documentation about this block " ) .. " ] "
2017-09-11 07:24:09 +02:00
end
2017-10-22 06:29:18 +02:00
local builder_formspec = function ( pos , meta )
2019-01-02 07:31:51 +01:00
local nodemeta = " nodemeta: " .. pos.x .. " , " .. pos.y .. " , " .. pos.z
2017-10-22 06:29:18 +02:00
return builder_formspec_string
2019-01-02 07:31:51 +01:00
: 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 )
2017-10-22 06:29:18 +02:00
end
2019-01-02 07:31:51 +01:00
local builder_on_rightclick = function ( pos , node , clicker , itemstack , pointed_thing )
2019-01-09 08:20:02 +01:00
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
2019-01-02 07:31:51 +01:00
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
2017-10-22 06:29:18 +02:00
2019-01-02 07:31:51 +01:00
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 )
2016-12-31 07:38:18 +01:00
-- Builds objects in the targeted node. This is a complicated beastie.
minetest.register_node ( " digtron:builder " , {
2017-02-05 07:08:38 +01:00
description = S ( " Digtron Builder Module " ) ,
2017-01-18 03:57:20 +01:00
_doc_items_longdesc = digtron.doc . builder_longdesc ,
_doc_items_usagehelp = digtron.doc . builder_usagehelp ,
2017-01-03 04:07:15 +01:00
groups = { cracky = 3 , oddly_breakable_by_hand = 3 , digtron = 4 } ,
2016-12-31 07:38:18 +01:00
drop = " digtron:builder " ,
2017-01-04 07:56:34 +01:00
sounds = digtron.metal_sounds ,
2017-01-06 07:52:09 +01:00
paramtype = " light " ,
2017-01-04 03:43:32 +01:00
paramtype2 = " facedir " ,
2017-01-06 07:52:09 +01:00
is_ground_content = false ,
2016-12-31 07:38:18 +01:00
tiles = {
" digtron_plate.png^[transformR90 " ,
" digtron_plate.png^[transformR270 " ,
" digtron_plate.png " ,
" digtron_plate.png^[transformR180 " ,
2017-01-11 06:59:11 +01:00
" digtron_plate.png^digtron_builder.png " ,
2016-12-31 07:38:18 +01:00
" 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 )
2017-01-04 03:43:32 +01:00
local meta = minetest.get_meta ( pos )
2017-01-02 01:12:32 +01:00
meta : set_int ( " period " , 1 )
meta : set_int ( " offset " , 0 )
meta : set_int ( " build_facing " , 0 )
2017-09-11 02:58:25 +02:00
meta : set_int ( " extrusion " , 1 )
2016-12-31 07:38:18 +01:00
local inv = meta : get_inventory ( )
inv : set_size ( " main " , 1 )
end ,
2019-01-02 07:31:51 +01:00
on_rightclick = builder_on_rightclick ,
2017-01-03 02:50:03 +01:00
on_destruct = function ( pos )
digtron.remove_builder_item ( pos )
2016-12-31 07:38:18 +01:00
end ,
2017-01-08 09:23:10 +01:00
after_place_node = function ( pos )
digtron.update_builder_item ( pos )
end ,
2016-12-31 07:38:18 +01:00
2017-01-05 03:23:21 +01:00
allow_metadata_inventory_put = function ( pos , listname , index , stack , player )
2017-11-17 00:53:01 +01:00
local stack_name = stack : get_name ( )
if minetest.get_item_group ( stack_name , " digtron " ) ~= 0 then
2017-01-21 08:55:11 +01:00
return 0 -- don't allow builders to be set to build Digtron nodes, they'll just clog the output.
end
2017-11-17 00:53:01 +01:00
2019-01-02 07:31:51 +01:00
local stack_def = minetest.registered_nodes [ stack_name ]
if not stack_def and not digtron.whitelisted_on_place ( stack_name ) then
2017-11-17 00:53:01 +01:00
return 0 -- don't allow craft items unless their on_place is whitelisted.
end
2019-01-05 06:06:12 +01:00
node_inventory_table.pos = pos
local inv = minetest.get_inventory ( node_inventory_table )
2017-01-05 03:23:21 +01:00
inv : set_stack ( listname , index , stack : take_item ( 1 ) )
2019-01-02 07:31:51 +01:00
-- 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 )
2019-01-03 04:36:21 +01:00
if stack_def ~= nil and stack_def.paramtype2 == " wallmounted " and tonumber ( meta : get_int ( " build_facing " ) ) > 5 then
2019-01-02 07:31:51 +01:00
meta : set_int ( " build_facing " , 0 )
end
2017-01-05 03:23:21 +01:00
return 0
end ,
allow_metadata_inventory_take = function ( pos , listname , index , stack , player )
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
inv : set_stack ( listname , index , ItemStack ( " " ) )
2017-01-05 03:23:21 +01:00
return 0
end ,
2016-12-31 07:38:18 +01:00
-- "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."
2017-01-01 08:53:40 +01:00
--return code and accompanying value:
2017-09-11 02:58:25 +02:00
-- 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
2016-12-31 07:38:18 +01:00
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 )
2017-01-02 01:12:32 +01:00
if ( buildpos [ controlling_coordinate ] + meta : get_int ( " offset " ) ) % meta : get_int ( " period " ) ~= 0 then
2016-12-31 07:38:18 +01:00
--It's not the builder's turn to build right now.
2017-09-11 02:58:25 +02:00
return 0 , { }
2016-12-31 07:38:18 +01:00
end
2017-09-11 02:58:25 +02:00
local extrusion_count = 0
local extrusion_target = meta : get_int ( " extrusion " )
2017-09-11 05:35:26 +02:00
if extrusion_target == nil or extrusion_target < 1 or extrusion_target > 100 then
extrusion_target = 1 -- failsafe
2016-12-31 07:38:18 +01:00
end
2017-09-11 02:58:25 +02:00
local return_items = { }
2019-01-05 06:06:12 +01:00
node_inventory_table.pos = pos
local inv = minetest.get_inventory ( node_inventory_table )
2016-12-31 07:38:18 +01:00
local item_stack = inv : get_stack ( " main " , 1 )
2017-09-11 02:58:25 +02:00
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
2016-12-31 07:38:18 +01:00
local source_location = digtron.take_from_inventory ( item_stack : get_name ( ) , inventory_positions )
if source_location ~= nil then
2017-09-11 02:58:25 +02:00
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"
2016-12-31 07:38:18 +01:00
end
2017-09-11 02:58:25 +02:00
extrusion_count = extrusion_count + 1
buildpos = digtron.find_new_pos ( buildpos , facing )
2016-12-31 07:38:18 +01:00
end
2017-09-11 02:58:25 +02:00
return 1 , return_items
2016-12-31 07:38:18 +01:00
end ,
execute_build = function ( pos , player , inventory_positions , protected_nodes , nodes_dug , controlling_coordinate , controller_pos )
local meta = minetest.get_meta ( pos )
2017-09-11 02:58:25 +02:00
local build_facing = tonumber ( meta : get_int ( " build_facing " ) )
2016-12-31 07:38:18 +01:00
local facing = minetest.get_node ( pos ) . param2
local buildpos = digtron.find_new_pos ( pos , facing )
2017-01-02 01:12:32 +01:00
if ( buildpos [ controlling_coordinate ] + meta : get_int ( " offset " ) ) % meta : get_int ( " period " ) ~= 0 then
2017-09-11 02:58:25 +02:00
return 0
2016-12-31 07:38:18 +01:00
end
2017-09-11 02:58:25 +02:00
local extrusion_count = 0
local extrusion_target = meta : get_int ( " extrusion " )
2017-09-11 05:35:26 +02:00
if extrusion_target == nil or extrusion_target < 1 or extrusion_target > 100 then
extrusion_target = 1 -- failsafe
2017-09-11 02:58:25 +02:00
end
local built_count = 0
2019-01-05 06:06:12 +01:00
node_inventory_table.pos = pos
local inv = minetest.get_inventory ( node_inventory_table )
2017-09-11 02:58:25 +02:00
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 )
2017-09-12 08:20:52 +02:00
if not digtron.config . uses_resources then
2017-09-11 02:58:25 +02:00
local returned_stack , success = digtron.item_place_node ( item_stack , player , buildpos , build_facing )
2016-12-31 07:38:18 +01:00
if success == true then
2017-09-20 07:44:59 +02:00
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 ) )
2016-12-31 07:38:18 +01:00
nodes_dug : set ( buildpos.x , buildpos.y , buildpos.z , false )
2017-09-11 02:58:25 +02:00
built_count = built_count + 1
2016-12-31 07:38:18 +01:00
else
2017-09-11 02:58:25 +02:00
return built_count
2016-12-31 07:38:18 +01:00
end
end
2017-09-11 02:58:25 +02:00
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
2019-01-06 06:04:56 +01:00
local returned_stack , success = digtron.item_place_node ( ItemStack ( item_stack ) , player , buildpos , build_facing )
2017-09-11 02:58:25 +02:00
if success == true then
2017-09-20 07:44:59 +02:00
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 ) )
2017-09-11 02:58:25 +02:00
--flag this node as *not* to be dug.
nodes_dug : set ( buildpos.x , buildpos.y , buildpos.z , false )
2018-12-24 03:06:49 +01:00
digtron.award_item_built ( item_stack : get_name ( ) , player )
2017-09-11 02:58:25 +02:00
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 )
2016-12-31 07:38:18 +01:00
end
2017-09-11 02:58:25 +02:00
return built_count
2016-12-31 07:38:18 +01:00
end ,
2019-01-02 07:31:51 +01:00
} )