complete help gui rewrite ( man pages with hyperlinks )

new demo scripts
master
rnd 2018-07-27 11:32:41 +02:00
parent 76d17d9e12
commit 34e8dcc59d
8 changed files with 682 additions and 250 deletions

223
init.lua
View File

@ -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, -6<v<6, if v = 0 then stop\n"..
" turn.left(), turn.right(), turn.angle(45)\n"..
" dig.direction()\n"..
" place.direction(\"default:dirt\", optional orientation param)\n"..
" read_node.direction() tells you names of nodes\n"..
" insert.direction(item, inventory) inserts item from robot inventory to target inventory\n"..
" check_inventory.direction(itemname, inventory,index) looks at node and returns false/true, direction can be self,\n"..
" if index>0 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)

341
robogui.lua Normal file
View File

@ -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, -6<v<6, if v = 0 then stop",
" turn.left(), turn.right(), turn.angle(45)",
" dig.direction()",
" place.direction(\"default:dirt\", optional orientation param)",
" read_node.direction() tells you names of nodes",
},
["TAKE INSERT AND INVENTORY"] = {
"back to [Commands reference]",
"TAKE INSERT AND INVENTORY","",
" insert.direction(item, inventory) inserts item from robot inventory to target",
" inventory",
" check_inventory.direction(itemname, inventory,index) looks at node and ",
" returns false/true, direction can be self, if index>0 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,
};

View File

@ -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","<Mensch argere dich nicht>") .. " 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","<Mensch argere dich nicht>") .. " 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","<Mensch argere dich nicht>") .. " " .. event.puncher .. " moved.";
minetest.chat_send_all(msg)
self.label(msg)
end
end
end
end
end

View File

@ -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<size and y>-1 and y<size then
--self.label(x .. " " .. y)
if y>0 then
if x>0 and x<size and y>-2 and y<size then
if y>0 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

View File

@ -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

View File

@ -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

54
scripts/gui/wiki.lua Normal file
View File

@ -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

View File

@ -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<jmin then -- pick closest one
jmin = j
mode = k
end
end
if mode ~= 0 then -- found something
j=jmin
ret[#ret+1] = {jmin}
end
if not j then break end -- found nothing
else
_,j = string.find(code,modes[mode][2],i); -- search for closing pair
if not j then break end
if (mode~=2 or string.sub(code,j-1,j-1) ~= "\\") then -- not (" and \")
ret[#ret][2] = j
mode = 0
end
end
i=j -- move to next position
end
if mode~= 0 then ret[#ret][2] = length end
return ret
end
identify_strings_test = function()
local code =
[[code; a = "text \"text\" more text"; b = 'some more text'; c = [==[ text ]==] ]]
local strings = identify_strings(code);
say(minetest.serialize(strings))
for i =1,#strings do
say(i ..": " .. string.sub(code, strings[i][1],strings[i][2]))
end
end
identify_strings_test()
self.remove()