2016-11-08 10:32:58 +01:00
basic_robot.commands = { } ;
2016-11-21 20:08:21 +01:00
-- set up nodes for planting (for example seeds -> plant) : [nodename] = plant_name
basic_robot.plant_table = { [ " farming:seed_barley " ] = " farming:barley_1 " , [ " farming:beans " ] = " farming:beanpole_1 " , -- so it works with farming redo mod
[ " farming:blueberries " ] = " farming:blueberry_1 " , [ " farming:carrot " ] = " farming:carrot_1 " , [ " farming:cocoa_beans " ] = " farming:cocoa_1 " ,
2017-02-10 09:59:36 +01:00
[ " farming:coffee_beans " ] = " farming:coffee_1 " , [ " farming:corn " ] = " farming:corn_1 " ,
2016-11-21 20:08:21 +01:00
[ " farming:seed_cotton " ] = " farming:cotton_1 " , [ " farming:cucumber " ] = " farming:cucumber_1 " , [ " farming:grapes " ] = " farming:grapes_1 " ,
[ " farming:melon_slice " ] = " farming:melon_1 " , [ " farming:potato " ] = " farming:potato_1 " , [ " farming:pumpkin_slice " ] = " farming:pumpkin_1 " ,
[ " farming:raspberries " ] = " farming:raspberry_1 " , [ " farming:rhubarb " ] = " farming:rhubarb_1 " , [ " farming:tomato " ] = " farming:tomato_1 " ,
[ " farming:seed_wheat " ] = " farming:wheat_1 " }
local function tick ( pos ) -- needed for plants to start growing: minetest 0.4.14 farming
minetest.get_node_timer ( pos ) : start ( math.random ( 166 , 286 ) )
end
2016-11-08 10:32:58 +01:00
local pi = math.pi ;
local function pos_in_dir ( obj , dir ) -- position after we move in specified direction
local yaw = obj : getyaw ( ) ;
local pos = obj : getpos ( ) ;
2017-10-21 15:40:52 +02:00
if dir == 1 then -- left
2016-11-08 10:32:58 +01:00
yaw = yaw + pi / 2 ;
2017-10-21 15:40:52 +02:00
elseif dir == 2 then --right
yaw = yaw - pi / 2 ;
elseif dir == 3 then -- forward
elseif dir == 4 then
yaw = yaw + pi ; -- backward
elseif dir == 5 then -- up
pos.y = pos.y + 1
elseif dir == 6 then -- down
pos.y = pos.y - 1
elseif dir == 7 then -- left_down
yaw = yaw + pi / 2 ; pos.y = pos.y - 1
elseif dir == 8 then -- right_down
yaw = yaw - pi / 2 ; pos.y = pos.y - 1
elseif dir == 9 then -- forward_down
pos.y = pos.y - 1
elseif dir == 10 then -- backward_down
yaw = yaw + pi ; pos.y = pos.y - 1
elseif dir == 11 then -- left_up
yaw = yaw + pi / 2 ; pos.y = pos.y + 1
elseif dir == 12 then -- right_up
yaw = yaw - pi / 2 ; pos.y = pos.y + 1
elseif dir == 13 then -- forward_up
pos.y = pos.y + 1
elseif dir == 14 then -- backward_up
2017-12-18 11:19:05 +01:00
yaw = yaw + pi ; pos.y = pos.y + 1
2016-11-08 10:32:58 +01:00
end
2017-10-21 15:40:52 +02:00
if dir ~= 5 and dir ~= 6 then
2016-11-08 10:32:58 +01:00
pos.x = pos.x + math.cos ( yaw )
pos.z = pos.z + math.sin ( yaw )
end
2016-11-14 17:20:04 +01:00
2016-11-08 10:32:58 +01:00
return pos
end
2017-08-02 12:48:33 +02:00
local check_operations = function ( name , amount , quit )
2017-07-01 23:00:16 +02:00
if basic_robot.maxoperations ~= 0 then
local data = basic_robot.data [ name ] ;
2017-08-02 12:48:33 +02:00
local operations = data.operations - amount ;
2017-07-16 23:27:52 +02:00
if operations >= 0 then
data.operations = operations
else
2017-07-01 23:00:16 +02:00
if quit then
2017-07-16 23:27:52 +02:00
error ( " robot out of available operations in one step. " ) ; return false
2017-07-01 23:00:16 +02:00
end
2017-07-16 23:27:52 +02:00
return false
2017-07-01 23:00:16 +02:00
end
end
end
2016-11-10 13:07:56 +01:00
basic_robot.commands . move = function ( name , dir )
local obj = basic_robot.data [ name ] . obj ;
2016-11-08 10:32:58 +01:00
local pos = pos_in_dir ( obj , dir )
2016-11-14 17:20:04 +01:00
-- can move through walkable nodes
if minetest.registered_nodes [ minetest.get_node ( pos ) . name ] . walkable then return end
2016-11-08 10:32:58 +01:00
-- up; no levitation!
if minetest.get_node ( { x = pos.x , y = pos.y - 1 , z = pos.z } ) . name == " air " and
minetest.get_node ( { x = pos.x , y = pos.y - 2 , z = pos.z } ) . name == " air " then
2016-11-14 17:20:04 +01:00
return false
2016-11-08 10:32:58 +01:00
end
2016-11-11 08:50:02 +01:00
2016-11-08 10:32:58 +01:00
obj : moveto ( pos , true )
2016-11-14 17:20:04 +01:00
-- sit and stand up for model - doenst work for overwriten obj export
-- if dir == 5 then-- up
-- obj:set_animation({x=0,y=0})
-- elseif dir == 6 then -- down
-- obj:set_animation({x=81,y=160})
-- end
return true
2016-11-08 10:32:58 +01:00
end
2016-11-10 13:07:56 +01:00
basic_robot.commands . turn = function ( name , angle )
local obj = basic_robot.data [ name ] . obj ;
2017-07-16 23:27:52 +02:00
local yaw ;
-- more precise turns by 1 degree resolution
local mult = math.pi / 180 ;
local yaw = obj : getyaw ( ) ;
yaw = math.floor ( ( yaw + angle ) / mult + 0.5 ) * mult ;
2016-11-08 10:32:58 +01:00
obj : setyaw ( yaw ) ;
end
2017-07-16 23:27:52 +02:00
basic_robot.digcosts = { -- 1 energy = 1 coal
[ " default:stone " ] = 1 / 25 ,
}
2016-11-10 13:07:56 +01:00
basic_robot.commands . dig = function ( name , dir )
2016-11-22 14:28:49 +01:00
2017-02-06 11:03:30 +01:00
local energy = 0 ;
2017-08-02 12:48:33 +02:00
check_operations ( name , 2 , true )
2016-11-22 14:28:49 +01:00
2016-11-10 13:07:56 +01:00
local obj = basic_robot.data [ name ] . obj ;
2016-11-08 10:32:58 +01:00
local pos = pos_in_dir ( obj , dir )
2017-01-18 17:13:17 +01:00
2016-11-08 10:32:58 +01:00
local luaent = obj : get_luaentity ( ) ;
2016-12-21 12:11:57 +01:00
if minetest.is_protected ( pos , luaent.owner ) then return false end
2016-11-10 11:36:22 +01:00
local nodename = minetest.get_node ( pos ) . name ;
2016-12-21 12:11:57 +01:00
if nodename == " air " or nodename == " ignore " then return false end
2016-11-10 11:36:22 +01:00
local spos = obj : get_luaentity ( ) . spawnpos ;
local inv = minetest.get_meta ( spos ) : get_inventory ( ) ;
2017-04-27 09:22:17 +02:00
2017-07-16 23:27:52 +02:00
--require energy to dig
if basic_robot.dig_require_energy then
local digcost = basic_robot.digcosts [ nodename ] ;
if digcost then
local data = basic_robot.data [ name ] ;
local energy = ( data.menergy or 0 ) - digcost ;
if energy < 0 then
2017-10-21 15:40:52 +02:00
error ( " need " .. digcost .. " energy to dig " .. nodename .. " . Use machine.generate(...) to get some energy. " ) ;
2017-04-27 09:22:17 +02:00
end
2017-07-16 23:27:52 +02:00
data.menergy = energy ;
2017-04-27 09:22:17 +02:00
end
end
2016-11-10 11:36:22 +01:00
if not inv then return end
2016-11-21 20:08:21 +01:00
--inv:add_item("main",ItemStack( nodename ));
2016-11-10 11:36:22 +01:00
2016-11-21 20:08:21 +01:00
basic_robot.give_drops ( nodename , inv ) ;
minetest.set_node ( pos , { name = " air " } )
2016-12-10 10:50:48 +01:00
2016-11-21 20:08:21 +01:00
--DS: sounds
local sounds = minetest.registered_nodes [ nodename ] . sounds
2016-11-14 17:20:04 +01:00
if sounds then
local sound = sounds.dug
if sound then
2017-01-18 17:13:17 +01:00
minetest.sound_play ( sound , { pos = pos , max_hear_distance = 10 } )
2016-11-14 17:20:04 +01:00
end
end
return true
2016-11-08 10:32:58 +01:00
end
2016-11-14 17:20:04 +01:00
basic_robot.commands . insert_item = function ( name , item , inventory , dir )
local obj = basic_robot.data [ name ] . obj ;
local tpos = pos_in_dir ( obj , dir ) ; -- position of target block
local luaent = obj : get_luaentity ( ) ;
if minetest.is_protected ( tpos , luaent.owner ) then return false end
local pos = basic_robot.data [ name ] . spawnpos ; -- position of spawner block
local meta = minetest.get_meta ( pos ) ;
local tmeta = minetest.get_meta ( tpos ) ;
local inv = minetest.get_meta ( pos ) : get_inventory ( ) ;
2017-02-10 09:59:36 +01:00
-- fertilize if soil
if item == " farming:fertilizer " then
local stack = ItemStack ( item ) ;
if minetest.get_node ( tpos ) . name == " farming:soil_wet " and ( meta : get_int ( " admin " ) == 1 or inv : contains_item ( " main " , stack ) ) then
inv : remove_item ( " main " , stack ) ;
local nutrient = tmeta : get_int ( " nutrient " ) ; nutrient = nutrient + 10 ; if nutrient > 20 then nutrient = 20 end
tmeta : set_int ( " nutrient " , nutrient ) ;
minetest.set_node ( { x = tpos.x , y = tpos.y + 1 , z = tpos.z } , { name = " air " } )
return true
end
end
2016-11-14 17:20:04 +01:00
local tinv = minetest.get_meta ( tpos ) : get_inventory ( ) ;
if not inventory then inventory = " main " ; end
--if not inv then return end
local stack = ItemStack ( item ) ;
if ( not inv : contains_item ( " main " , stack ) or not tinv : room_for_item ( inventory , stack ) ) and meta : get_int ( " admin " ) ~= 1 then
return false
end
tinv : add_item ( inventory , stack ) ;
inv : remove_item ( " main " , stack ) ;
return true
end
basic_robot.commands . take_item = function ( name , item , inventory , dir )
local obj = basic_robot.data [ name ] . obj ;
local tpos = pos_in_dir ( obj , dir ) ; -- position of target block
local luaent = obj : get_luaentity ( ) ;
if minetest.is_protected ( tpos , luaent.owner ) then return false end
local pos = basic_robot.data [ name ] . spawnpos ; -- position of spawner block
if basic_robot.bad_inventory_blocks [ minetest.get_node ( tpos ) . name ] then return false end -- dont allow take from
local meta = minetest.get_meta ( pos ) ;
local tmeta = minetest.get_meta ( tpos ) ;
local inv = minetest.get_meta ( pos ) : get_inventory ( ) ;
local tinv = minetest.get_meta ( tpos ) : get_inventory ( ) ;
if not inventory then inventory = " main " ; end
--if not inv then return end
local stack = ItemStack ( item ) ;
2016-11-27 23:24:35 +01:00
local contains = tinv : contains_item ( inventory , stack ) ;
if ( not contains or not inv : room_for_item ( " main " , stack ) ) and meta : get_int ( " admin " ) ~= 1 then
2016-11-14 17:20:04 +01:00
return false
end
inv : add_item ( " main " , stack ) ;
tinv : remove_item ( inventory , stack ) ;
2017-01-19 12:04:12 +01:00
return true
2016-11-14 17:20:04 +01:00
end
2017-02-06 11:03:30 +01:00
-- check_inventory(item, inventory, position)
--if position>0 then it returns name of item at that position
basic_robot.commands . check_inventory = function ( name , itemname , inventory , position , dir )
2016-12-21 12:11:57 +01:00
local obj = basic_robot.data [ name ] . obj ;
local tpos ;
if dir ~= 0 then
tpos = pos_in_dir ( obj , dir ) ; -- position of target block in front
else
tpos = obj : get_luaentity ( ) . spawnpos ;
end
local tinv = minetest.get_meta ( tpos ) : get_inventory ( ) ;
2017-02-06 11:03:30 +01:00
if not position then position = - 1 end
if position > 0 then
return tinv : get_stack ( inventory , position ) : to_string ( )
end
if itemname == " " then
return tinv : is_empty ( inventory )
end
2016-12-21 12:11:57 +01:00
local stack = ItemStack ( itemname ) ;
if not inventory then inventory = " main " ; end
return tinv : contains_item ( inventory , stack ) ;
end
2016-11-14 17:20:04 +01:00
2016-12-10 10:50:48 +01:00
basic_robot.no_teleport_table = {
[ " itemframes:item " ] = true ,
[ " signs:text " ] = true ,
2017-04-03 08:37:00 +02:00
[ " basic_robot:robot " ] = true ,
[ " robot " ] = true ,
2016-12-10 10:50:48 +01:00
}
2017-02-06 11:03:30 +01:00
2016-12-10 10:50:48 +01:00
basic_robot.commands . pickup = function ( r , name )
if r > 8 then return false end
2016-12-21 12:11:57 +01:00
2016-12-10 10:50:48 +01:00
local pos = basic_robot.data [ name ] . obj : getpos ( ) ;
local spos = basic_robot.data [ name ] . spawnpos ; -- position of spawner block
local meta = minetest.get_meta ( spos ) ;
local inv = minetest.get_meta ( spos ) : get_inventory ( ) ;
2017-02-06 11:03:30 +01:00
local picklist = { } ;
2016-12-10 10:50:48 +01:00
for _ , obj in pairs ( minetest.get_objects_inside_radius ( { x = pos.x , y = pos.y , z = pos.z } , r ) ) do
local lua_entity = obj : get_luaentity ( )
2016-12-21 12:11:57 +01:00
if not obj : is_player ( ) and lua_entity and lua_entity.itemstring then
local detected_obj = lua_entity.itemstring or " "
2016-12-10 10:50:48 +01:00
if not basic_robot.no_teleport_table [ detected_obj ] then -- object on no teleport list
-- put item in chest
local stack = ItemStack ( lua_entity.itemstring )
2017-02-06 11:03:30 +01:00
picklist [ # picklist + 1 ] = detected_obj ;
2016-12-10 10:50:48 +01:00
if inv : room_for_item ( " main " , stack ) then
inv : add_item ( " main " , stack ) ;
2017-10-21 15:40:52 +02:00
obj : setpos ( { x = 0 , y = 0 , z = 0 } ) -- no dupe
2016-12-10 10:50:48 +01:00
end
2017-04-03 08:37:00 +02:00
obj : remove ( ) ;
2016-12-10 10:50:48 +01:00
end
end
end
2017-02-06 11:03:30 +01:00
if not picklist [ 1 ] then return nil end
return picklist
2016-12-10 10:50:48 +01:00
end
2016-11-14 17:20:04 +01:00
2016-11-10 13:07:56 +01:00
basic_robot.commands . read_node = function ( name , dir )
local obj = basic_robot.data [ name ] . obj ;
2016-11-08 10:32:58 +01:00
local pos = pos_in_dir ( obj , dir )
return minetest.get_node ( pos ) . name or " "
end
2017-02-10 09:59:36 +01:00
basic_robot.commands . read_text = function ( name , mode , dir , stringname )
if not mode then mode = 0 end
2016-11-11 08:50:02 +01:00
local obj = basic_robot.data [ name ] . obj ;
local pos = pos_in_dir ( obj , dir )
2017-02-10 09:59:36 +01:00
2016-11-21 20:08:21 +01:00
if stringname == nil then
stringname = " infotext "
end
2017-02-10 09:59:36 +01:00
if mode == 1 then return minetest.get_meta ( pos ) : get_int ( stringname ) else
return minetest.get_meta ( pos ) : get_string ( stringname ) or " " end
2016-11-11 08:50:02 +01:00
end
2016-11-08 10:32:58 +01:00
2016-12-24 12:36:03 +01:00
basic_robot.commands . write_text = function ( name , dir , text )
local obj = basic_robot.data [ name ] . obj ;
local pos = pos_in_dir ( obj , dir )
local luaent = obj : get_luaentity ( ) ;
if minetest.is_protected ( pos , luaent.owner ) then return false end
minetest.get_meta ( pos ) : set_string ( " infotext " , text or " " )
return true
end
2017-04-27 09:22:17 +02:00
basic_robot.commands . place = function ( name , nodename , param2 , dir )
2016-11-10 13:07:56 +01:00
local obj = basic_robot.data [ name ] . obj ;
2016-11-08 10:32:58 +01:00
local pos = pos_in_dir ( obj , dir )
local luaent = obj : get_luaentity ( ) ;
2016-11-14 17:20:04 +01:00
if minetest.is_protected ( pos , luaent.owner ) then return false end
if minetest.get_node ( pos ) . name ~= " air " then return false end
2016-11-10 11:36:22 +01:00
local spos = obj : get_luaentity ( ) . spawnpos ;
2016-11-10 13:07:56 +01:00
local meta = minetest.get_meta ( spos ) ;
local inv = meta : get_inventory ( ) ;
2016-11-14 17:20:04 +01:00
if not inv then return false end
2016-12-10 10:50:48 +01:00
if not inv : contains_item ( " main " , ItemStack ( nodename ) ) and meta : get_int ( " admin " ) ~= 1 then return false end
2016-11-10 11:36:22 +01:00
inv : remove_item ( " main " , ItemStack ( nodename ) ) ;
2016-11-10 13:07:56 +01:00
2016-11-14 17:20:04 +01:00
--DS
2016-12-21 12:11:57 +01:00
local registered_node = minetest.registered_nodes [ nodename ] ;
if registered_node then
local sounds = registered_node.sounds
if sounds then
local sound = sounds.place
if sound then
2017-01-18 17:13:17 +01:00
minetest.sound_play ( sound , { pos = pos , max_hear_distance = 10 } )
2016-12-21 12:11:57 +01:00
end
2016-11-14 17:20:04 +01:00
end
end
2016-12-10 10:50:48 +01:00
local placename = basic_robot.plant_table [ nodename ] ;
2016-11-21 20:08:21 +01:00
if placename then
minetest.set_node ( pos , { name = placename } )
tick ( pos ) ; -- needed for seeds to grow
2017-04-27 09:22:17 +02:00
else -- normal place
if param2 then
minetest.set_node ( pos , { name = nodename , param2 = param2 } )
else
minetest.set_node ( pos , { name = nodename } )
end
2016-11-21 20:08:21 +01:00
end
2016-11-14 17:20:04 +01:00
return true
end
basic_robot.commands . attack = function ( name , target ) -- attack range 4, damage 5
2017-03-04 22:03:01 +01:00
local energy = 0 ;
2017-08-02 12:48:33 +02:00
check_operations ( name , 2 , true ) ;
2017-03-04 22:03:01 +01:00
2016-11-14 17:20:04 +01:00
local reach = 4 ;
local damage = 5 ;
local tplayer = minetest.get_player_by_name ( target ) ;
if not tplayer then return false end
local obj = basic_robot.data [ name ] . obj ;
local pos = obj : getpos ( ) ;
local tpos = tplayer : getpos ( ) ;
if math.abs ( pos.x - tpos.x ) > reach or math.abs ( pos.y - tpos.y ) > reach or math.abs ( pos.z - tpos.z ) > reach then
return false
end
tplayer : set_hp ( tplayer : get_hp ( ) - damage )
return true
end
2016-12-21 12:11:57 +01:00
basic_robot.commands . grab = function ( name , target )
local reach = 4 ;
local tplayer = minetest.get_player_by_name ( target ) ;
if not tplayer then return false end
local obj = basic_robot.data [ name ] . obj ;
local pos = obj : getpos ( ) ;
local tpos = tplayer : getpos ( ) ;
if math.abs ( pos.x - tpos.x ) > reach or math.abs ( pos.y - tpos.y ) > reach or math.abs ( pos.z - tpos.z ) > reach then
return false
end
if tplayer : get_attach ( ) then
tplayer : set_detach ( )
2017-01-03 17:03:46 +01:00
return false
2016-12-21 12:11:57 +01:00
else
tplayer : set_attach ( obj , " " , { x = 0 , y = 5 , z = 0 } , { x = 0 , y = 0 , z = 0 } )
end
return true
end
2016-11-14 17:20:04 +01:00
basic_robot.commands . read_book = function ( itemstack ) -- itemstack should contain book
2017-07-16 23:27:52 +02:00
local data = itemstack : get_meta ( ) : to_table ( ) . fields -- 0.4.16
--local data = minetest.deserialize(itemstack:get_metadata()) -- pre 0.4.16
2016-11-14 17:20:04 +01:00
if data then
2017-01-18 17:13:17 +01:00
return data.title , data.text ;
2016-11-14 17:20:04 +01:00
else
return nil
end
end
2016-12-21 12:11:57 +01:00
basic_robot.commands . write_book = function ( name , title , text ) -- returns itemstack containing book
2016-11-14 17:20:04 +01:00
local lpp = 14 ;
local new_stack = ItemStack ( " default:book_written " )
local data = { }
2016-12-21 12:11:57 +01:00
if title == " " or not title then title = " program book " .. minetest.get_gametime ( ) end
data.title = title
data.text = text or " "
2016-11-14 17:20:04 +01:00
data.text_len = # data.text
data.page = 1
data.page_max = math.ceil ( ( # data.text : gsub ( " [^ \n ] " , " " ) + 1 ) / lpp )
data.owner = name
2017-07-16 23:27:52 +02:00
--local data_str = minetest.serialize(data) -- pre 0.4.16
--new_stack:set_metadata(data_str);
new_stack : get_meta ( ) : from_table ( { fields = data } ) -- 0.4.16
2016-11-14 17:20:04 +01:00
return new_stack ;
end
2016-11-21 20:08:21 +01:00
basic_robot.give_drops = function ( nodename , inv ) -- gives apropriate drops when node is dug
local table = minetest.registered_items [ nodename ] ;
local dropname ;
if table ~= nil then --put in chest
if table.drop ~= nil then -- drop handling
if table.drop . items then
--handle drops better, emulation of drop code
local max_items = table.drop . max_items or 0 ;
if max_items == 0 then -- just drop all the items (taking the rarity into consideration)
max_items = # table.drop . items or 0 ;
end
local drop = table.drop ;
local i = 0 ;
for k , v in pairs ( drop.items ) do
if i > max_items then break end ; i = i + 1 ;
local rare = v.rarity or 1 ;
if math.random ( 1 , rare ) == 1 then
dropname = v.items [ math.random ( 1 , # v.items ) ] ; -- pick item randomly from list
inv : add_item ( " main " , dropname ) ;
end
end
else
inv : add_item ( " main " , table.drop ) ;
end
else
inv : add_item ( " main " , nodename ) ;
end
end
end
2017-01-18 17:13:17 +01:00
local render_text = function ( text , linesize )
local count = math.floor ( string.len ( text ) / linesize ) + 1 ;
local width = 18 ; local height = 24 ;
local tex = " " ;
local y = 0 ; local x = 0 ;
for i = 1 , string.len ( text ) do
local cb = string.byte ( text , i ) ;
local c = " " ;
if cb == 10 or cb == 13 then
y = y + 1 ; x = 0
else
c = string.format ( " %03d " , cb ) .. " .png "
tex = tex .. " : " .. ( x * width ) .. " , " .. ( y * height ) .. " = " .. c ;
if x == linesize - 1 then y = y + 1 x = 0 else x = x + 1 end
end
end
local background = " (black_screen.png^[resize: " .. ( linesize * width ) .. " x " .. ( linesize * height ) .. " ) " ;
tex = " ([combine: " .. ( linesize * width ) .. " x " .. ( linesize * height ) .. tex .. " ) " ;
tex = background .. " ^ " .. tex ;
return tex ;
2017-02-06 11:03:30 +01:00
end
2017-01-18 17:13:17 +01:00
basic_robot.commands . display_text = function ( obj , text , linesize , size )
2017-02-06 11:03:30 +01:00
if not linesize or linesize < 1 then linesize = 20 end
2017-08-03 23:18:52 +02:00
if size and size <= 0 then size = 1 end
2017-01-18 17:13:17 +01:00
if string.len ( text ) > linesize * linesize then text = string.sub ( text , 1 , linesize * linesize ) end
local tex = render_text ( text , linesize ) ;
2017-08-03 23:18:52 +02:00
if not size then return tex end
2017-01-18 17:13:17 +01:00
if string.len ( tex ) < 60000 then
obj : set_properties ( { textures = { " arrow.png " , " basic_machine_side.png " , tex , " basic_machine_side.png " , " basic_machine_side.png " , " basic_machine_side.png " } , visual_size = { x = size , y = size } } )
else
self.label ( " error: string too long " )
end
end
2017-02-06 11:03:30 +01:00
local robot_activate_furnace = minetest.registered_nodes [ " default:furnace " ] . on_metadata_inventory_put ; -- this function will activate furnace
2017-01-18 17:13:17 +01:00
basic_robot.commands . activate = function ( name , mode , dir )
local obj = basic_robot.data [ name ] . obj ;
local tpos = pos_in_dir ( obj , dir ) ; -- position of target block in front
local node = minetest.get_node ( tpos ) ;
2017-02-06 11:03:30 +01:00
if node.name == " default:furnace " or node.name == " default:furnace_active " then
if mode > 0 then robot_activate_furnace ( tpos ) end
2017-04-27 09:22:17 +02:00
return true
2017-02-06 11:03:30 +01:00
end
2017-01-18 17:13:17 +01:00
local table = minetest.registered_nodes [ node.name ] ;
if table and table.mesecons and table.mesecons . effector then
else
2017-04-27 09:22:17 +02:00
return false
2017-01-18 17:13:17 +01:00
end -- error
local effector = table.mesecons . effector ;
2017-07-16 23:27:52 +02:00
if not mode then mode = 1 end
2017-01-18 17:13:17 +01:00
if mode > 0 then
2017-04-27 09:22:17 +02:00
if not effector.action_on then return false end
2017-01-18 17:13:17 +01:00
effector.action_on ( tpos , node , 16 )
elseif mode < 0 then
2017-04-27 09:22:17 +02:00
if not effector.action_off then return false end
2017-01-18 17:13:17 +01:00
effector.action_off ( tpos , node , 16 )
end
2017-04-27 09:22:17 +02:00
return true
2017-01-18 17:13:17 +01:00
end
local register_robot_button = function ( R , G , B , type )
2017-10-21 15:40:52 +02:00
minetest.register_node ( " basic_robot:button " .. R .. G .. B ,
{
description = " robot button " ,
tiles = { " robot_button.png^[colorize:# " .. R .. G .. B .. " :180 " } ,
inventory_image = " robot_button.png^[colorize:# " .. R .. G .. B .. " :180 " ,
wield_image = " robot_button.png^[colorize:# " .. R .. G .. B .. " :180 " ,
is_ground_content = false ,
groups = { cracky = 3 } ,
on_punch = function ( pos , node , player )
local name = player : get_player_name ( ) ; if name == nil then return end
local round = math.floor ;
2017-12-18 11:19:05 +01:00
local r = basic_robot.radius ; local ry = 2 * r ; -- note: this is skyblock adjusted
2017-10-21 15:40:52 +02:00
local ppos = { x = round ( pos.x / r + 0.5 ) * r , y = round ( pos.y / ry + 0.5 ) * ry + 1 , z = round ( pos.z / r + 0.5 ) * r } ; -- just on top of basic_protect:protector!
local meta = minetest.get_meta ( ppos ) ;
local name = meta : get_string ( " name " ) ;
local data = basic_robot.data [ name ] ;
if data then data.keyboard = { x = pos.x , y = pos.y , z = pos.z , puncher = player : get_player_name ( ) , type = type } end
end
} )
end
local register_robot_button_number = function ( number , type )
minetest.register_node ( " basic_robot:button " .. number ,
2017-01-18 17:13:17 +01:00
{
description = " robot button " ,
2017-10-21 15:40:52 +02:00
tiles = { " robot_button " .. number .. " .png " } ,
inventory_image = " robot_button " .. number .. " .png " ,
wield_image = " robot_button " .. number .. " .png " ,
2017-01-18 17:13:17 +01:00
is_ground_content = false ,
groups = { cracky = 3 } ,
on_punch = function ( pos , node , player )
local name = player : get_player_name ( ) ; if name == nil then return end
local round = math.floor ;
2017-12-18 11:19:05 +01:00
local r = basic_robot.radius ; local ry = 2 * r ;
2017-01-18 17:13:17 +01:00
local ppos = { x = round ( pos.x / r + 0.5 ) * r , y = round ( pos.y / ry + 0.5 ) * ry + 1 , z = round ( pos.z / r + 0.5 ) * r } ;
local meta = minetest.get_meta ( ppos ) ;
local name = meta : get_string ( " name " ) ;
local data = basic_robot.data [ name ] ;
if data then data.keyboard = { x = pos.x , y = pos.y , z = pos.z , puncher = player : get_player_name ( ) , type = type } end
end
} )
end
2017-10-21 15:40:52 +02:00
local register_robot_button_char = function ( number , type )
minetest.register_node ( " basic_robot:button_ " .. number ,
2017-03-04 22:03:01 +01:00
{
description = " robot button " ,
2017-10-21 15:40:52 +02:00
tiles = { string.format ( " %03d " , number ) .. " .png " } ,
inventory_image = string.format ( " %03d " , number ) .. " .png " ,
wield_image = string.format ( " %03d " , number ) .. " .png " ,
2017-03-04 22:03:01 +01:00
is_ground_content = false ,
groups = { cracky = 3 } ,
on_punch = function ( pos , node , player )
local name = player : get_player_name ( ) ; if name == nil then return end
local round = math.floor ;
2017-12-18 11:19:05 +01:00
local r = basic_robot.radius ; local ry = 2 * r ;
2017-03-04 22:03:01 +01:00
local ppos = { x = round ( pos.x / r + 0.5 ) * r , y = round ( pos.y / ry + 0.5 ) * ry + 1 , z = round ( pos.z / r + 0.5 ) * r } ;
local meta = minetest.get_meta ( ppos ) ;
local name = meta : get_string ( " name " ) ;
local data = basic_robot.data [ name ] ;
if data then data.keyboard = { x = pos.x , y = pos.y , z = pos.z , puncher = player : get_player_name ( ) , type = type } end
end
} )
end
2017-10-21 15:40:52 +02:00
local register_robot_button_custom = function ( number , texture )
minetest.register_node ( " basic_robot:button_ " .. number ,
{
description = " robot button " ,
tiles = { texture .. " .png " } ,
inventory_image = texture .. " .png " ,
wield_image = texture .. " .png " ,
is_ground_content = false ,
groups = { cracky = 3 } ,
on_punch = function ( pos , node , player )
local name = player : get_player_name ( ) ; if name == nil then return end
local round = math.floor ;
2017-12-18 11:19:05 +01:00
local r = basic_robot.radius ; local ry = 2 * r ;
2017-10-21 15:40:52 +02:00
local ppos = { x = round ( pos.x / r + 0.5 ) * r , y = round ( pos.y / ry + 0.5 ) * ry + 1 , z = round ( pos.z / r + 0.5 ) * r } ;
local meta = minetest.get_meta ( ppos ) ;
local name = meta : get_string ( " name " ) ;
local data = basic_robot.data [ name ] ;
if data then data.keyboard = { x = pos.x , y = pos.y , z = pos.z , puncher = player : get_player_name ( ) , type = number } end
end
} )
end
2017-01-18 17:13:17 +01:00
register_robot_button ( " FF " , " FF " , " FF " , 1 ) ;
register_robot_button ( " 80 " , " 80 " , " 80 " , 2 ) ;
register_robot_button ( " FF " , " 80 " , " 80 " , 3 ) ;
register_robot_button ( " 80 " , " FF " , " 80 " , 4 ) ;
register_robot_button ( " 80 " , " 80 " , " FF " , 5 ) ;
register_robot_button ( " FF " , " FF " , " 80 " , 6 ) ;
2017-03-04 22:03:01 +01:00
for i = 0 , 9 do register_robot_button_number ( i , i + 7 ) end
2017-10-21 15:40:52 +02:00
for i = 0 , 255 do register_robot_button_char ( i , i + 17 ) end
register_robot_button_custom ( 273 , " puzzle_switch_off " )
register_robot_button_custom ( 274 , " puzzle_switch_on " )
register_robot_button_custom ( 275 , " puzzle_button_off " )
register_robot_button_custom ( 276 , " puzzle_button_on " )
register_robot_button_custom ( 277 , " puzzle_equalizer " )
register_robot_button_custom ( 278 , " puzzle_setter " )
register_robot_button_custom ( 279 , " puzzle_piston " )
register_robot_button_custom ( 280 , " puzzle_diode " )
register_robot_button_custom ( 281 , " puzzle_NOT " )
register_robot_button_custom ( 282 , " puzzle_delayer " )
register_robot_button_custom ( 283 , " puzzle_platform " )
2017-03-04 22:03:01 +01:00
2017-12-18 11:19:05 +01:00
register_robot_button_custom ( 284 , " puzzle_giver " )
register_robot_button_custom ( 285 , " puzzle_checker " )
2017-01-18 17:13:17 +01:00
-- interactive button for robot: place robot on top of protector to intercept events
basic_robot.commands . keyboard = {
get = function ( name )
local data = basic_robot.data [ name ] ;
if data.keyboard then
local keyboard = data.keyboard ;
local event = { x = keyboard.x , y = keyboard.y , z = keyboard.z , puncher = keyboard.puncher , type = keyboard.type } ;
data.keyboard = nil ;
return event
else
return nil
end
end ,
2017-07-16 23:27:52 +02:00
set = function ( data , pos , type )
local owner = data.owner ;
2017-07-18 14:15:53 +02:00
local spos = data.spawnpos ;
local dist = math.max ( math.abs ( spos.x - pos.x ) , math.abs ( spos.y - pos.y ) , math.abs ( spos.z - pos.z ) ) ;
if dist > 10 then return false end
2017-07-16 23:27:52 +02:00
if minetest.is_protected ( pos , owner ) then return false end -- with fast protect checks this shouldnt be problem!
2017-01-18 17:13:17 +01:00
local nodename ;
if type == 0 then
nodename = " air "
elseif type == 1 then
nodename = " basic_robot:buttonFFFFFF " ;
elseif type == 2 then
nodename = " basic_robot:button808080 " ;
elseif type == 3 then
nodename = " basic_robot:buttonFF8080 " ;
elseif type == 4 then
nodename = " basic_robot:button80FF80 " ;
elseif type == 5 then
nodename = " basic_robot:button8080FF " ;
elseif type == 6 then
nodename = " basic_robot:buttonFFFF80 " ;
2017-10-21 15:40:52 +02:00
elseif type >= 7 and type <= 16 then
2017-03-04 22:03:01 +01:00
nodename = " basic_robot:button " .. ( type - 7 ) ;
2017-10-21 15:40:52 +02:00
else
nodename = " basic_robot:button_ " .. ( type - 17 ) ;
2017-01-18 17:13:17 +01:00
end
minetest.swap_node ( pos , { name = nodename } )
return true
end ,
2017-02-10 09:59:36 +01:00
}
basic_robot.commands . craftcache = { } ;
2017-07-01 23:00:16 +02:00
2017-10-21 15:40:52 +02:00
basic_robot.commands . craft = function ( item , mode , idx , name )
if not item then return false end
2017-02-10 09:59:36 +01:00
local cache = basic_robot.commands . craftcache [ name ] ;
if not cache then basic_robot.commands . craftcache [ name ] = { } ; cache = basic_robot.commands . craftcache [ name ] end
2017-04-27 09:22:17 +02:00
local itemlist = { } ; local output = " " ;
2018-06-30 12:53:45 +02:00
if cache.item == item and cache.idx == idx then -- read cache
2017-02-10 09:59:36 +01:00
itemlist = cache.itemlist ;
2017-04-27 09:22:17 +02:00
output = cache.output ;
2017-02-10 09:59:36 +01:00
else
2017-04-03 08:37:00 +02:00
2017-10-21 15:40:52 +02:00
local craft ;
if not idx then
craft = minetest.get_craft_recipe ( item ) ;
else
craft = minetest.get_all_craft_recipes ( item ) [ idx ]
end
if craft and craft.type == " normal " and craft.items then else return false end
2017-04-27 09:22:17 +02:00
output = craft.output ;
2017-02-10 09:59:36 +01:00
local items = craft.items ;
for _ , item in pairs ( items ) do
itemlist [ item ] = ( itemlist [ item ] or 0 ) + 1 ;
end
cache.item = item ;
2018-06-30 12:53:45 +02:00
cache.idx = idx ;
2017-04-03 08:37:00 +02:00
cache.itemlist = itemlist ;
2017-04-27 09:22:17 +02:00
cache.output = output ;
2017-04-03 08:37:00 +02:00
-- loop through robot inventory for those "group" items and see if anything in inventory matches group - then replace
-- group name with that item
local pos = basic_robot.data [ name ] . spawnpos ; -- position of spawner block
local inv = minetest.get_meta ( pos ) : get_inventory ( ) ;
for item , v in pairs ( itemlist ) do
local k = string.find ( item , " group: " ) ;
if k then
local group = string.sub ( item , k + 6 ) ;
-- do we have that in inventory?
local size = inv : get_size ( " main " ) ;
for i = 1 , size do
local itemname = inv : get_stack ( " main " , i ) : get_name ( ) ;
local groups = minetest.registered_items [ itemname ] . groups or { } ;
if groups [ group ] then cache.itemlist [ item ] = nil ; cache.itemlist [ itemname ] = v break end
end
end
end
2017-02-10 09:59:36 +01:00
end
--minetest.chat_send_all(item)
--minetest.chat_send_all(dump(itemlist))
2017-07-01 23:00:16 +02:00
if mode == 1 then return itemlist end
2017-02-10 09:59:36 +01:00
-- check if all items from itemlist..
-- craft item
local pos = basic_robot.data [ name ] . spawnpos ; -- position of spawner block
local inv = minetest.get_meta ( pos ) : get_inventory ( ) ;
for item , quantity in pairs ( itemlist ) do
local stack = ItemStack ( item .. " " .. quantity ) ;
if not inv : contains_item ( " main " , stack ) then return false end
end
for item , quantity in pairs ( itemlist ) do
local stack = ItemStack ( item .. " " .. quantity ) ;
inv : remove_item ( " main " , stack ) ;
end
2017-04-27 09:22:17 +02:00
inv : add_item ( " main " , ItemStack ( output ) )
2017-02-10 09:59:36 +01:00
return true
2017-03-04 22:03:01 +01:00
end
--FORMS
basic_robot.commands . show_form = function ( name , playername , form )
minetest.show_formspec ( playername , " robot_form " .. name , form )
end
-- handle robots receiving fields
minetest.register_on_player_receive_fields ( function ( player , formname , fields )
if not string.sub ( formname , 1 , 10 ) == " robot_form " then return end
local name = string.sub ( formname , 11 ) ; -- robot name
if not basic_robot.data [ name ] then return end
basic_robot.data [ name ] . read_form = fields ;
basic_robot.data [ name ] . form_sender = player : get_player_name ( ) or " " ;
2017-04-27 09:22:17 +02:00
end )
-- ROBOT TECHNIC
-- amount parameter in generate_power, smelt,... is determined by upgrade level
-- it specifies how much energy will be generated :
basic_robot.technic = { -- data cache
fuels = { } , --[fuel] = value
2017-07-01 23:00:16 +02:00
smelts = { } , -- [item] = [cooktime, cookeditem, aftercookeditem]
2017-04-27 09:22:17 +02:00
grinder_recipes = { --[in] ={fuel cost, out, quantity of material required for processing}
[ " default:stone " ] = { 2 , " default:sand " , 1 } ,
[ " default:cobble " ] = { 1 , " default:gravel " , 1 } ,
[ " default:gravel " ] = { 0.5 , " default:dirt " , 1 } ,
[ " default:dirt " ] = { 0.5 , " default:clay_lump 4 " , 1 } ,
[ " es:aikerum_crystal " ] = { 16 , " es:aikerum_dust 2 " , 1 } , -- added for es mod
[ " es:ruby_crystal " ] = { 16 , " es:ruby_dust 2 " , 1 } ,
[ " es:emerald_crystal " ] = { 16 , " es:emerald_dust 2 " , 1 } ,
[ " es:purpellium_lump " ] = { 16 , " es:purpellium_dust 2 " , 1 } ,
[ " default:obsidian_shard " ] = { 199 , " default:lava_source " , 1 } ,
[ " gloopblocks:basalt " ] = { 1 , " default:cobble " , 1 } , -- enable coble farms with gloopblocks mod
[ " default:ice " ] = { 1 , " default:snow 4 " , 1 } ,
[ " darkage:silt_lump " ] = { 1 , " darkage:chalk_powder " , 1 } ,
[ " default:diamond " ] = { 16 , " basic_machines:diamond_dust_33 2 " , 1 } ,
[ " default:ice " ] = { 1 , " default:snow " , 1 } ,
[ " moreores:tin_lump " ] = { 4 , " basic_machines:tin_dust_33 2 " , 1 } ,
[ " default:obsidian_shard " ] = { 199 , " default:lava_source " , 1 } ,
[ " default:mese_crystal " ] = { 8 , " basic_machines:mese_dust_33 2 " , 1 } ,
[ " moreores:mithril_ingot " ] = { 16 , " basic_machines:mithril_dust_33 2 " , 1 } ,
[ " moreores:silver_ingot " ] = { 5 , " basic_machines:silver_dust_33 2 " , 1 } ,
[ " moreores:tin_ingot " ] = { 4 , " basic_machines:tin_dust_33 2 " , 1 } ,
[ " moreores:mithril_lump " ] = { 16 , " basic_machines:mithril_dust_33 2 " , 1 } ,
[ " default:steel_ingot " ] = { 4 , " basic_machines:iron_dust_33 2 " , 1 } ,
[ " moreores:silver_lump " ] = { 5 , " basic_machines:silver_dust_33 2 " , 1 } ,
[ " default:gold_ingot " ] = { 6 , " basic_machines:gold_dust_33 2 " , 1 } ,
[ " default:copper_ingot " ] = { 4 , " basic_machines:copper_dust_33 2 " , 1 } ,
[ " default:gold_lump " ] = { 6 , " basic_machines:gold_dust_33 2 " , 1 } ,
[ " default:iron_lump " ] = { 4 , " basic_machines:iron_dust_33 2 " , 1 } ,
[ " default:copper_lump " ] = { 4 , " basic_machines:copper_dust_33 2 " , 1 } ,
} ,
compressor_recipes = { --[in] ={fuel cost, out, quantity of material required for processing}
[ " default:snow " ] = { 1 , " default:ice " } ,
2018-06-30 12:53:45 +02:00
[ " default:coalblock " ] = { 41 , " default:diamond " } , -- to smelt diamond dust to diamond need 25 coal + 16 for grinder
2017-04-27 09:22:17 +02:00
} ,
}
local chk_machine_level = function ( inv , level ) -- does machine have upgrade to be classified with at least "level"
2017-07-01 23:00:16 +02:00
if level < 1 then level = 1 end
2017-04-27 09:22:17 +02:00
local upg = { " default:diamondblock " , " default:mese " , " default:goldblock " } ;
for i = 1 , # upg do
if not inv : contains_item ( " main " , ItemStack ( upg [ i ] .. " " .. level ) ) then return false end
end
return true
end
2017-07-01 23:00:16 +02:00
2017-04-27 09:22:17 +02:00
basic_robot.commands . machine = {
-- convert fuel into energy
generate_power = function ( name , input , amount ) -- fuel used, if no fuel then amount specifies how much energy builtin generator should produce
2017-08-02 12:48:33 +02:00
check_operations ( name , 1.5 , true )
2017-07-01 23:00:16 +02:00
2017-04-27 09:22:17 +02:00
if amount and amount > 0 then -- attempt to generate power from builtin generator
local pos = basic_robot.data [ name ] . spawnpos ; -- position of spawner block
local inv = minetest.get_meta ( pos ) : get_inventory ( ) ;
local level = amount * 40 ; -- to generate 1 unit ( coal lump per second ) we need at least upgrade 40
if not chk_machine_level ( inv , level ) then error ( " generate_power : tried to generate " .. amount .. " energy requires upgrade level at least " .. level .. " (blocks of mese, diamond, gold ) " ) return end
local data = basic_robot.data [ name ] ;
local energy = ( data.menergy or 0 ) + amount ;
data.menergy = energy ;
return energy ;
end
local energy = 0 ; -- can only do one step at a run time
2017-07-01 23:00:16 +02:00
2017-04-27 09:22:17 +02:00
if string.find ( input , " " ) then return nil , " 1: can convert only one item at once " end
local pos = basic_robot.data [ name ] . spawnpos ; -- position of spawner block
local inv = minetest.get_meta ( pos ) : get_inventory ( ) ;
local stack = ItemStack ( input ) ;
if not inv : contains_item ( " main " , stack ) then return nil , " 2: no input material " end
2017-07-16 23:27:52 +02:00
-- read energy value of input ( coal lump = 1)
2017-04-27 09:22:17 +02:00
local add_energy = basic_robot.technic . fuels [ input ] ;
if not add_energy then -- lookup fuel value
local fueladd , afterfuel = minetest.get_craft_result ( { method = " fuel " , width = 1 , items = { stack } } )
if fueladd.time > 0 then
2017-07-16 23:27:52 +02:00
add_energy = fueladd.time / 40 ; -- fix by kurik
2017-07-01 23:00:16 +02:00
else
return nil , " 3: material can not be used as a fuel "
2017-04-27 09:22:17 +02:00
end
2017-07-16 23:27:52 +02:00
if add_energy > 0 then basic_robot.technic . fuels [ input ] = add_energy end
2017-04-27 09:22:17 +02:00
end
inv : remove_item ( " main " , stack ) ;
--add energy
local data = basic_robot.data [ name ] ; energy = data.menergy or 0 ;
energy = energy + add_energy ; data.menergy = energy
return energy ;
end ,
-- smelting
smelt = function ( name , input , amount ) -- input material, amount of energy used for smelt
local energy = 0 ; -- can only do one step at a run time
2017-08-02 12:48:33 +02:00
check_operations ( name , 2 , true )
2017-04-27 09:22:17 +02:00
if string.find ( input , " " ) then return nil , " 0: only one item per smelt " end
local pos = basic_robot.data [ name ] . spawnpos ; -- position of spawner block
local meta = minetest.get_meta ( pos ) ;
local inv = minetest.get_meta ( pos ) : get_inventory ( ) ;
--read robot energy
local cost = 1 / 40 ;
local smelttimeboost = 1 ;
local level = 1 ;
if amount and amount > 0 then
level = amount * 10 ; -- 10 level required for 1 of amount
if not chk_machine_level ( inv , level ) then
error ( " 3 smelting: need at least level " .. level .. " upgrade for required power " .. amount ) ;
return
end
cost = cost * ( 1 + amount ) ;
smelttimeboost = smelttimeboost + amount ; -- double speed with amount 1
end
local data = basic_robot.data [ name ]
energy = data.menergy or 0 ; -- machine energy
2017-07-01 23:00:16 +02:00
if energy < cost then return nil , " 1: not enough energy " end
2017-04-27 09:22:17 +02:00
local stack = ItemStack ( input ) ;
if not inv : contains_item ( " main " , stack ) then return nil , " 2: no input materials " end
local src_time = ( data.src_time or 0 ) + smelttimeboost ;
-- get smelting data
2017-07-01 23:00:16 +02:00
local smelts = basic_robot.technic . smelts [ input ] ;
2017-04-27 09:22:17 +02:00
if not smelts then
local cooked , aftercooked ;
cooked , aftercooked = minetest.get_craft_result ( { method = " cooking " , width = 1 , items = { stack } } )
if cooked.time > 0 then
2017-07-01 23:00:16 +02:00
basic_robot.technic . smelts [ input ] = { cooked.time , cooked.item , aftercooked.items [ 1 ] } ;
smelts = basic_robot.technic . smelts [ input ] ;
else
return nil , " 3: material can not be smelted "
2017-04-27 09:22:17 +02:00
end
end
local cooktime = smelts [ 1 ] ; local cookeditem = smelts [ 2 ] ; local aftercookeditem = smelts [ 3 ]
-- is smelting done?
data.menergy = energy - cost ;
if src_time >= cooktime then
inv : remove_item ( " main " , stack ) ;
inv : add_item ( " main " , ItemStack ( aftercookeditem ) ) ;
inv : add_item ( " main " , ItemStack ( cookeditem ) ) ;
data.src_time = 0
return true
else
data.src_time = src_time
return math.floor ( src_time / cooktime * 100 * 100 ) / 100
end
end ,
-- grind
grind = function ( name , input )
--[in] ={fuel cost, out, quantity of material required for processing}
local recipe = basic_robot.technic . grinder_recipes [ input ] ;
if not recipe then return nil , " unknown recipe " end
local cost = recipe [ 1 ] ; local output = recipe [ 2 ] ;
local pos = basic_robot.data [ name ] . spawnpos ; -- position of spawner block
local meta = minetest.get_meta ( pos ) ;
local inv = minetest.get_meta ( pos ) : get_inventory ( ) ;
--level requirement
2017-07-01 23:00:16 +02:00
local level = math.floor ( ( cost - 1 ) / 3 ) ;
2017-04-27 09:22:17 +02:00
if not chk_machine_level ( inv , level ) then error ( " 0: tried to grind " .. input .. " requires upgrade level at least " .. level ) return end
local stack = ItemStack ( input ) ;
if not inv : contains_item ( " main " , stack ) then return nil , " 1: missing input material " end
local data = basic_robot.data [ name ] ;
local energy = data.menergy or 0 ;
if energy < cost then return nil , " 2: low energy " .. energy .. " / " .. cost end
data.menergy = energy - cost
inv : remove_item ( " main " , ItemStack ( input ) )
inv : add_item ( " main " , ItemStack ( output ) ) ;
return true
end ,
-- compress
compress = function ( name , input )
--[in] ={fuel cost, out, quantity of material required for processing}
local recipe = basic_robot.technic . compressor_recipes [ input ] ;
if not recipe then return nil , " unknown recipe " end
local cost = recipe [ 1 ] ; local output = recipe [ 2 ] ;
local pos = basic_robot.data [ name ] . spawnpos ; -- position of spawner block
local meta = minetest.get_meta ( pos ) ;
local inv = minetest.get_meta ( pos ) : get_inventory ( ) ;
--level requirement
local level = math.floor ( cost / 2 )
if not chk_machine_level ( inv , level ) then error ( " tried to compress " .. input .. " requires upgrade level at least " .. level ) return end
local stack = ItemStack ( input ) ;
if not inv : contains_item ( " main " , stack ) then return nil , " 1: missing input material " end
local data = basic_robot.data [ name ] ;
local energy = data.menergy or 0 ;
if energy < cost then return nil , " 2: low energy " .. energy .. " / " .. cost end
data.menergy = energy - cost
inv : remove_item ( " main " , ItemStack ( input ) )
inv : add_item ( " main " , ItemStack ( output ) ) ;
return true
end ,
transfer_power = function ( name , amount , target )
local pos = basic_robot.data [ name ] . spawnpos ;
local data = basic_robot.data [ name ] ;
local tdata = basic_robot.data [ target ] ;
if not tdata then return nil , " target inactive " end
local energy = 0 ; -- can only do one step at a run time
2017-08-02 12:48:33 +02:00
check_operations ( name , 0.5 , true ) ;
2017-04-27 09:22:17 +02:00
energy = data.menergy or 0 ;
if amount > energy then return nil , " energy too low " end
if not tdata.menergy then tdata.menergy = 0 end
tdata.menergy = tdata.menergy + amount
2017-07-01 23:00:16 +02:00
data.menergy = energy - amount ;
return true
2017-04-27 09:22:17 +02:00
end ,
2017-07-18 14:15:53 +02:00
}
-- CRPYTOGRAPHY
-- rnd 2017
-- nonlinear block stream cypher encryption with scrambling
local scramble = function ( input , password , sgn ) -- permutes text randomly, nice after touch to stream cypher to prevent block analysis
_G.math . randomseed ( password ) ;
local n = # input ;
local permute = { }
for i = 1 , n do permute [ i ] = i end --input:sub(i, i)
for i = n , 2 , - 1 do
local j = math.random ( i - 1 ) ;
local tmp = permute [ j ] ;
permute [ j ] = permute [ i ] ; permute [ i ] = tmp ;
end
local out = { } ;
if sgn > 0 then -- unscramble
for i = 1 , n do out [ permute [ i ] ] = string.sub ( input , i , i ) end
else -- scramble
for i = 1 , n do out [ i ] = string.sub ( input , permute [ i ] , permute [ i ] ) end
end
return table.concat ( out , " " )
end
local scramble_test = function ( )
local text = " testing scrambling 1 2 3 " ;
local enc = scramble ( text , 10 , 1 ) ; -- scramble
local dec = scramble ( enc , 10 , - 1 ) ; -- descramble
say ( " SCRAMBLED--> " .. enc .. " DESCRAMBLED--> " .. dec )
end
--scramble_test()
local get_hash = function ( s , p ) -- basic modular hash, first convert string into 4*8=32 bit int
if not s then return end
local h = 0 ; local n = string.len ( s ) ; local m = 4 ; -- put 4 characters together
local r = 0 ; local i = 0 ;
while i < n do
i = i + 1 ; r = 256 * r + string.byte ( s , i ) ;
if i % m == 0 then h = h + ( r % p ) r = 0 end
end
if i % m ~= 0 then h = h + ( r % p ) end
return h % p
end
local encrypt_ = function ( input , password , sgn ) -- nonlinear stream cypher with extra block offsets
local n = 16 ; -- range 0-255 (for just chat can use 32 - 132)
local m = 65 ;
local ret = " " ; input = input or " " ;
local rndseed = get_hash ( password , 10 ^ 30 ) ;
_G.math . randomseed ( rndseed ) ;
2017-07-18 14:54:15 +02:00
local block_offset = 1 + math.random ( n ) ;
local offset = 1 + math.random ( n ) ;
2017-07-18 14:15:53 +02:00
for i = 1 , string.len ( input ) do
2017-07-18 14:54:15 +02:00
offset = math.random ( n + math.random ( 2 + ( i + offset + block_offset ) ^ 2 ) ) ; -- yay, nested nonlinearity is fun and makes cryptanalysis 'trivial' hehe
2017-08-02 12:48:33 +02:00
if i % 8 == 1 then -- every 8 characters new offset using strong hash function incorporating recent offsets in nonlinear way
2017-07-18 14:54:15 +02:00
block_offset = get_hash ( _G.minetest . get_password_hash ( " " , i * ( offset + 1 ) .. password .. ( block_offset ^ 2 ) ) , n ) ; -- composite fun with more serious hash function
math.randomseed ( rndseed + block_offset ) -- time for change of tune, can you keep up ? :)
if math.random ( 100 ) > 50 then block_offset = block_offset * math.random ( n * ( 1 + block_offset ) ) end -- extra fun, why not
2017-07-18 14:15:53 +02:00
end
2017-07-18 14:54:15 +02:00
offset = ( offset + block_offset ) % n ;
2017-07-18 14:15:53 +02:00
local c = string.byte ( input , i ) - m ;
c = m + ( ( c + offset * sgn ) % n ) ;
ret = ret .. string.char ( c )
end
return ret
end
local ascii2hex = function ( input ) -- compress 256 char set to 16 charset range
local ret = " "
for i = 1 , string.len ( input ) do
local c = string.byte ( input , i ) ; -- c = c2*16+c1
local c1 = c % 16 ;
local c2 = ( c - c1 ) / 16 ;
ret = ret .. ( string.char ( c1 + 65 ) .. string.char ( c2 + 65 ) )
end
return ret ;
end
local hex2ascii = function ( input )
local ret = " "
if string.len ( input ) % 2 == 1 then input = input .. " A " end -- padding
for i = 1 , string.len ( input ) , 2 do
local c1 = string.byte ( input , i ) - 65 ; -- c = c2*16+c1
local c2 = string.byte ( input , i + 1 ) - 65 ;
ret = ret .. string.char ( c2 * 16 + c1 )
end
return ret ;
end
-- scheme: encrypt: (stream cypher) encrypt+scramble, decrypt: unscramble+decrypt
local encrypt = function ( input , password )
local ret = encrypt_ ( ascii2hex ( input ) , password , 1 ) ;
local scrambleseed = get_hash ( _G.minetest . get_password_hash ( " " , password ) , 10 ^ 30 ) ; -- strong hash from password, 10^30 possible permutes
return scramble ( ret , scrambleseed , 1 ) ;
end
local decrypt = function ( input , password )
local scrambleseed = get_hash ( _G.minetest . get_password_hash ( " " , password ) , 10 ^ 30 ) ;
input = scramble ( input , scrambleseed , - 1 ) ; -- descramble
return hex2ascii ( encrypt_ ( input , password , - 1 ) )
end
-- just test
local encrypt_test = function ( )
local text = " testing encryption 1 2 3 " ; local password = " hello encryptions " ;
local enc = encrypt ( text , password ) ; local dec = decrypt ( enc , password ) ;
say ( " INPUT: " .. text .. " ENC: " .. enc .. " DEC: " .. dec )
end
2017-10-21 15:40:52 +02:00
basic_robot.commands . crypto = { encrypt = encrypt , decrypt = decrypt , scramble = scramble , basic_hash = get_hash }
-- PUZZLE GAMEPLAY - need puzzle privs
local is_same_block = function ( pos1 , pos2 )
local round = math.floor ;
2017-12-18 11:19:05 +01:00
local r = basic_robot.radius ; local ry = 2 * r ; -- note: this is skyblock adjusted
2017-10-21 15:40:52 +02:00
local ppos1 = { round ( pos1.x / r + 0.5 ) * r , round ( pos1.y / ry + 0.5 ) * ry , round ( pos1.z / r + 0.5 ) * r } ;
local ppos2 = { round ( pos2.x / r + 0.5 ) * r , round ( pos2.y / ry + 0.5 ) * ry , round ( pos2.z / r + 0.5 ) * r } ;
return ppos1 [ 1 ] == ppos2 [ 1 ] and ppos1 [ 2 ] == ppos2 [ 2 ] and ppos1 [ 3 ] == ppos2 [ 3 ]
end
local cmd_set_node = function ( data , pos , node )
if minetest.is_protected ( pos , data.owner ) then return end
local spos = data.spawnpos ;
if not is_same_block ( pos , spos ) then return end -- only allow to edit same block as spawner is in
minetest.swap_node ( pos , node )
end
local cmd_get_node_inv = function ( data , pos )
local spos = data.spawnpos ;
if minetest.is_protected ( pos , data.owner ) then return end
if not is_same_block ( pos , spos ) then return end
return minetest.get_meta ( pos ) : get_inventory ( )
end
local cmd_get_player = function ( data , pname ) -- return player for further manipulation
local player = minetest.get_player_by_name ( pname )
if not player then return end
local spos = data.spawnpos ;
local ppos = player : getpos ( ) ;
if not is_same_block ( ppos , spos ) then return end
return player
end
local cmd_get_player_inv = function ( data , pname )
local player = minetest.get_player_by_name ( pname )
if not player then return end
local spos = data.spawnpos ;
local ppos = player : getpos ( ) ;
if not is_same_block ( ppos , spos ) then return end
return player : get_inventory ( ) ;
end
-- spatial triggers with hashing
local trigger_range = 5 -- how close player must be to "activate" - also size/2 of cells
local round = math.floor
local cmd_get_pos_id = function ( data , pos , no_neighbors ) -- return 4 nearby block ids
local r = trigger_range * 2 ;
local range = 1000 * 2 ; -- coordinates from -1000 to +1000 allowed
local n = range / r ;
local x1 = round ( pos.x / r + 0.5 )
local z1 = round ( pos.z / r + 0.5 )
local y1 = round ( pos.y / r + 0.5 )
local baseid = x1 + z1 * n + y1 * n ^ 2 -- hash value
if no_neighbors then return baseid end
--check 4 nearby closest squares: 2D
local x0 = round ( pos.x / r ) ;
local z0 = round ( pos.z / r ) ;
if x0 < x1 and z0 < z1 then -- lower left
data.block_ids = { baseid , baseid - 1 - n , baseid - n , baseid - 1 } ;
return
elseif x0 == x1 and z0 < z1 then
data.block_ids = { baseid , baseid - n , baseid + 1 - n , baseid + 1 }
return
elseif x0 == x1 and z0 == z1 then
data.block_ids = { baseid , baseid + 1 , baseid + 1 + n , baseid + n }
return
else -- upper left
data.block_ids = { baseid , baseid - 1 , baseid - 1 + n , baseid + n }
return
end
end
local cmd_set_triggers = function ( pdata , triggers )
pdata.triggers = { } ;
local dtriggers = pdata.triggers ;
for i = 1 , # triggers do
dtriggers [ i ] = triggers [ i ] ;
end
-- init triggerdata
pdata.triggerdata = { } ;
local triggerdata = pdata.triggerdata ;
for i = 1 , # triggers do
local data = triggers [ i ] ;
local id = cmd_get_pos_id ( nil , data.pos , true ) ;
local tdata = triggerdata [ id ] ;
if not tdata then triggerdata [ id ] = { } ; tdata = triggerdata [ id ] end
tdata [ # tdata + 1 ] = i ; -- add index (=id)
triggers [ i ] . init ( i ) -- initialize trigger
end
end
local cmd_checkpos = function ( data , pos , pname ) -- does the position pos trigger any triggers?
cmd_get_pos_id ( data , pos ) ; -- we dont init new table structure every time but store ids in block_ids
local block_ids = data.block_ids ;
local gamedata = data.gamedata ;
local triggerdata = data.triggerdata ;
local triggers = data.triggers ;
for j = 1 , 4 do -- check 4 nearby blocks cause we could be near border
local block_id = block_ids [ j ] ;
local gdata = gamedata ;
local tdata = triggerdata [ block_id ] ; -- list of trigger indices in this block
if tdata then
for i = 1 , # tdata do -- check all triggers inside block
local trigger = triggers [ tdata [ i ] ] ;
local id = tdata [ i ] ; -- trigger id
if trigger.onetime and gdata [ id ] then -- trigger already "triggered"
else
--say("trigger " .. id)
trigger.action ( pname , id )
end
end
end
end
end
-- PUZZLE COMMANDS
basic_robot.commands . puzzle = {
set_triggers = cmd_set_triggers , checkpos = cmd_checkpos , set_node = cmd_set_node , get_node_inv = cmd_get_node_inv , get_player_inv = cmd_get_player_inv ,
get_player = cmd_get_player ,
get_meta = function ( data , pos )
local spos = data.spawnpos ;
if minetest.is_protected ( pos , data.owner ) then return end
if not is_same_block ( pos , spos ) then return end
if minetest.get_node ( pos ) . name == " basic_robot:spawner " then return end
2018-06-28 17:38:46 +02:00
local meta = minetest.get_meta ( pos ) ;
if not meta then error ( " get_meta in puzzle returned nil " ) ; return end
return meta
2017-10-21 15:40:52 +02:00
end ,
get_gametime = function ( ) return minetest.get_gametime ( ) end ,
activate = function ( data , mode , tpos )
local spos = data.spawnpos ;
if minetest.is_protected ( tpos , data.owner ) then return end
if not is_same_block ( tpos , spos ) then return end
local node = minetest.get_node ( tpos ) ;
if node.name == " default:furnace " or node.name == " default:furnace_active " then
if mode > 0 then robot_activate_furnace ( tpos ) end
return true
end
local table = minetest.registered_nodes [ node.name ] ;
if table and table.mesecons and table.mesecons . effector then
else
return false
end -- error
local effector = table.mesecons . effector ;
if not mode then mode = 1 end
if mode > 0 then
if not effector.action_on then return false end
effector.action_on ( tpos , node , 16 )
elseif mode < 0 then
if not effector.action_off then return false end
effector.action_off ( tpos , node , 16 )
end
return true
end ,
2018-02-21 10:54:22 +00:00
}
-- VIRTUAL PLAYER --
local Vplayer = { } ;
function Vplayer : new ( name ) -- constructor
if not basic_robot.data [ name ] . obj then return end -- only make it for existing robot
if basic_robot.virtual_players [ name ] then return end -- already exists
local o = { }
setmetatable ( o , self )
self.__index = self
o.obj = basic_robot.data [ name ] . obj ;
o.data = basic_robot.data [ name ] ;
local spawnpos = o.data . spawnpos ;
local meta = minetest.get_meta ( spawnpos ) ; if not meta then return end
o.inv = meta : get_inventory ( ) ;
basic_robot.virtual_players [ name ] = o ;
end
-- functions
function Vplayer : getpos ( ) return self.obj : getpos ( ) end
function Vplayer : remove ( ) end
function Vplayer : setpos ( ) end
function Vplayer : move_to ( ) end
function Vplayer : punch ( ) end
function Vplayer : rightlick ( ) end
function Vplayer : get_hp ( ) return 20 end
function Vplayer : set_hp ( ) return 20 end
function Vplayer : get_inventory ( ) return self.inv end
function Vplayer : get_wield_list ( ) return " main " end
function Vplayer : get_wield_index ( ) return 1 end
function Vplayer : get_wielded_item ( ) return self.inv : get_stack ( " main " , 1 ) end
function Vplayer : set_wielded_item ( ) end
function Vplayer : set_armor_groups ( ) end
function Vplayer : get_armor_groups ( ) return { fleshy = 100 } end
function Vplayer : set_animation ( ) end
function Vplayer : get_animation ( ) end
function Vplayer : set_attach ( ) end
function Vplayer : get_attach ( ) end
function Vplayer : set_detach ( ) end
function Vplayer : set_bone_position ( ) end
function Vplayer : get_bone_position ( ) end
function Vplayer : set_properties ( ) end
function Vplayer : get_properties ( ) end
function Vplayer : is_player ( ) return true end
function Vplayer : get_nametag_attributes ( ) end
function Vplayer : set_nametag_attributes ( ) end
function Vplayer : set_velocity ( ) end
function Vplayer : get_velocity ( ) end
function Vplayer : set_acceleration ( ) end
function Vplayer : get_acceleration ( ) end
function Vplayer : set_yaw ( ) end
function Vplayer : get_yaw ( ) end
function Vplayer : set_texture_mod ( ) end
function Vplayer : get_luaentity ( ) end
function Vplayer : get_player_name ( ) return self.data . name end
function Vplayer : get_player_velocity ( ) return { x = 0 , y = 0 , z = 0 } end
function Vplayer : get_look_dir ( ) return { x = 1 , y = 0 , z = 0 } end
function Vplayer : get_look_vertical ( ) return 0 end
function Vplayer : get_look_horizontal ( ) return 0 end
function Vplayer : set_look_vertical ( ) end
function Vplayer : set_look_horizontal ( ) end
function Vplayer : get_breath ( ) return 1 end
function Vplayer : set_breath ( ) end
function Vplayer : set_attribute ( ) end
function Vplayer : get_attribute ( ) end
function Vplayer : set_inventory_formspec ( ) end
function Vplayer : get_inventory_formspec ( ) return " " end
function Vplayer : get_player_control ( ) return { } end
function Vplayer : get_player_control_bits ( ) return 0 end
function Vplayer : set_physics_override ( ) end
function Vplayer : get_physics_override ( ) return { } end
function Vplayer : hud_add ( ) end
function Vplayer : hud_remove ( ) end
function Vplayer : hud_change ( ) end
function Vplayer : hud_get ( ) end
function Vplayer : hud_set_flags ( ) end
function Vplayer : hud_get_flags ( ) return { } end
function Vplayer : hud_set_hotbar_itemcount ( ) end
function Vplayer : hud_get_hotbar_itemcount ( ) return 0 end
function Vplayer : hud_set_hotbar_image ( ) end
function Vplayer : hud_get_hotbar_image ( ) return " " end
function Vplayer : hud_set_hotbar_selected_image ( ) end
function Vplayer : hud_get_hotbar_selected_image ( ) return " " end
function Vplayer : set_sky ( ) end
function Vplayer : get_sky ( ) end
function Vplayer : set_clouds ( ) end
function Vplayer : get_clouds ( ) end
function Vplayer : override_day_night_ratio ( ) end
function Vplayer : get_day_night_ratio ( ) end
function Vplayer : set_local_animation ( ) end
function Vplayer : get_local_animation ( ) end
function Vplayer : set_eye_offset ( ) end
function Vplayer : get_eye_offset ( ) end
-- code for act borrowed from: https://github.com/minetest-mods/pipeworks/blob/fa4817136c8d1e62dafd6ab694821cba255b5206/wielder.lua, line 372