diff --git a/init.lua b/init.lua index 92b86ca..8defba6 100644 --- a/init.lua +++ b/init.lua @@ -25,8 +25,9 @@ basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes invento basic_robot.http_api = minetest.request_http_api(); -basic_robot.version = "2018/07/23a"; +basic_robot.version = "2018/07/27a"; +basic_robot.gui = {}; local robogui = basic_robot.gui -- gui management basic_robot.data = {}; -- stores all robot related data --[[ [name] = { sandbox= .., bytecode = ..., ram = ..., obj = robot object, spawnpos= ..., authlevel = ... , t = code execution time} @@ -38,6 +39,8 @@ basic_robot.ids = {}; -- stores maxid for each player basic_robot.virtual_players = {}; -- this way robot can interact with the world as "player" TODO basic_robot.data.listening = {}; -- which robots listen to chat + +dofile(minetest.get_modpath("basic_robot").."/robogui.lua") -- gui stuff dofile(minetest.get_modpath("basic_robot").."/commands.lua") local check_code, preprocess_code,is_inside_string; @@ -1231,113 +1234,6 @@ local despawn_robot = function(pos) end --- GUI - --- robogui GUI START ================================================== -robogui = {}; -- a simple table of entries: [guiName] = {getForm = ... , show = ... , response = ... , guidata = ...} -robogui.register = function(def) - robogui[def.guiName] = {getForm = def.getForm, show = def.show, response = def.response, guidata = def.guidata or {}} -end -minetest.register_on_player_receive_fields( - function(player, formname, fields) - local gui = robogui[formname]; - if gui then gui.response(player,formname,fields) end - end -) --- robogui GUI END ==================================================== - - - ---- DEMO of simple form registration, all in one place, clean and tidy --- adapted for use with basic_robot - - --- if not basic_gui then - -- basic_gui = _G.basic_gui; minetest = _G.minetest; - -- basic_gui.register({ - -- guiName = "mainWindow", -- formname - - -- getForm = function(form_id, update) -- actual form design - -- local gui = basic_gui["mainWindow"]; - -- local formdata = gui.guidata[form_id] - - -- if not formdata then -- init - -- gui.guidata[form_id] = {}; formdata = gui.guidata[form_id] - -- formdata.sel_tab = 1; - -- formdata.text = "default"; - -- formdata.form = ""; - -- end - -- if not update then return formdata.form end - - -- local sel_tab = formdata.sel_tab; - -- local text = formdata.text; - - -- formdata.form = "size[8,9]".. - -- "label[0,0;basic_gui_DEMO, form_id " .. form_id .. ", tab " .. sel_tab .. "]".. - -- "button[0,1;2,1;gui_button;CLICK ME]".. - -- "textarea[0.25,2;2,1;gui_textarea;text;" .. text .. "]".. - -- "tabheader[0,0;tabs;tab1,table demo,tab3;".. sel_tab .. ";true;true]".. - -- "list[current_player;main;0,5;8,4;]"; - - -- if sel_tab == 2 then - -- formdata.form = "size[12,6.5;true]" .. - -- "tablecolumns[color;tree;text,width=32;text]" .. - -- "tableoptions[background=#00000000;border=false]" .. - -- "field[0.3,0.1;10.2,1;search_string;;" .. minetest.formspec_escape(text) .. "]" .. - -- "field_close_on_enter[search_string;false]" .. - -- "button[10.2,-0.2;2,1;search;" .. "Search" .. "]" .. - -- "table[0,0.8;12,4.5;list_settings;".. - -- "#FFFF00,1,TEST A,,".. - -- "#FFFF00,2,TEST A,,".. - -- ",3,test a,value A,".. - -- "#FFFF00,1,TEST B,,".. - -- ",2,test b,value B," - -- end - - -- formdata.info = "This information comes with the form post"; -- extra data set - -- end, - - -- show = function(player_name,update) -- this is used to show form to user - -- local formname = "mainWindow"; - -- local form_id = player_name; -- each player has his own window! - -- local gui = basic_gui[formname]; - -- local formdata = gui.guidata[form_id]; -- all form data for this id gets stored here - -- if update then gui.getForm(form_id,true); formdata = gui.guidata[form_id]; end - -- minetest.show_formspec(player_name, "mainWindow", formdata.form) - -- end, - - -- response = function(player,formname, fields) -- this handles response - - -- local player_name = player:get_player_name(); - -- local form_id = player_name; - -- local gui = basic_gui[formname]; - -- local formdata = gui.guidata[form_id]; --gui.guidata[form_id]; - -- if not formdata then say("err") return end --error! - - -- if fields.gui_textarea then - -- formdata.text = fields.gui_textarea or "" - -- end - - - -- if fields.tabs then - -- formdata.sel_tab = tonumber(fields.tabs) or 1; - - -- gui.show(player_name,true) -- update and show form - -- else - - -- local form = "size[5,5]" .. - -- "label[0,0;you interacted with demo form, fields : " .. - -- _G.minetest.formspec_escape(_G.dump(fields)) .. "]".. - -- "label[0,4;" .. formdata.info .. "]" - -- _G.minetest.show_formspec(player_name,"basic_response", form); - -- end - -- end, - - --- }) --- end - - --process forms from spawner local on_receive_robot_form = function(pos, formname, fields, sender) @@ -1400,105 +1296,7 @@ local on_receive_robot_form = function(pos, formname, fields, sender) end if fields.help then ----- INGAME HELP ------ - - local text = "BASIC LUA SYNTAX\n \nif x==1 then A else B end".. - "\n for i = 1, 5 do something end \nwhile i<6 do A; i=i+1; end\n".. - "\n arrays: myTable1 = {1,2,3}, myTable2 = {[\"entry1\"]=5, [\"entry2\"]=1}\n".. - " access table entries with myTable1[1] or myTable2.entry1 or myTable2[\"entry1\"]\n \n".. - "ROBOT COMMANDS\n \n".. - "**MOVEMENT,DIGGING, PLACING, INVENTORY TAKE/INSERT\n move.direction(), where direction is forward, backward, left,right, up, down)\n".. - " left_down, ..., backward_down, left_up, ..., backward_up\n".. - " boost(v) sets robot velocity, -60 it returns itemname. if itemname == \"\" it checks if inventory empty\n".. - " activate.direction(mode) activates target block\n".. - " pickup(r) picks up all items around robot in radius r<8 and returns list or nil\n".. - " craft(item,idx,mode) crafts item if required materials are present in inventory. mode = 1 returns recipe, optional recipe idx\n".. - " take.direction(item, inventory) takes item from target inventory into robot inventory\n".. - " read_text.direction(stringname,mode) reads text of signs, chests and other blocks, optional stringname for other meta,\n mode 1 read number\n".. - " write_text.direction(text,mode) writes text to target block as infotext\n".. - "**BOOKS/CODE\n title,text=book.read(i) returns title,contents of book at i-th position in library \n book.write(i,title,text) writes book at i-th position at spawner library\n".. - " code.run(text) compiles and runs the code in sandbox (privs only)\n".. - " code.set(text) replaces current bytecode of robot\n".. - " find_nodes(\"default:dirt\",3) returns distance to node in radius 3 around robot, or false if none\n".. - " string.concat({string1,string2,...}, separator) returns concatenated string. maxlength 1024\n".. - "**PLAYERS\n".. - " find_player(3,pos) finds players in radius 3 around robot(position) and returns list, if none returns nil\n".. - " attack(target) attempts to attack target player if nearby \n".. - " grab(target) attempt to grab target player if nearby and returns true if succesful \n".. - " player.getpos(name) return position of player, player.connected() returns list of players\n".. - "**ROBOT\n".. - " say(\"hello\") will speak\n".. - " self.listen(0/1) (de)attaches chat listener to robot\n".. - " speaker, msg = self.listen_msg() retrieves last chat message if robot listens\n".. - " self.send_mail(target,mail) sends mail to target robot\n".. - " sender,mail = self.read_mail() reads mail, if any\n" .. - " self.pos() returns table {x=pos.x,y=pos.y,z=pos.z}\n".. - " self.name() returns robot name\n".. - " self.operations() returns remaining robot operations\n".. - " self.set_properties({textures=.., visual=..,visual_size=.., , ) sets visual appearance\n".. - " set_animation(anim_start,anim_end,anim_speed,anim_stand_start) set mesh animation \n".. - " self.spam(0/1) (dis)enable message repeat to all\n".. - " self.remove() stops program and removes robot object\n".. - " self.reset() resets robot position\n".. - " self.spawnpos() returns position of spawner block\n".. - " self.viewdir() returns vector of view for robot\n".. - " self.fire(speed, pitch,gravity, texture, is_entity) fires a projectile from robot\n".. - " self.fire_pos() returns last hit position\n".. - " self.label(text) changes robot label\n".. - " self.display_text(text,linesize,size) displays text instead of robot face, if no size return tex\n".. - " self.sound(sample,volume, opt. pos) plays sound named 'sample' at robot location (opt. pos)\n".. - " rom is aditional table that can store persistent data, like rom.x=1\n".. - "**KEYBOARD : place spawner at coordinates (20i,40j+1,20k) to monitor events\n".. - " keyboard.get() returns table {x=..,y=..,z=..,puncher = .. , type = .. } for keyboard event\n".. - " keyboard.set(pos,type) set key at pos of type 0=air,1-6,7-15,16-271, limited to range 10 around\n".. - " keyboard.read(pos) return node name at pos\n".. - "**TECHNIC FUNCTIONALITY: namespace 'machine'. most functions return true or nil, error\n" .. - " energy() displays available energy\n".. - " generate_power(fuel, amount) = energy, attempt to generate power from fuel material,\n" .. - " if amount>0 try generate amount of power using builtin generator - this requires\n" .. - " 40 gold/mese/diamonblock upgrades for each 1 amount\n".. - " smelt(input,amount) = progress/true. works as a furnace, if amount>0 try to\n" .. - " use power to smelt - requires 10 upgrades for each 1 amount, energy cost is:\n".. - " 1/40*(1+amount)\n".. - " grind(input) - grinds input material, requires upgrades for harder material\n".. - " compress(input) - requires upgrades - energy intensive process\n" .. - " transfer_power(amount,target_robot_name)\n".. - "**CRYPTOGRAPHY: namespace 'crypto'\n".. - " encrypt(input,password) returns encrypted text, password is any string \n".. - " decrypt(input,password) attempts to decrypt encrypted text\n".. - " scramble(input,randomseed,sgn) (de)permutes text randomly according to sgn = -1,1\n".. - " basic_hash(input,n) returns simple mod hash from string input within range 0...n-1\n".. - "**PUZZLE: namespace 'puzzle' - need puzzle priv\n".. - " set_triggers({trigger1, trigger2,...}) sets and initializes spatial triggers\n".. - " check_triggers(pname) check if player is close to any trigger and run that trigger\n".. - " set_node(pos,node) - set any node, limited to current protector mapblock & get_node(pos)\n".. - " get_player(pname) return player objRef in current mapblock\n".. - " chat_send_player(pname, text) \n".. - " get_node_inv(pos) / get_player_inv(pname) - return inventories of nodes/players in current mapblock\n".. - " get_meta(pos) - return meta of target position\n".. - " get_gametime() - return current gametime\n".. - " ItemStack(itemname) returns ItemRef to be used with inventory\n".. - " count_objects(pos,radius)\n".. - " pdata contains puzzle data like .triggers and .gamedata\n".. - " add_particle(def)\n" - - - text = minetest.formspec_escape(text); - - --local form = "size [8,7] textarea[0,0;8.5,8.5;help;HELP;".. text.."]" - - --textlist[X,Y;W,H;name;listelem 1,listelem 2,...,listelem n] - local list = ""; - for word in string.gmatch(text, "(.-)\r?\n+") do list = list .. word .. ", " end - local form = "size [10,8] textlist[-0.25,-0.25;10.25,8.5;help;" .. list .. "]" - minetest.show_formspec(sender:get_player_name(), "robot_help", form); - + robogui["robot_help"].show(name) return end @@ -1590,19 +1388,10 @@ local on_receive_robot_form = function(pos, formname, fields, sender) end end - + -- handle form: when rightclicking robot entity, remote controller --- minetest.register_on_player_receive_fields( - -- function(player, formname, fields) - -- local gui = robogui[formname]; - -- if gui then gui.response(player,formname,fields) end - -- end --- ) - - - minetest.register_on_player_receive_fields( function(player, formname, fields) diff --git a/robogui.lua b/robogui.lua new file mode 100644 index 0000000..e058526 --- /dev/null +++ b/robogui.lua @@ -0,0 +1,341 @@ +local robogui = basic_robot.gui; + +-- GUI + +-- robogui GUI START ================================================== +-- a simple table of entries: [guiName] = {getForm = ... , show = ... , response = ... , guidata = ...} +robogui.register = function(def) + robogui[def.guiName] = {getForm = def.getForm, show = def.show, response = def.response, guidata = def.guidata or {}} +end +minetest.register_on_player_receive_fields( + function(player, formname, fields) + local gui = robogui[formname]; + if gui then gui.response(player,formname,fields) end + end +) +-- robogui GUI END ==================================================== + + +--- DEMO of simple form registration, all in one place, clean and tidy +-- adapted for use with basic_robot + + +-- if not basic_gui then + -- basic_gui = _G.basic_gui; minetest = _G.minetest; + -- basic_gui.register({ + -- guiName = "mainWindow", -- formname + + -- getForm = function(form_id, update) -- actual form design + -- local gui = basic_gui["mainWindow"]; + -- local formdata = gui.guidata[form_id] + + -- if not formdata then -- init + -- gui.guidata[form_id] = {}; formdata = gui.guidata[form_id] + -- formdata.sel_tab = 1; + -- formdata.text = "default"; + -- formdata.form = ""; + -- end + -- if not update then return formdata.form end + + -- local sel_tab = formdata.sel_tab; + -- local text = formdata.text; + + -- formdata.form = "size[8,9]".. + -- "label[0,0;basic_gui_DEMO, form_id " .. form_id .. ", tab " .. sel_tab .. "]".. + -- "button[0,1;2,1;gui_button;CLICK ME]".. + -- "textarea[0.25,2;2,1;gui_textarea;text;" .. text .. "]".. + -- "tabheader[0,0;tabs;tab1,table demo,tab3;".. sel_tab .. ";true;true]".. + -- "list[current_player;main;0,5;8,4;]"; + + -- if sel_tab == 2 then + -- formdata.form = "size[12,6.5;true]" .. + -- "tablecolumns[color;tree;text,width=32;text]" .. + -- "tableoptions[background=#00000000;border=false]" .. + -- "field[0.3,0.1;10.2,1;search_string;;" .. minetest.formspec_escape(text) .. "]" .. + -- "field_close_on_enter[search_string;false]" .. + -- "button[10.2,-0.2;2,1;search;" .. "Search" .. "]" .. + -- "table[0,0.8;12,4.5;list_settings;".. + -- "#FFFF00,1,TEST A,,".. + -- "#FFFF00,2,TEST A,,".. + -- ",3,test a,value A,".. + -- "#FFFF00,1,TEST B,,".. + -- ",2,test b,value B," + -- end + + -- formdata.info = "This information comes with the form post"; -- extra data set + -- end, + + -- show = function(player_name,update) -- this is used to show form to user + -- local formname = "mainWindow"; + -- local form_id = player_name; -- each player has his own window! + -- local gui = basic_gui[formname]; + -- local formdata = gui.guidata[form_id]; -- all form data for this id gets stored here + -- if update then gui.getForm(form_id,true); formdata = gui.guidata[form_id]; end + -- minetest.show_formspec(player_name, "mainWindow", formdata.form) + -- end, + + -- response = function(player,formname, fields) -- this handles response + + -- local player_name = player:get_player_name(); + -- local form_id = player_name; + -- local gui = basic_gui[formname]; + -- local formdata = gui.guidata[form_id]; --gui.guidata[form_id]; + -- if not formdata then say("err") return end --error! + + -- if fields.gui_textarea then + -- formdata.text = fields.gui_textarea or "" + -- end + + + -- if fields.tabs then + -- formdata.sel_tab = tonumber(fields.tabs) or 1; + + -- gui.show(player_name,true) -- update and show form + -- else + + -- local form = "size[5,5]" .. + -- "label[0,0;you interacted with demo form, fields : " .. + -- _G.minetest.formspec_escape(_G.dump(fields)) .. "]".. + -- "label[0,4;" .. formdata.info .. "]" + -- _G.minetest.show_formspec(player_name,"basic_response", form); + -- end + -- end, + + +-- }) +-- end + +local help_address = {}; -- array containing current page name for player +local help_pages = { + ["main"] = { + " === ROBOT HELP - MAIN SCREEN === ","", + "[Commands reference] display list of robot commands", + "[Lua basics] short introduction to lua","", + "INSTRUCTIONS: double click links marked with []", + "------------------------------------------","", + "basic_robot version " .. basic_robot.version, + "(c) 2016 rnd", + }, + + ["Lua basics"] = { + "back to [main] menu", + "BASIC LUA SYNTAX","", + " IF CONDITIONAL: if x==1 then A else B end", + " FOR LOOP: for i = 1, 5 do something end", + " WHILE LOOP: while i<6 do A; i=i+1; end", + ' ARRAYS: myTable1 = {1,2,3}, myTable2 = {["entry1"]=5, ["entry2"]=1}', + ' access table entries with myTable1[1] or myTable2.entry1 or', + ' myTable2["entry1"]', + " FUNCTIONS: f = function(x) return 2*x end, call like f(3)", + " STRINGS: name = \"rnd\" or name = [[rnd ]] (multiline string)", + " string.concat({string1,string2,...}, separator) returns", + " concatenated string with maxlength is 1024", + }, + + ["Commands reference"] = { + "back to [main] menu", + + "ROBOT COMMANDS","", + " 1. [MOVEMENT DIGGING PLACING NODE SENSING]", + " 2. [TAKE INSERT AND INVENTORY]", + " 3. [BOOKS CODE TEXT WRITE OR READ]", + " 4. [PLAYERS]", + " 5. [ROBOT]", + " 6. [KEYBOARD]", + " 7. [TECHNIC FUNCTIONALITY]", + " 8. [CRYPTOGRAPHY]", + " 9. [PUZZLE]", + }, + + ["MOVEMENT DIGGING PLACING NODE SENSING"] = { + "back to [Commands reference]", + "MOVEMENT,DIGGING, PLACING, NODE SENSING","", + " move.direction(), where direction is: left, right, forward, backward,", + " up, down, left_down, right_down, forward_down, backward_down,", + " left_up, right_up, forward_up, backward_up", + " boost(v) sets robot velocity, -60 it returns itemname.", + " if itemname == \"\" it checks if inventory empty", + " activate.direction(mode) activates target block", + " pickup(r) picks up all items around robot in radius r<8 and returns list or nil", + " craft(item,idx,mode) crafts item if required materials are present in inventory", + " mode = 1 returns recipe, optional recipe idx", + " take.direction(item, inventory) takes item from target inventory into robot", + " inventory", + }, + + ["BOOKS CODE TEXT WRITE OR READ"] = { + "back to [Commands reference]", + "BOOKS CODE TEXT WRITE OR READ","", + " title,text=book.read(i) returns title,contents of book at i-th position in library", + " book.write(i,title,text) writes book at i-th position at spawner library", + " code.run(text) compiles and runs the code in sandbox (privs only)", + " code.set(text) replaces current bytecode of robot", + " find_nodes(\"default:dirt\",3) returns distance to node in radius 3 around robot", + " or false if none", + " read_text.direction(stringname,mode) reads text of signs, chests and other", + " blocks, optional stringname for other meta", + " mode 1 read number", + " write_text.direction(text,mode) writes text to target block as infotext", + }, + + ["PLAYERS"] = { + "back to [Commands reference]", + "PLAYERS","", + " find_player(3,pos) finds players in radius 3 around robot(position) and returns", + " list of found player names, if none returns nil", + " attack(target) attempts to attack target player if nearby", + " grab(target) attempt to grab target player if nearby and returns true if succesful", + " player.getpos(name) return position of player, player.connected() returns list", + " of connected players names", + }, + + ["ROBOT"] = { + "back to [Commands reference]", + "ROBOT","", + " say(\"hello\") will speak", + " self.listen(0/1) (de)attaches chat listener to robot", + " speaker, msg = self.listen_msg() retrieves last chat message if robot listens", + " self.send_mail(target,mail) sends mail to target robot", + " sender,mail = self.read_mail() reads mail, if any", + " self.pos() returns table {x=pos.x,y=pos.y,z=pos.z}", + " self.name() returns robot name", + " self.operations() returns remaining robot operations", + " self.set_properties({textures=.., visual=..,visual_size=.., , ) sets visual", + " appearance", + " set_animation(anim_start,anim_end,anim_speed,anim_stand_start) set mesh", + " animation", + " self.spam(0/1) (dis)enable message repeat to all", + " self.remove() stops program and removes robot object", + " self.reset() resets robot position", + " self.spawnpos() returns position of spawner block", + " self.viewdir() returns vector of view for robot", + " self.fire(speed, pitch,gravity, texture, is_entity) fires a projectile from robot", + " self.fire_pos() returns last hit position", + " self.label(text) changes robot label", + " self.display_text(text,linesize,size) displays text instead of robot face, if no", + " size return tex", + " self.sound(sample,volume, opt. pos) plays sound named 'sample' at robot", + " location (optional pos)", + " rom is aditional table that can store persistent data, like rom.x=1", + }, + + ["KEYBOARD"] = { + "back to [Commands reference]", + "KEYBOARD","", + " EVENTS : place spawner at coordinates (20i,40j+1,20k) to monitor events", + " keyboard.get() returns table {x=..,y=..,z=..,puncher = .. , type = .. }", + " for keyboard event", + " keyboard.set(pos,type) set key at pos of type 0=air,1-6,7-15,16-271, limited to", + " range 10 around spawner", + " keyboard.read(pos) return node name at pos", + }, + + ["TECHNIC FUNCTIONALITY"] = { + "back to [Commands reference]", + "TECHNIC FUNCTIONALITY","", + " namespace 'machine'. most functions return", + " true or nil, error", + " energy() displays available energy", + " generate_power(fuel, amount) = energy, attempt to generate power from fuel", + " material. If amount>0 try generate amount of power using builtin generator", + " - this requires 40 gold/mese/diamonblock upgrades for each 1 amount", + " smelt(input,amount) = progress/true. works as a furnace, if amount>0 try to", + " use power to smelt - requires 10 upgrades for each 1 amount, energy cost is:", + " 1/40*(1+amount)", + " grind(input) - grinds input material, requires upgrades for harder material", + " compress(input) - requires upgrades - energy intensive process", + " transfer_power(amount,target_robot_name)", + }, + + ["CRYPTOGRAPHY"] = { + "back to [Commands reference]", + "CRYPTOGRAPHY","", + " namespace 'crypto'", + " encrypt(input,password) returns encrypted text, password is any string", + " decrypt(input,password) attempts to decrypt encrypted text", + " scramble(input,randomseed,sgn) (de)permutes text randomly according", + " to sgn = -1,1", + " basic_hash(input,n) returns simple mod hash from string input within range", + " 0...n-1", + }, + + ["PUZZLE"] = { + "back to [Commands reference]", + "PUZZLE","", + " namespace 'puzzle' - need puzzle priv", + " set_triggers({trigger1, trigger2,...}) sets and initializes spatial triggers", + " check_triggers(pname) check if player is close to any trigger and run that", + " trigger", + " set_node(pos,node) - set any node, limited to current protector mapblock", + " & get_node(pos)", + " get_player(pname) return player objRef in current mapblock", + " chat_send_player(pname, text)", + " get_node_inv(pos) / get_player_inv(pname) - return inventories of nodes/", + " players in current mapblock", + " get_meta(pos) - return meta of target position", + " get_gametime() - return current gametime", + " ItemStack(itemname) returns ItemRef to be used with inventory", + " count_objects(pos,radius)", + " pdata contains puzzle data like .triggers and .gamedata", + " add_particle(def)" + } + +} +for k,v in pairs(help_pages) do + local pages = help_pages[k]; for i = 1,#pages do pages[i] = minetest.formspec_escape(pages[i]) end +end + + +local robot_show_help = function(pname) --formname: robot_help + local address = help_address[pname] or "main"; + + --minetest.chat_send_all("D DISPLAY HELP for ".. address ) + local pages = help_pages[address]; + + local content = table.concat(pages,",") + local size = 8; local vsize = 7.75; + + local form = "size[" .. size .. "," .. size .. "] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]"; + --minetest.chat_send_all("D " .. form) + minetest.show_formspec(pname, "robot_help", form) + return +end + + +robogui["robot_help"] = { + response = function(player,formname,fields) + local name = player:get_player_name() + + local fsel = fields.wiki; + if fsel and string.sub(fsel,1,3) == "DCL" then + local sel = tonumber(string.sub(fsel,5)) or 1; -- selected line + local address = help_address[name] or "main"; + local pages = help_pages[address]; + + local link = string.match(pages[sel] or "", "\\%[([%w%s]+)\\%]") + if help_pages[link] then + help_address[name] = link; + robot_show_help(name) + end + end + end, + + getForm = function(player_name) + + end, + + show = robot_show_help, +}; diff --git a/scripts/games/mensch_argere_dich_nicht.lua b/scripts/games/mensch_argere_dich_nicht.lua new file mode 100644 index 0000000..2c3a6cb --- /dev/null +++ b/scripts/games/mensch_argere_dich_nicht.lua @@ -0,0 +1,89 @@ + +if not init then + + msg = + "'Mensch argere Dich nicht' is a German board game (but not a German-style board game), developed by Josef Friedrich Schmidt in 1907/1908.\n".. + " The players throw a die in turn and can advance any of their pieces in the game by the thrown number of dots on the dice.\n" .. + "Throwing a six means bringing a piece into the game (by placing one from the 'out' area onto the 'start' field) and throwing the dice again. If\n" .. + "a piece is on the 'start' field and there are still pieces in the 'out' area, it must be moved as soon as possible. If a piece cannot be\n".. "brought into the game then any other piece in the game must be moved by the thrown number, if that is possible. Pay attention that throwing\n".." dice continuously without moving is forbidden and by each dice throw you have to make a move.\n" .. + "Pieces can jump over other pieces, and throw out pieces from other players (into that player's 'out' area) if they land on them. A player\n".. "cannot throw out his own pieces though, he can advance further than the last field in the 'home' row. A player can be thrown out if he is on\n".. + "his 'start' field.\n" .. + "Variation which is played by most players: A player who has no pieces in the game has 3 tries to throw a six" + self.label(msg) + + init = true; + state = 1; -- game on + step = 0; + punchstate = 1; -- first punch + punchpos = {} + pos = self.spawnpos() + dice = 0 + spawns = {{2,2,"basic_robot:buttonFF8080"},{2,11,"basic_robot:button8080FF"},{11,11,"basic_robot:button80FF80"},{11,2,"basic_robot:buttonFFFF80"}} + + for i = 1,12 do for j = 1,12 do + minetest.swap_node({x=pos.x+i,y=pos.y+1,z=pos.z+j},{name = "air"}) + end end + + for k = 1,#spawns do + for i = 0,1 do for j = 0,1 do + minetest.swap_node({x=pos.x+i+spawns[k][1],y=pos.y+1,z=pos.z+j+spawns[k][2]},{name = spawns[k][3]}) + end end + end + + keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7) + +end + +if state == 0 then +elseif state == 1 then + event = keyboard.get(); + if event then + x = event.x-pos.x; y = event.y-pos.y; z = event.z-pos.z + --say("type " .. event.type .. " pos " .. x .. " " .. y .. " " .. z) + if x == 7 and y == 1 and z == 7 then + _G.math.randomseed(os.time()) + dice = math.random(6); + keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7+dice) + step = step + 1; + msg = colorize("red","") .. " STEP " .. step .. ": " ..event.puncher .. " threw dice = " .. dice; + minetest.chat_send_all(msg) + self.label(msg) + punchstate = 1 + elseif punchstate == 1 then + if y == 1 and event.type ~= 2 and event.type<7 then + punchpos = 2; minetest.chat_send_player(event.puncher,colorize("red","") .. " punch place on board where to move ") + punchpos = {x=event.x,y=event.y,z=event.z} + punchstate = 2 + end + elseif punchstate == 2 then + if y == 0 and event.type ~= 2 then + if x<2 or x>12 or z<2 or z>12 then + else + local nodename = minetest.get_node(punchpos).name; + minetest.swap_node({x=event.x, y = event.y+1, z=event.z},{name = nodename}) + minetest.swap_node(punchpos,{name = "air"}) + punchstate = 1; dice = 0 + minetest.add_particle( + { + pos = punchpos, + expirationtime = 15, + velocity = {x=0, y=0,z=0}, + size = 18, + texture = "default_apple.png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + } + ) + msg = colorize("red","") .. " " .. event.puncher .. " moved."; + minetest.chat_send_all(msg) + self.label(msg) + end + end + + + end + end + + +end \ No newline at end of file diff --git a/scripts/games/paint_game.lua b/scripts/games/paint_game.lua index 8c29108..0b7075e 100644 --- a/scripts/games/paint_game.lua +++ b/scripts/games/paint_game.lua @@ -1,13 +1,10 @@ --- paint canvas by rnd --- TODO: add load/save button, save in book --- save: ask for name (chat ) and save in book --- load display list of names and ask which one (chat) +-- paint canvas by rnd, 2018 if not init then - colors = { "black","blue","brown","cyan","dark_green","dark_grey","green","grey", "magenta","orange","pink","red","violet","white","yellow" } + invcolors = {}; for i = 1,#colors do invcolors[colors[i]] = i end color = 1; size = 16; @@ -24,18 +21,49 @@ if not init then spos = self.spawnpos(); spos.y=spos.y+1; canvasn = "wool:white" - for i = 1, size do - for j = 1, size do - minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn}) - end - end - + reset_canvas = function() + for i = 1, size do + for j = 1, size do + minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn}) + end + end + end + reset_canvas() + + save_image = function() + local ret = {}; + for i = 1, size do + for j = 1, size do + local nname = string.sub(minetest.get_node({x=spos.x +i , y = spos.y + j, z = spos.z }).name,6) + local pcolor = invcolors[nname] or 1; + ret[#ret+1]= string.char(96+pcolor) + end + end + return table.concat(ret,"") + end + + load_image = function(image) + if not image then return end + local ret = {}; local k = 0; + for i = 1, size do + for j = 1, size do + k=k+1; + local pcolor = colors[string.byte(image,k)-96] or "black"; + minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = "wool:"..pcolor}) + end + end + end + --draw buttons for i = 1,#colors do minetest.set_node({x=spos.x +i , y = spos.y , z = spos.z },{name = "wool:"..colors[i]}) end + minetest.set_node({x=spos.x +1 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_83"}) + minetest.set_node({x=spos.x +2 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_76"}) + + vn = {x=0,y=0,z=1}; T0 = {x=spos.x+0.5,y=spos.y+0.5,z=spos.z-0.5*vn.z}; @@ -50,27 +78,35 @@ if not init then end -if player:get_player_control().LMB then +if player:get_player_control().LMB then -- player interacts with 'virtual canvas gui' local v = player:get_look_dir(); local p = player:get_pos(); p.y = p.y + 1.5 local c = get_intersect(vn,T0,p,v); if c then local x = c.x - T0.x; local y = c.y - T0.y - if x>0 and x-1 and y0 then + if x>0 and x-2 and y0 then -- above: painting c.z = c.z+0.5 minetest.set_node(c, {name = "wool:" .. colors[color]}) - else + elseif y>-1 then -- color selection x = 1+math.floor(x) if colors[x] then color = x; self.label(colors[x]) end + else -- save,load button row + x = 1+math.floor(x) + if x==1 then + self.label("SAVED.") + book.write(1,"ROBOT_IMAGE",save_image()) + elseif x==2 then + local _,image = book.read(1) + load_image(image); + self.label("LOADED.") + end end end end - -end +end \ No newline at end of file diff --git a/scripts/graphics/painting_import.lua b/scripts/graphics/painting_import.lua new file mode 100644 index 0000000..43e3519 --- /dev/null +++ b/scripts/graphics/painting_import.lua @@ -0,0 +1,58 @@ +-- painting import from minetest 'painting mod' to robot canvas by rnd +-- stand near image and run "get_texture()" command in remote control + +if not init then + self.label("PAINTING IMPORTER") + pname = "rnd" + player = minetest.get_player_by_name(pname) + + get_texture = function() + + + local pos = player:get_pos(); local radius = 2 + local objs = minetest.get_objects_inside_radius(pos, radius) + local obj = {}; + + local ret = {}; + for i=1,#objs do + if not objs[i]:is_player() then obj = objs[i] break end + end + + if obj then + local tex = obj:get_properties().textures + local out = tex[1] or "" + if string.sub(out,1,9) == "[combine:" then + local pcolors = {"black","blue","brown","cyan","darkgreen","darkgrey","green","grey", + "magenta","orange","pink","red","violet","white","yellow"} + local ipcolors = {}; for i = 1,#pcolors do ipcolors[pcolors[i]] = i end + + local ret = {}; + local i =0; local j = 1; local k = 0; local size = 16; + --ret[1] = {} + for word in out:gmatch("=(%a+)%.png") do + ret[#ret+1] = string.char(96 + (ipcolors[word] or 1)) + end + + local rret = {}; + for i = 1, size do rret[i] = {} for j = 1,size do rret[i][j] = 0 end end + + k = 0 -- rotate 90 right + for j = 1,size do + for i = size,1,-1 do + k = k + 1 + rret[size-i+1][size-j+1] = ret[k] + end + end + + ret = {}; for i = 1, size do for j = 1, size do ret[#ret+1]= rret[i][j] end end -- write back + + out = table.concat(ret,"") + book.write(1,"IMPORTED_PAINTING", out) + minetest.chat_send_player(pname, "PAINTING FOUND, saved in robot library in book 1.") + end + else return "empty" + end + end + + init = true +end \ No newline at end of file diff --git a/scripts/gui/gui_deposit_withdraw_demo.lua b/scripts/gui/gui_deposit_withdraw_demo.lua index 3c87489..bd6785a 100644 --- a/scripts/gui/gui_deposit_withdraw_demo.lua +++ b/scripts/gui/gui_deposit_withdraw_demo.lua @@ -20,7 +20,12 @@ if not init then init = true data[pname] = data[pname] or {}; local pdata = data[pname]; - pdata[item] = (pdata[item] or 0) + count + + pdata[item] = pdata[item] or {}; + + local t = minetest.get_gametime() + pdata[item][1] = (pdata[item][1] or 0) + count + pdata[item][2] = t; inv:set_stack("main", 1, _G.ItemStack("")) @@ -36,11 +41,7 @@ if not init then init = true data[pname] = data[pname] or {}; --say(serialize(data[pname])) - local text = serialize(data[pname]) - local form = "size[5,5] textarea[0,0;6,6;STORAGE;STORAGE;".. - "YOU HAVE STORED FOLLOWING ITEMS:\n\n".. minetest.formspec_escape(text) .. "\n\nUse WITHDRAW to get items back]" - self.show_form(pname, form) - --say(pname .. " deposited " .. item .. "( " .. count .. " pieces) ") + return serialize(data[pname]) end withdraw = function(pname) @@ -51,32 +52,43 @@ if not init then init = true local player = _G.minetest.get_player_by_name(pname) local inv = player:get_inventory(); local pdata = data[pname] + local t0 = minetest.get_gametime(); + for k,v in pairs(pdata) do - inv:add_item("main", _G.ItemStack(k .. " " .. v)) + local t = t0 - v[2]; -- time since last deposit + local a = (1+interests/100)^t; + self.label("deposited time " .. t .. ", deposited quantity " .. v[1] .. ", new quantity : " .. math.floor(a*tonumber(v[1])) ) + inv:add_item("main", _G.ItemStack(k .. " " .. math.floor(a*tonumber(v[1]))) ) end data[pname] = nil; book.write(1,"",serialize(data)) end + function show(pname) + local itemlist = check(pname) + local form = "size [5,5] button[0,0;2,1;DEPOSIT;DEPOSIT] button[0,1;2,1;WITHDRAW;WITHDRAW]" .. + "textarea[0,2.5;6,3;STORAGE;YOUR DEPOSITS;" .. minetest.formspec_escape(itemlist) .. "]" + self.show_form(pname, form) + end + + + interests = 5; -- interest rate per second (demo) local players = find_player(4) if not players then self.remove() end - - pname = players[1] - - local form = "size [5,5] button[0,0;2,1;DEPOSIT;DEPOSIT] button[0,1;2,1;CHECK;CHECK] button[0,2;2,1;WITHDRAW;WITHDRAW]" - self.show_form(pname, form) + show(players[1]) + end sender,fields = self.read_form() if sender then if fields.DEPOSIT then deposit(sender) - elseif fields.CHECK then - check(sender) + show(sender) elseif fields.WITHDRAW then withdraw(sender) + show(sender) end --say(sender .. " clicked " .. serialize(fields)) -end +end \ No newline at end of file diff --git a/scripts/gui/wiki.lua b/scripts/gui/wiki.lua new file mode 100644 index 0000000..4675f2b --- /dev/null +++ b/scripts/gui/wiki.lua @@ -0,0 +1,54 @@ +-- ROBOT WIKI +if not init then + _G.basic_robot.data[self.name()].obj:get_luaentity().timestep = 0.1 + local players = find_player(4); + if not players then self.remove() end + pname = players[1]; + size = 8; + vsize = 8; + linesize = 60; -- break up longer lines + + wiki = { + ["Main menu"] = "HELP CONTENTS\n \n".."double click link marked with [] or press enter while selected.\n \n".."[How to play]\n".."[Robot tutorial]", + ["How to play"] = "HOW TO PLAY\n \nOpen inventory (press i on pc), then go to Quests and read.".. + "Complete quests to progress in game and get nice rewards.\n \n[Main menu]", + ["Robot tutorial"] = "ROBOT TUTORIAL\n \nLearn on simple programs first then make a lot of your own\n \n[Main menu]", + } + current = "Main menu"; + + render_page = function() + page = {} + local text = wiki[current]; + for line in text:gmatch("[^\n]+") do + local llen = string.len(line); + local m = math.floor(llen/linesize)+1; + for i = 1, m do + page[#page+1]=minetest.formspec_escape(string.sub(line,(i-1)*linesize+1, i*linesize)) + end + + end + + local content = table.concat(page,",") + return "size[" .. size .. "," .. size .. "] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]"; + end + + page = {} + self.show_form(pname,render_page()) + init = true +end + +sender,fields = self.read_form() +if sender then + --self.label(serialize(fields)) + local fsel = fields.wiki; + if fsel then + if string.sub(fsel,1,3) == "DCL" then + local sel = tonumber(string.sub(fsel,5)) or 1; + if string.sub(page[sel],1,2) == "\\[" then + current = string.sub(page[sel],3,-3) + self.show_form(pname,render_page()) + end + end + end + +end \ No newline at end of file diff --git a/scripts/programming/code_parser_strings_identify.lua b/scripts/programming/code_parser_strings_identify.lua new file mode 100644 index 0000000..a58b313 --- /dev/null +++ b/scripts/programming/code_parser_strings_identify.lua @@ -0,0 +1,53 @@ +function identify_strings(code) -- returns list of positions {start,end} of literal strings in lua code + + local i = 0; local j; local length = string.len(code); + local mode = 0; -- 0: not in string, 1: in '...' string, 2: in "..." string, 3. in [==[ ... ]==] string + local modes = { + {"'","'"}, + {"\"","\""}, + {"%[=*%[","%]=*%]"} + } + local ret = {} + while i < length do + i=i+1 + + local jmin = length+1; + if mode == 0 then -- not yet inside string + for k=1,#modes do + j = string.find(code,modes[k][1],i); + if j and j