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 = {}; -- table containing current page name for player [name] = {address = ..., sel = ..} 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 SPEAK LABEL APPEARANCE OTHER]", " 6. [KEYBOARD AND USER INTERACTIONS]", " 7. [TECHNIC FUNCTIONALITY]", " 8. [CRYPTOGRAPHY]", " 9. [PUZZLE]", " 10.[COROUTINES AND LIBRARIES AND ROM] - easier alternative to", " finite state machines", }, ["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 found", " read_text.direction(stringname,mode) reads text of signs, chests and", " other blocks, optional stringname for other meta, mode 1 to 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.get_pos(name) return position of player, player.connected()", " returns list of connected players names", }, ["ROBOT SPEAK LABEL APPEARANCE OTHER"] = { "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", " has listener attached", " 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", " self.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. if is_entity false (default) it fires particle.", " 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 just return texture string", " self.find_path(pos) attempts to find path to pos (coordinates must be", " integer). On success return length of path, otherwise nil", " self.walk_path() attempts to walk path it previously found. On success ", " returns number of remaining nodes, on fail it returns -next node distance or 0", " 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 AND USER INTERACTIONS"] = { "back to [Commands reference]", "KEYBOARD","", " EVENTS : first attach listener to robot with self.listen_punch", " self.listen_punch(pos,is_remove) robot will listen to punch events in ", " ".. basic_robot.radius .. " sized chunk containing position", " pos = {x=.., y=.., z=..}. if is_remove==true then remove listener", " keyboard.get() returns table {x=..,y=..,z=..,puncher = .. , type = .. }", " if there was keyboard event, nil if there was none", " keyboard.set(pos,type) set key as a node in world 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","", " All commands are in namespace 'machine', for example machine.energy()", " most functions return: ok, error = true or nil, error", " To use some commands fully robot must be upgraded. 1 upgrade is", " goldblock+meseblock+diamonblock.", " 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 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 of smelt is: 1/40*(1+amount)", " grind(input) - grinds input material, requires upgrades for harder", " materials", " compress(input) - requires upgrades - energy intensive process", " transfer_power(amount,target_robot_name)", " dig_seed.direction digs seed node and transforms it to seed book", " place_seed.direction(seedbookname) takes oldest seed from seed book", " and plants it", }, ["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", " region", " get_player(pname) return player objRef in current protector region", " 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)" }, ["COROUTINES AND LIBRARIES AND ROM"] = { "back to [Commands reference]", "COROUTINES","", "robot can run code using lua coroutines. To enable this mode just put", "the word coroutine in the first 32 characters of your program. Example:", " --testing program for coroutine", " for i = 1,5 do ", " say(i); dig.forward(); move.forward()", " pause()", " end", "pause(amount) temporarily stops if available operations are less than", " amount","", "LIBRARIES","", "you can define functions for robot in another robot and re-use them later.", "for example do: rom.lib_src = \"f1 = function(x) return 2*x end\" and later", "in another robot recreate function with: ", "if not f1 then code.run(rom.lib_src) end ", "self.label(f1(1))","", "ROM","", "all robots by a player share same \"rom\" variable that can be used to", "store more persistent data like numbers, strings or tables", }, } 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 data = help_address[pname]; if not data then help_address[pname] = {address = "main", sel = 1} data = help_address[pname] end local address = data.address; --minetest.chat_send_all("D DISPLAY HELP for ".. address ) local pages = help_pages[address]; if not pages then return end local content = table.concat(pages,",") local size = 9.5; local vsize = 8.75; local form = "size[" .. size .. "," .. size .. "] button[-0.25,".. size-0.15 ..";1,0.5;go;go] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+0.25) .. ";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; --minetest.chat_send_all("D " .. minetest.serialize(fields)) local go = false; if fsel then help_address[name].sel = tonumber(string.sub(fsel or "",5)) or 1 if string.sub(fsel,1,3) == "CHG" then return end -- just select topic, dont go there go = true -- we want to go to selected page end if fields.go then go = true end if not go then return end local sel = help_address[name].sel; -- selected line local address = help_address[name].address; local pages = help_pages[address]; local link = string.match(pages[sel] or "", "\\%[([%w%s]+)\\%]") if help_pages[link] then help_address[name].address = link help_address[name].sel = 1 robot_show_help(name) end end, getForm = function(player_name) end, show = robot_show_help, };