Compare commits

...

7 Commits

Author SHA1 Message Date
ac-minetest 8c9d0fd68f custom buttons fix. redstone script fix 2022-08-28 09:52:37 +02:00
ac-minetest 2020477700 sandbox bugfixes 2021-06-28 08:49:33 +02:00
ac-minetest c3a021d1fc gui fixes 2021-02-03 21:32:21 +01:00
ac-minetest bf7002b1c3 Merge branch 'master' of https://github.com/ac-minetest/basic_robot 2021-01-07 11:39:40 +01:00
ac-minetest 79bbe6c2af fixes and new scripts 2021-01-07 11:30:01 +01:00
ac-minetest e237985e8e corrupted book fix 2020-11-27 17:16:58 +01:00
ac-minetest 5d94e03351 boost angle fix 2020-11-27 16:58:55 +01:00
57 changed files with 6920 additions and 2037 deletions

View File

@ -18,8 +18,8 @@ end
local pi = math.pi; local pi = math.pi;
local function pos_in_dir(obj, dir) -- position after we move in specified direction local function pos_in_dir(obj, dir) -- position after we move in specified direction
local yaw = obj:getyaw(); local yaw = obj:get_yaw();
local pos = obj:getpos(); local pos = obj:get_pos();
if dir == 1 then -- left if dir == 1 then -- left
yaw = yaw + pi/2; yaw = yaw + pi/2;
@ -88,7 +88,7 @@ basic_robot.commands.move = function(name,dir)
return false return false
end end
obj:moveto(pos, true) obj:move_to(pos, true)
-- sit and stand up for model - doesnt work for overwriten obj export -- sit and stand up for model - doesnt work for overwriten obj export
-- if dir == 5 then-- up -- if dir == 5 then-- up
@ -105,15 +105,16 @@ basic_robot.commands.turn = function (name, angle)
local yaw; local yaw;
-- more precise turns by 1 degree resolution -- more precise turns by 1 degree resolution
local mult = math.pi/180; local mult = math.pi/180;
local yaw = obj:getyaw(); local yaw = obj:get_yaw();
yaw = math.floor((yaw+angle)/mult+0.5)*mult; yaw = math.floor((yaw+angle)/mult+0.5)*mult;
obj:setyaw(yaw); obj:set_yaw(yaw);
end end
basic_robot.digcosts = { -- 1 energy = 1 coal basic_robot.digcosts = { -- 1 energy = 1 coal
["default:stone"] = 1/25, ["default:stone"] = 1/25,
["default:cloud"] = 10^8, ["gloopblocks:pumice_cooled"] = 1/25,
["default:cloud"] = 10^9,
} }
@ -273,7 +274,7 @@ basic_robot.commands.pickup = function(r,name)
if r>8 then return false end if r>8 then return false end
check_operations(name,4,true) check_operations(name,4,true)
local pos = basic_robot.data[name].obj:getpos(); local pos = basic_robot.data[name].obj:get_pos();
local spos = basic_robot.data[name].spawnpos; -- position of spawner block local spos = basic_robot.data[name].spawnpos; -- position of spawner block
local meta = minetest.get_meta(spos); local meta = minetest.get_meta(spos);
local inv = minetest.get_meta(spos):get_inventory(); local inv = minetest.get_meta(spos):get_inventory();
@ -289,7 +290,7 @@ basic_robot.commands.pickup = function(r,name)
picklist[#picklist+1]=detected_obj; picklist[#picklist+1]=detected_obj;
if inv:room_for_item("main", stack) then if inv:room_for_item("main", stack) then
inv:add_item("main", stack); inv:add_item("main", stack);
obj:setpos({x=0,y=0,z=0}) -- no dupe obj:set_pos({x=0,y=0,z=0}) -- no dupe
end end
obj:remove(); obj:remove();
end end
@ -382,8 +383,8 @@ basic_robot.commands.attack = function(name, target) -- attack range 4, damage 5
local tplayer = minetest.get_player_by_name(target); local tplayer = minetest.get_player_by_name(target);
if not tplayer then return false end if not tplayer then return false end
local obj = basic_robot.data[name].obj; local obj = basic_robot.data[name].obj;
local pos = obj:getpos(); local pos = obj:get_pos();
local tpos = tplayer:getpos(); local tpos = tplayer:get_pos();
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 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 return false
@ -400,8 +401,8 @@ basic_robot.commands.grab = function(name,target)
local tplayer = minetest.get_player_by_name(target); local tplayer = minetest.get_player_by_name(target);
if not tplayer then return false end if not tplayer then return false end
local obj = basic_robot.data[name].obj; local obj = basic_robot.data[name].obj;
local pos = obj:getpos(); local pos = obj:get_pos();
local tpos = tplayer:getpos(); local tpos = tplayer:get_pos();
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 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 return false
@ -444,8 +445,9 @@ basic_robot.commands.write_book = function(name,title,text) -- returns itemstack
local data = {} local data = {}
if title == "" or not title then title = "program book "..minetest.get_gametime() end if title == "" or not title then title = "program book "..minetest.get_gametime() end
data.title = title or "" if text == "" or not text then text = "empty" end
data.text = text or "" data.text = text
data.title = title
data.text_len = #data.text data.text_len = #data.text
data.page = 1 data.page = 1
data.description = title or "" data.description = title or ""
@ -568,9 +570,51 @@ basic_robot.commands.activate = function(name,mode, dir)
return true return true
end end
local write_keyevent = function(data,pos, puncher,type)
local keyevent = data.keyevent;
if not keyevent then -- init
data.keyevent = {5,1,1,{}} -- how many events buffer holds, input idx, output idx, buffer data
keyevent = data.keyevent;
end
local iidx = keyevent[2]; -- input idx
-- write event at input idx
keyevent[4][iidx] = {x=pos.x,y=pos.y,z=pos.z, puncher = puncher, type = type}
local oidx = keyevent[3]; -- output idx
iidx=iidx+1; if iidx>keyevent[1] then iidx = 1 end -- advance idx for next input write
keyevent[2] = iidx
if iidx == oidx then -- old event was overwritten, lets increase output idx
oidx = oidx+1
if oidx>keyevent[1] then oidx = 1 end
keyevent[3] = oidx
end
end
local register_robot_button = function(R,G,B,type) basic_robot.commands.write_keyevent = write_keyevent;
minetest.register_node("basic_robot:button"..R..G..B,
local button_punched = function(pos, node, player,type)
local name = player:get_player_name(); if name==nil then return end
local round = math.floor;
local r = basic_robot.radius; local ry = 2*r;
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 hppos = minetest.hash_node_position(ppos)
local rname = basic_robot.data.punchareas[hppos];
local data = basic_robot.data[rname];
if data then
write_keyevent(data,pos, player:get_player_name(),type)
end
end
local buttoncolors = {};
local register_robot_button = function(R,G,B,colorname,type)
buttoncolors[type] = "basic_robot:button"..colorname;
minetest.register_node("basic_robot:button"..colorname,
{ {
description = "robot button", description = "robot button",
tiles = {"robot_button.png^[colorize:#"..R..G..B..":180"}, tiles = {"robot_button.png^[colorize:#"..R..G..B..":180"},
@ -578,129 +622,139 @@ local register_robot_button = function(R,G,B,type)
wield_image = "robot_button.png^[colorize:#"..R..G..B..":180", wield_image = "robot_button.png^[colorize:#"..R..G..B..":180",
is_ground_content = false, is_ground_content = false,
groups = {cracky=3}, groups = {cracky=3,not_in_craft_guide = 1},
on_punch = function(pos, node, player) on_punch = function(pos, node,player) button_punched(pos, node,player,type) end
local name = player:get_player_name(); if name==nil then return end
local round = math.floor;
local r = basic_robot.radius; local ry = 2*r; -- note: this is skyblock adjusted
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 end
local register_robot_button_number = function(number,type) local register_robot_button_number = function(number,type)
local idx = number+48
local texname = "robochars.png^[sheet:16x16:" .. idx % 16 .. "," .. math.floor(idx/ 16).."^[invert:rgb";
minetest.register_node("basic_robot:button"..number, minetest.register_node("basic_robot:button"..number,
{ {
description = "robot button", description = "robot button",
tiles = {"robot_button".. number .. ".png"}, tiles = {texname},
inventory_image = "robot_button".. number .. ".png", inventory_image = texname,
wield_image = "robot_button".. number .. ".png", wield_image = texname,
paramtype2 = "facedir",
is_ground_content = false, is_ground_content = false,
groups = {cracky=3}, groups = {cracky=3,not_in_craft_guide = 1},
on_punch = function(pos, node, player) on_punch = function(pos, node,player) button_punched(pos, node,player,type) end
local name = player:get_player_name(); if name==nil then return end
local round = math.floor;
local r = basic_robot.radius; local ry = 2*r;
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 end
local register_robot_button_char = function(number,type) local register_robot_button_char = function(number,type)
local texname = "robochars.png^[sheet:16x16:" .. number % 16 .. "," .. math.floor(number/ 16);
-- local texname = string.format("%03d",number).. ".png";
minetest.register_node("basic_robot:button_"..number, minetest.register_node("basic_robot:button_"..number,
{ {
description = "robot button", description = "robot button",
tiles = {string.format("%03d",number).. ".png"}, tiles = {texname},
inventory_image = string.format("%03d",number).. ".png", inventory_image = texname,
wield_image = string.format("%03d",number).. ".png", wield_image = texname,
is_ground_content = false, is_ground_content = false,
groups = {cracky=3}, groups = {cracky=3,not_in_craft_guide = 1},
on_punch = function(pos, node, player) paramtype2 = "facedir",
local name = player:get_player_name(); if name==nil then return end on_punch = function(pos, node,player) button_punched(pos, node,player,type) end
local round = math.floor;
local r = basic_robot.radius; local ry = 2*r;
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 end
local register_robot_button_custom = function(number,texture) local register_robot_button_custom = function(number,texture)
minetest.register_node("basic_robot:button_"..number, local type = number
{ minetest.register_node("basic_robot:button_"..number,
description = "robot button", {
tiles = {texture .. ".png"}, description = "robot button",
inventory_image = texture .. ".png", tiles = {texture .. ".png"},
wield_image = texture .. ".png", inventory_image = texture .. ".png",
is_ground_content = false, wield_image = texture .. ".png",
groups = {cracky=3}, is_ground_content = false,
on_punch = function(pos, node, player) groups = {cracky=3,not_in_craft_guide = 1},
local name = player:get_player_name(); if name==nil then return end on_punch = function(pos, node,player) button_punched(pos, node,player,type) end
local round = math.floor;
local r = basic_robot.radius; local ry = 2*r;
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 end
--[[ colors!
0 white 8 green
1 yellow 9 dark green
2 orange 10 brown
3 red 11 tan
4 magenta 12 light grey
5 purple 13 medium grey
6 blue 14 dark grey
7 cyan 15 black
--]]
register_robot_button("FF","FF","FF",1); --16-color palette from macintosh II, 1987
register_robot_button("80","80","80",2);
register_robot_button("FF","FF","FF","white",1); -- white
register_robot_button("FC","F4","00","yellow",2); -- yellow
register_robot_button("FF","64","00","orange",3); -- orange
register_robot_button("DD","02","02","red",4); -- red
register_robot_button("F0","02","85","magenta",5); -- magenta
register_robot_button("46","00","A5","purple",6); -- purple
register_robot_button("00","00","D5","blue",7); -- blue
register_robot_button("00","AE","E9","cyan",8); -- cyan
register_robot_button("1A","B9","0C","green",9); -- green
register_robot_button("00","64","08","dark_green",10);-- dark_green
register_robot_button("57","28","00","brown",11);-- brown
register_robot_button("91","71","35","tan",12);-- tan
register_robot_button("C1","C1","C1","light_grey",13);-- light_grey
register_robot_button("81","81","81","medium_grey",14);-- medium_grey
register_robot_button("3E","3E","3E","dark_grey",15);-- dark_grey
register_robot_button("00","00","00","black",16);-- black
--[[ -- old colors
register_robot_button("FF","FF","FF",1);
register_robot_button("80","80","80",2);
register_robot_button("FF","80","80",3); register_robot_button("FF","80","80",3);
register_robot_button("80","FF","80",4); register_robot_button("80","FF","80",4);
register_robot_button("80","80","FF",5); register_robot_button("80","80","FF",5);
register_robot_button("FF","FF","80",6); register_robot_button("FF","FF","80",6);
--]]
for i = 0,9 do register_robot_button_number(i,i+7) end for i = 0,9 do register_robot_button_number(i,i+17) end -- all buttons shift by 10 from old version!
for i = 0,255 do register_robot_button_char(i,i+17) end for i = 0,255 do register_robot_button_char(i,i+27) end
register_robot_button_custom(273,"puzzle_switch_off") register_robot_button_custom(283,"puzzle_switch_off")
register_robot_button_custom(274,"puzzle_switch_on") register_robot_button_custom(284,"puzzle_switch_on")
register_robot_button_custom(275,"puzzle_button_off") register_robot_button_custom(285,"puzzle_button_off")
register_robot_button_custom(276,"puzzle_button_on") register_robot_button_custom(286,"puzzle_button_on")
register_robot_button_custom(277,"puzzle_equalizer") register_robot_button_custom(287,"puzzle_equalizer")
register_robot_button_custom(278,"puzzle_setter") register_robot_button_custom(288,"puzzle_setter")
register_robot_button_custom(279,"puzzle_piston") register_robot_button_custom(289,"puzzle_piston")
register_robot_button_custom(280,"puzzle_diode") register_robot_button_custom(290,"puzzle_diode")
register_robot_button_custom(281,"puzzle_NOT") register_robot_button_custom(291,"puzzle_NOT")
register_robot_button_custom(282,"puzzle_delayer") register_robot_button_custom(292,"puzzle_delayer")
register_robot_button_custom(283,"puzzle_platform") register_robot_button_custom(293,"puzzle_platform")
register_robot_button_custom(284,"puzzle_giver") register_robot_button_custom(294,"puzzle_giver")
register_robot_button_custom(285,"puzzle_checker") register_robot_button_custom(295,"puzzle_checker")
-- interactive button for robot: place robot on top of protector to intercept events -- interactive button for robot: place robot on top of protector to intercept events
local dout = minetest.chat_send_all
basic_robot.commands.keyboard = { basic_robot.commands.keyboard = {
get = function(name) get = function(name) -- read keyboard event
local data = basic_robot.data[name]; local data = basic_robot.data[name];
if data.keyboard then if data.keyevent then
local keyboard = data.keyboard; local keyevent = data.keyevent;
local event = {x=keyboard.x,y=keyboard.y,z=keyboard.z, puncher = keyboard.puncher, type = keyboard.type}; local oidx = keyevent[3];
data.keyboard = nil; local iidx = keyevent[2];
if oidx == iidx then return nil end --just read past last written event, nothing to read anymore
local event = keyevent[4][oidx];
-- move oidx (read position ) to newer event
oidx = oidx+1; if oidx>keyevent[1] then oidx = 1 end
keyevent[3] = oidx
return event return event
else else
return nil return nil
@ -718,22 +772,12 @@ basic_robot.commands.keyboard = {
local nodename; local nodename;
if type == 0 then if type == 0 then
nodename = "air" nodename = "air"
elseif type == 1 then elseif type < 17 then
nodename = "basic_robot:buttonFFFFFF"; nodename = buttoncolors[type]
elseif type == 2 then elseif type>=17 and type <= 26 then
nodename = "basic_robot:button808080"; nodename = "basic_robot:button"..(type-17);
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";
elseif type>=7 and type <= 16 then
nodename = "basic_robot:button"..(type-7);
else else
nodename = "basic_robot:button_"..(type-17); nodename = "basic_robot:button_"..(type-27);
end end
minetest.swap_node(pos, {name = nodename}) minetest.swap_node(pos, {name = nodename})
@ -829,7 +873,7 @@ end
--pathfinding: find_path, walk_path --pathfinding: find_path, walk_path
basic_robot.commands.find_path = function(name,pos2) basic_robot.commands.find_path = function(name,pos2)
local obj = basic_robot.data[name].obj; local obj = basic_robot.data[name].obj;
local pos1 = obj:getpos(); local pos1 = obj:get_pos();
if (pos1.x-pos2.x)^2+(pos1.y-pos2.y)^2+(pos1.z-pos2.z)^2> 50^2 then if (pos1.x-pos2.x)^2+(pos1.y-pos2.y)^2+(pos1.z-pos2.z)^2> 50^2 then
return nil,"2: distance too large" return nil,"2: distance too large"
@ -869,7 +913,7 @@ basic_robot.commands.walk_path = function(name)
local pos2 = path[idx] local pos2 = path[idx]
local obj = basic_robot.data[name].obj; local obj = basic_robot.data[name].obj;
local pos1 = obj:getpos(); local pos1 = obj:get_pos();
local ndist = (pos1.x-pos2.x)^2+(pos1.y-pos2.y)^2+(pos1.z-pos2.z)^2 local ndist = (pos1.x-pos2.x)^2+(pos1.y-pos2.y)^2+(pos1.z-pos2.z)^2
if ndist> 4 then return -ndist end -- too far away from next node if ndist> 4 then return -ndist end -- too far away from next node
@ -895,7 +939,7 @@ basic_robot.commands.walk_path = function(name)
end end
end end
yaw = yaw - math.pi/2 yaw = yaw - math.pi/2
obj:setyaw(yaw); obj:set_yaw(yaw);
pathdata[1] = idx + 1 -- target next node pathdata[1] = idx + 1 -- target next node
obj:moveto(pos2, true) obj:moveto(pos2, true)
@ -976,7 +1020,7 @@ basic_robot.commands.machine = {
check_operations(name,6, true) check_operations(name,6, true)
if amount and amount>0 then -- attempt to generate power from builtin generator if amount and amount>0 and amount<10^6 then -- attempt to generate power from builtin generator
local pos = basic_robot.data[name].spawnpos; -- position of spawner block local pos = basic_robot.data[name].spawnpos; -- position of spawner block
local inv = minetest.get_meta(pos):get_inventory(); 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 local level = amount*40; -- to generate 1 unit ( coal lump per second ) we need at least upgrade 40
@ -1150,9 +1194,8 @@ basic_robot.commands.machine = {
local energy = 0; -- can only do one step at a run time local energy = 0; -- can only do one step at a run time
energy = data.menergy or 0; energy = data.menergy or 0;
if amount>energy or amount<0 then return nil,"energy too low" end if amount>energy or amount<0 or amount>10^6 then return nil,"energy too low" end
if not tdata.menergy then tdata.menergy = 0 end if not tdata.menergy then tdata.menergy = 0 end
tdata.menergy = tdata.menergy + amount tdata.menergy = tdata.menergy + amount
@ -1160,6 +1203,52 @@ basic_robot.commands.machine = {
return true return true
end, end,
place_seed = function(name,dir,seedbookname) -- use basic_farming seedbook to place seed
if not basic_farming then return end
local obj = basic_robot.data[name].obj;
local pos = pos_in_dir(obj, dir)
local spos = obj:get_luaentity().spawnpos;
local inv = minetest.get_meta(spos):get_inventory();
local idx = 0; -- where is seedbook?
for i = 1,inv:get_size("main") do -- where in inventory is seedbook?
if inv:get_stack("main", i):get_name() == seedbookname then idx = i; break end
end
if idx == 0 then return end -- no book in inventory!
local itemstack = basic_farming.seed_on_place(inv:get_stack("main", idx), nil, {type = "node",above = pos})
inv:set_stack("main", idx, itemstack) -- refresh stack in inventory
end,
dig_seed = function(name, dir)
if not basic_farming then return end
local obj = basic_robot.data[name].obj;
local pos = pos_in_dir(obj, dir)
local nodename = minetest.get_node(pos).name;
local basename,stage
basename,stage=string.match(nodename,"%w+:(%w+)_(%d+)") -- modname:basename_stage
if not basename then return end
local spos = basic_robot.data[name].spawnpos; -- position of spawner block
local inv = minetest.get_meta(spos):get_inventory();
local itemstack = ItemStack("basic_farming:seedbook_" .. basename)
local seeds = tostring(minetest.get_meta(pos):get_int("gene"));
-- possibly several seeds?
local count = minetest.get_meta(pos):get_int("count"); if count == 0 then count = 1 end
local data = {name = basename, items = string.rep(seeds.. " ",count-1) .. seeds }
minetest.set_node(pos,{name = "air"})
local meta = itemstack:get_meta()
meta:from_table({fields = data})
inv:add_item("main",itemstack);
end,
} }
-- CRPYTOGRAPHY -- CRPYTOGRAPHY
@ -1304,7 +1393,7 @@ local cmd_get_player = function(data,pname) -- return player for further manipul
local player = minetest.get_player_by_name(pname) local player = minetest.get_player_by_name(pname)
if not player then error("player does not exist"); return end if not player then error("player does not exist"); return end
local spos = data.spawnpos; local spos = data.spawnpos;
local ppos = player:getpos(); local ppos = player:get_pos();
if not is_same_block(ppos,spos) then error("can not get player in another protection zone") return end if not is_same_block(ppos,spos) then error("can not get player in another protection zone") return end
return player return player
end end
@ -1313,7 +1402,7 @@ local cmd_get_player_inv = function(data,pname)
local player = minetest.get_player_by_name(pname) local player = minetest.get_player_by_name(pname)
if not player then return end if not player then return end
local spos = data.spawnpos; local spos = data.spawnpos;
local ppos = player:getpos(); local ppos = player:get_pos();
if not is_same_block(ppos,spos) then error("can not get player in another protection zone") return end if not is_same_block(ppos,spos) then error("can not get player in another protection zone") return end
return player:get_inventory(); return player:get_inventory();
end end
@ -1472,7 +1561,7 @@ function Vplayer:new(name) -- constructor
end end
-- functions -- functions
function Vplayer:getpos() return self.obj:getpos() end function Vplayer:get_pos() return self.obj:get_pos() end
function Vplayer:remove() end function Vplayer:remove() end
function Vplayer:setpos() end function Vplayer:setpos() end
function Vplayer:move_to() end function Vplayer:move_to() end

266
init.lua
View File

@ -1,32 +1,11 @@
-- basic_robot by rnd, 2016 -- basic_robot by rnd, 2016-2022
basic_robot = {}; basic_robot = {};
------ SETTINGS -------- basic_robot.version = "2022/02/02b";
basic_robot.call_limit = {50,200,1500,10^9}; -- how many execution calls per script run allowed, for auth levels 0,1,2 (normal, robot, puzzle, admin)
basic_robot.count = {2,6,16,128} -- how many robots player can have
basic_robot.radius = 32; -- divide whole world into blocks of this size - used for managing events like keyboard punches dofile(minetest.get_modpath("basic_robot").."/settings.lua") -- read configuration SETTINGS
basic_robot.password = "raN___dOM_ p4S"; -- IMPORTANT: change it before running mod, password used for authentifications
basic_robot.admin_bot_pos = {x=0,y=1,z=0} -- position of admin robot spawner that will be run automatically on server start -- structures for storing robot data
basic_robot.maxoperations = 10; -- how many operations (dig, place,move,...,generate energy,..) available per run, 0 = unlimited
basic_robot.dig_require_energy = true; -- does robot require energy to dig stone?
basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes inventories to prevent player abuses
["moreblocks:circular_saw"] = true,
["craft_guide:sign_wall"] = true,
["basic_machines:battery_0"] = true,
["basic_machines:battery_1"] = true,
["basic_machines:battery_2"] = true,
["basic_machines:generator"] = true,
}
----- END OF SETTINGS ------
basic_robot.http_api = minetest.request_http_api();
basic_robot.version = "2020/10/14a";
basic_robot.gui = {}; local robogui = basic_robot.gui -- gui management basic_robot.gui = {}; local robogui = basic_robot.gui -- gui management
basic_robot.data = {}; -- stores all robot related data basic_robot.data = {}; -- stores all robot related data
@ -40,6 +19,7 @@ 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.virtual_players = {}; -- this way robot can interact with the world as "player" TODO
basic_robot.data.listening = {}; -- which robots listen to chat basic_robot.data.listening = {}; -- which robots listen to chat
basic_robot.data.punchareas = {}; -- where robots listen punch events, [hashes of 32 sized chunk] = robot name
dofile(minetest.get_modpath("basic_robot").."/robogui.lua") -- gui stuff dofile(minetest.get_modpath("basic_robot").."/robogui.lua") -- gui stuff
dofile(minetest.get_modpath("basic_robot").."/commands.lua") dofile(minetest.get_modpath("basic_robot").."/commands.lua")
@ -49,7 +29,6 @@ local check_code, preprocess_code,is_inside_string;
-- SANDBOX for running lua code isolated and safely -- SANDBOX for running lua code isolated and safely
function getSandboxEnv (name) function getSandboxEnv (name)
local authlevel = basic_robot.data[name].authlevel or 0; local authlevel = basic_robot.data[name].authlevel or 0;
@ -60,21 +39,26 @@ function getSandboxEnv (name)
left_up = 11, right_up = 12, forward_up = 13, backward_up = 14 left_up = 11, right_up = 12, forward_up = 13, backward_up = 14
} }
if not basic_robot.data[name].rom then basic_robot.data[name].rom = {} end -- create rom if not yet existing local pname = string.sub(name,1,-2)
if not basic_robot.data[pname] then basic_robot.data[pname] = {} end
-- all robots by player share same rom now
if not basic_robot.data[pname].rom then basic_robot.data[pname].rom = {} end -- create rom if not yet existing
local env = local env =
{ {
_Gerror = error,
pcall=pcall, pcall=pcall,
robot_version = function() return basic_robot.version end, robot_version = function() return basic_robot.version end,
boost = function(v) boost = function(v)
if math.abs(v)>2 then v = 0 end; local obj = basic_robot.data[name].obj; if math.abs(v)>2 then v = 0 end; local obj = basic_robot.data[name].obj;
if v == 0 then if v == 0 then
local pos = obj:getpos(); pos.x = math.floor(pos.x+0.5);pos.y = math.floor(pos.y+0.5); pos.z = math.floor(pos.z+0.5); local pos = obj:get_pos(); pos.x = math.floor(pos.x+0.5);pos.y = math.floor(pos.y+0.5); pos.z = math.floor(pos.z+0.5);
obj:setpos(pos); obj:set_velocity({x=0,y=0,z=0}); obj:setpos(pos); obj:set_velocity({x=0,y=0,z=0});
return return
end end
local yaw = obj:get_yaw(); local yaw = obj:get_yaw();
obj:set_velocity({x=v*math.cos(yaw),y=0,z=v*math.sin(yaw)}); obj:set_velocity({x=-v*math.sin(yaw),y=0,z=v*math.cos(yaw)});
end, end,
turn = { turn = {
@ -91,17 +75,19 @@ function getSandboxEnv (name)
return commands.craft(item, mode, idx, amount, name) return commands.craft(item, mode, idx, amount, name)
end, end,
pause = function() -- pause coroutine pause = function(amount) -- pause coroutine
if not basic_robot.data[name].cor then error("you must start program with '--coroutine' to use pause()") return end if not basic_robot.data[name].cor then error("you must start program with '--coroutine' to use pause()") return end
coroutine.yield() if not amount or basic_robot.data[name].operations<amount then
coroutine.yield()
end
end, end,
self = { self = {
pos = function() return basic_robot.data[name].obj:getpos() end, pos = function() return basic_robot.data[name].obj:get_pos() end,
spawnpos = function() local pos = basic_robot.data[name].spawnpos; return {x=pos.x,y=pos.y,z=pos.z} end, spawnpos = function() local pos = basic_robot.data[name].spawnpos; return {x=pos.x,y=pos.y,z=pos.z} end,
name = function() return name end, name = function() return name end,
operations = function() return basic_robot.data[name].operations end, operations = function() return basic_robot.data[name].operations end,
viewdir = function() local yaw = basic_robot.data[name].obj:getyaw(); return {x=math.cos(yaw), y = 0, z=math.sin(yaw)} end, viewdir = function() local yaw = basic_robot.data[name].obj:getyaw(); return {x=-math.sin(yaw), y = 0, z=math.cos(yaw)} end,
set_properties = function(properties) set_properties = function(properties)
if not properties then return end; local obj = basic_robot.data[name].obj; if not properties then return end; local obj = basic_robot.data[name].obj;
@ -113,7 +99,7 @@ function getSandboxEnv (name)
obj:set_animation({x=anim_start,y=anim_end}, anim_speed, anim_stand_start) obj:set_animation({x=anim_start,y=anim_end}, anim_speed, anim_stand_start)
end, end,
listen = function (mode) listen = function (mode) -- will robot listen to chat?
if mode == 1 then if mode == 1 then
basic_robot.data.listening[name] = true basic_robot.data.listening[name] = true
else else
@ -121,6 +107,24 @@ function getSandboxEnv (name)
end end
end, end,
listen_punch = function(pos, is_remove) -- robot will listen to punch events in 32 sized chunk containing pos
local round = math.floor;
local r = basic_robot.radius; local ry = 2*r; -- note: this is skyblock adjusted
if not pos then return end
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 hppos = minetest.hash_node_position(ppos)
local rname = basic_robot.data.punchareas[hppos];
if is_remove then -- remove listener
basic_robot.data.punchareas[hppos] = nil
return
end
if rname then -- area already registered for listening
return rname
end
basic_robot.data.punchareas[hppos] = name; -- register robot name
end,
listen_msg = function() listen_msg = function()
local msg = basic_robot.data[name].listen_msg; local msg = basic_robot.data[name].listen_msg;
local speaker = basic_robot.data[name].listen_speaker; local speaker = basic_robot.data[name].listen_speaker;
@ -153,7 +157,7 @@ function getSandboxEnv (name)
reset = function() reset = function()
local pos = basic_robot.data[name].spawnpos; local pos = basic_robot.data[name].spawnpos;
local obj = basic_robot.data[name].obj; local obj = basic_robot.data[name].obj;
obj:setpos({x=pos.x,y=pos.y+1,z=pos.z}); obj:setyaw(0); obj:set_pos({x=pos.x,y=pos.y+1,z=pos.z}); obj:set_yaw(0);
end, end,
set_libpos = function(pos) set_libpos = function(pos)
@ -171,7 +175,7 @@ function getSandboxEnv (name)
fire = function(speed, pitch,gravity, texture, is_entity) -- experimental: fires an projectile fire = function(speed, pitch,gravity, texture, is_entity) -- experimental: fires an projectile
local obj = basic_robot.data[name].obj; local obj = basic_robot.data[name].obj;
local pos = obj:getpos(); local pos = obj:get_pos();
local yaw = obj:getyaw()+ math.pi/2; local yaw = obj:getyaw()+ math.pi/2;
pitch = pitch*math.pi/180 pitch = pitch*math.pi/180
local velocity = {x=speed*math.cos(yaw)*math.cos(pitch), y=speed*math.sin(pitch),z=speed*math.sin(yaw)*math.cos(pitch)}; local velocity = {x=speed*math.cos(yaw)*math.cos(pitch), y=speed*math.sin(pitch),z=speed*math.sin(yaw)*math.cos(pitch)};
@ -253,16 +257,16 @@ function getSandboxEnv (name)
find_nodes = find_nodes =
function(nodename,r) function(nodename,r)
if r>8 then return false end if r>8 then return false end
local q = minetest.find_node_near(basic_robot.data[name].obj:getpos(), r, nodename); local q = minetest.find_node_near(basic_robot.data[name].obj:get_pos(), r, nodename);
if q==nil then return false end if q==nil then return false end
local p = basic_robot.data[name].obj:getpos() local p = basic_robot.data[name].obj:get_pos()
return math.sqrt((p.x-q.x)^2+(p.y-q.y)^2+(p.z-q.z)^2) return math.sqrt((p.x-q.x)^2+(p.y-q.y)^2+(p.z-q.z)^2)
end, -- in radius around position end, -- in radius around position
find_player = find_player =
function(r,pos) function(r,pos)
pos = pos or basic_robot.data[name].obj:getpos(); pos = pos or basic_robot.data[name].obj:get_pos();
if r>10 then return false end if r<0 or r>10 then return false end
local objects = minetest.get_objects_inside_radius(pos, r); local objects = minetest.get_objects_inside_radius(pos, r);
local plist = {}; local plist = {};
for _,obj in pairs(objects) do for _,obj in pairs(objects) do
@ -277,7 +281,12 @@ function getSandboxEnv (name)
player = { player = {
getpos = function(name) getpos = function(name)
local player = minetest.get_player_by_name(name); local player = minetest.get_player_by_name(name);
if player then return player:getpos() else return nil end if player then return player:get_pos() else return nil end
end,
getview = function(name)
local player = minetest.get_player_by_name(name);
if player then return player:get_look_dir() else return nil end
end, end,
connected = function() connected = function()
@ -326,6 +335,8 @@ function getSandboxEnv (name)
write = function(i,title,text) write = function(i,title,text)
if i<=0 or i > 32 then return nil end if i<=0 or i > 32 then return nil end
local inv = minetest.get_meta(basic_robot.data[name].spawnpos):get_inventory(); local inv = minetest.get_meta(basic_robot.data[name].spawnpos):get_inventory();
local stackname = inv:get_stack("library",i):get_name();
if basic_robot.data[name].authlevel<3 and (stackname ~= "default:book_written" and stackname~= "default:book") then return nil end
local stack = basic_robot.commands.write_book(basic_robot.data[name].owner,title,text); local stack = basic_robot.commands.write_book(basic_robot.data[name].owner,title,text);
if stack then inv:set_stack("library", i, stack) end if stack then inv:set_stack("library", i, stack) end
end end
@ -344,8 +355,8 @@ function getSandboxEnv (name)
end, end,
}, },
rom = basic_robot.data[name].rom, rom = basic_robot.data[pname].rom,
string = { string = {
byte = string.byte, char = string.char, byte = string.byte, char = string.char,
find = string.find, find = string.find,
@ -460,6 +471,14 @@ function getSandboxEnv (name)
for dir, dir_id in pairs(directions) do for dir, dir_id in pairs(directions) do
env.write_text[dir] = function(text) return commands.write_text(name, dir_id,text) end env.write_text[dir] = function(text) return commands.write_text(name, dir_id,text) end
end end
--farming specials
env.machine.place_seed = {}; local env_machine_place_seed = env.machine.place_seed
env.machine.dig_seed = {}; local env_machine_dig_seed = env.machine.dig_seed;
for dir, dir_id in pairs(directions) do
env_machine_place_seed[dir] = function(seedbookname) return commands.machine.place_seed(name,dir_id,seedbookname) end
env_machine_dig_seed[dir] = function(dir) return commands.machine.dig_seed(name,dir_id) end
end
if authlevel>=1 then -- robot privs if authlevel>=1 then -- robot privs
@ -492,7 +511,7 @@ function getSandboxEnv (name)
setfenv( ScriptFunc, basic_robot.data[name].sandbox ) setfenv( ScriptFunc, basic_robot.data[name].sandbox )
local Result, RuntimeError = pcall( ScriptFunc ); local _, RuntimeError = pcall( ScriptFunc );
if RuntimeError then if RuntimeError then
minetest.chat_send_player(name, "#code.run: run error " .. RuntimeError ) minetest.chat_send_player(name, "#code.run: run error " .. RuntimeError )
return false return false
@ -536,7 +555,7 @@ function getSandboxEnv (name)
set_triggers = function(triggers) commands.puzzle.set_triggers(pdata,triggers) end, -- FIX THIS! set_triggers = function(triggers) commands.puzzle.set_triggers(pdata,triggers) end, -- FIX THIS!
check_triggers = function(pname) check_triggers = function(pname)
local player = minetest.get_player_by_name(pname); if not player then return end local player = minetest.get_player_by_name(pname); if not player then return end
commands.puzzle.checkpos(pdata,player:getpos(),pname) commands.puzzle.checkpos(pdata,player:get_pos(),pname)
end, end,
add_particle = function(def) minetest.add_particle(def) end, add_particle = function(def) minetest.add_particle(def) end,
count_objects = function(pos,radius) return #minetest.get_objects_inside_radius(pos, math.min(radius,5)) end, count_objects = function(pos,radius) return #minetest.get_objects_inside_radius(pos, math.min(radius,5)) end,
@ -559,10 +578,13 @@ function getSandboxEnv (name)
end end
-- code checker -- code checker
-- bugfixes:
-- player Midskip found problem with code checking, fixed
check_code = function(code) check_code = function(code)
--"while ", "for ", "do ","goto ", --"while ", "for ", "do ","goto ",
local bad_code = {"repeat", "until", "_c_", "_G", "while%(", "while{", "pcall","%.%.[^%.]"} --,"\\\"", "%[=*%[","--[["} local bad_code = {"repeat", "until", "_G", "while%(", "while{", "pcall","[^%.]%.%.[^%.]","\\\"","\\\'","%[=*%["}
for _, v in pairs(bad_code) do for _, v in pairs(bad_code) do
if string.find(code, v) then if string.find(code, v) then
return v .. " is not allowed!"; return v .. " is not allowed!";
@ -570,8 +592,7 @@ check_code = function(code)
end end
end end
identify_strings = function(code) -- returns list of positions {start,end} of literal strings in lua code
local identify_strings = function(code) -- returns list of positions {start,end} of literal strings in lua code
local i = 0; local j; local _; local length = string.len(code); local i = 0; local j; local _; local length = string.len(code);
local mode = 0; -- 0: not in string, 1: in '...' string, 2: in "..." string, 3. in [==[ ... ]==] string local mode = 0; -- 0: not in string, 1: in '...' string, 2: in "..." string, 3. in [==[ ... ]==] string
@ -589,8 +610,12 @@ local identify_strings = function(code) -- returns list of positions {start,end}
for k=1,#modes do for k=1,#modes do
j = string.find(code,modes[k][1],i); j = string.find(code,modes[k][1],i);
if j and j<jmin then -- pick closest one if j and j<jmin then -- pick closest one
jmin = j if string.sub(code,j-1,j-1) ~="\\" then
mode = k jmin = j
mode = k
else
j=j+1;
end
end end
end end
if mode ~= 0 then -- found something if mode ~= 0 then -- found something
@ -601,10 +626,12 @@ local identify_strings = function(code) -- returns list of positions {start,end}
else else
_,j = string.find(code,modes[mode][2],i); -- search for closing pair _,j = string.find(code,modes[mode][2],i); -- search for closing pair
if not j then break end if not j then break end
if (mode~=2 or (string.sub(code,j-1,j-1) ~= "\\") or string.sub(code,j-2,j-1) == "\\\\") then -- not (" and not \" - but "\\" is allowed) local pchar = string.sub(code,j-1,j-1)
if (mode~=2 or (pchar ~= "\\") or string.sub(code,j-2,j-1) == "\\\\") then -- not (" and not \" - but "\\" is allowed)
ret[#ret][2] = j ret[#ret][2] = j
mode = 0 mode = 0
end end
if pchar == "\\" then j=j+1 end
end end
i=j -- move to next position i=j -- move to next position
end end
@ -650,11 +677,11 @@ preprocess_code = function(script, call_limit) -- version 07/24/2018
when counter exceeds limit exit with error when counter exceeds limit exit with error
--]] --]]
script = script:gsub("%-%-%[%[.*%-%-%]%]",""):gsub("%-%-[^\n]*\n","\n") -- strip comments --script = script:gsub("%-%-%[%[.*%-%-%]%]",""):gsub("%-%-[^\n]*\n","\n") -- strip comments
script = string.gsub(script,"%-%-[^\n]+\n","\n") -- strip single line comments, multiline not allowed
-- process script to insert call counter in every function -- process script to insert call counter in every function
local _increase_ccounter = " _c_ = _c_ + 1; if _c_ > " .. call_limit .. local _increase_ccounter = " _Gc = _Gc + 1; if _Gc > " .. call_limit ..
" then _G.error(\"Execution count \".. _c_ .. \" exceeded ".. call_limit .. "\") end; " " then _Gerror(\"Execution count \".. _Gc .. \" exceeded ".. call_limit .. "\") end; "
local i1=0; local i2 = 0; local i1=0; local i2 = 0;
local found = true; local found = true;
@ -713,7 +740,7 @@ preprocess_code = function(script, call_limit) -- version 07/24/2018
-- must reset ccounter when paused, but user should not be able to force reset by modifying pause! -- must reset ccounter when paused, but user should not be able to force reset by modifying pause!
-- (suggestion about 'pause' by Kimapr, 09/26/2019) -- (suggestion about 'pause' by Kimapr, 09/26/2019)
return "_c_ = 0 local _pause_ = pause pause = function() _c_ = 0; _pause_() end " .. script; return "_Gc = 0 local _Gpause = pause pause = function(amount) _Gc = 0; _Gpause(amount) end " .. script;
--return script:gsub("pause%(%)", "_c_ = 0; pause()") -- reset ccounter at pause --return script:gsub("pause%(%)", "_c_ = 0; pause()") -- reset ccounter at pause
end end
@ -770,7 +797,7 @@ local function runSandbox( name)
end end
data.operations = basic_robot.maxoperations; data.operations = basic_robot.maxoperations;
data.t = os.clock() data.t = os.clock() -- timing
setfenv( ScriptFunc, data.sandbox ) setfenv( ScriptFunc, data.sandbox )
@ -830,25 +857,28 @@ local robot_spawner_update_form = function (pos, mode)
form = form =
"size[9.5,8]" .. -- width, height "size[9.5,8]" .. -- width, height
"textarea[1.25,-0.25;8.75,9.8;code;;".. code.."]".. "style_type[textarea;font_size=12;font=mono;bgcolor=#000000;textcolor=#00FF00;border=false]"..
"button[-0.25,7.5;1.25,1;EDIT;EDIT]".. "style_type[button;font_size=14;font=mono;bgcolor=#000000;border=false]"..
"button_exit[-0.25,-0.25;1.25,1;OK;SAVE]".. "style_type[button_exit;font_size=14;font=mono;bgcolor=#000000;border=false]"..
"button_exit[-0.25, 0.75;1.25,1;spawn;START]".. "textarea[1.25,-0.25;8.8,10.25;code;;".. code.."]"..
"button[-0.25, 1.75;1.25,1;despawn;STOP]".. "button[-0.15,7.5;1.25,1;EDIT;EDIT]"..
"field[0.25,3.;1.,1;id;id;"..id.."]".. "button[-0.15,-0.25;1.25,1;OK;"..minetest.colorize("yellow","SAVE").."]"..
"button[-0.25, 3.6;1.25,1;inventory;storage]".. "button_exit[-0.15, 0.75;1.25,1;spawn;"..minetest.colorize("green","START").."]"..
"button[-0.25, 4.6;1.25,1;library;library]".. "button[-0.15, 1.75;1.25,1;despawn;"..minetest.colorize("red","STOP").."]"..
"button[-0.25, 5.6;1.25,1;help;help]"; "field[0.15,3.;1.2,1;id;id;"..id.."]"..
"button[-0.15, 3.6;1.25,1;inventory;STORAGE]"..
"button[-0.15, 4.6;1.25,1;library;LIBRARY]"..
"button[-0.15, 5.6;1.25,1;help;HELP]";
else -- when robot clicked else -- when robot clicked
form = form =
"size[9.5,8]" .. -- width, height "size[9.5,8]" .. -- width, height
"textarea[1.25,-0.25;8.75,9.8;code;;".. code.."]".. "textarea[1.25,-0.25;8.75,10.25;code;;".. code.."]"..
"button_exit[-0.25,-0.25;1.25,1;OK;SAVE]".. "button_exit[-0.15,-0.25;1.25,1;OK;SAVE]"..
"button[-0.25, 1.75;1.25,1;despawn;STOP]".. "button[-0.15, 1.75;1.25,1;despawn;STOP]"..
"button[-0.25, 3.6;1.25,1;inventory;storage]".. "button[-0.15, 3.6;1.25,1;inventory;storage]"..
"button[-0.25, 4.6;1.25,1;library;library]".. "button[-0.15, 4.6;1.25,1;library;library]"..
"button[-0.25, 5.6;1.25,1;help;help]"; "button[-0.15, 5.6;1.25,1;help;help]";
end end
@ -882,10 +912,10 @@ code_edit_form = function(pos,name)
local list = ""; local list = "";
for _,line in pairs(lines) do list = list .. minetest.formspec_escape(line) .. "," end for _,line in pairs(lines) do list = list .. minetest.formspec_escape(line) .. "," end
local form = "size[12,9.25]" .. "textlist[0,0;12,8;listname;" .. list .. ";"..selection..";false]" .. local form = "size[12,9.25]" .. "textlist[0,0;12,8;listname;" .. list .. ";"..selection..";false]" ..
"button[10,8;2,1;INSERT;INSERT LINE]" .. "button[10,8;2.25,1;INSERT;INSERT LINE]" ..
"button[10,8.75;2,1;DELETE;DELETE LINE]" .. "button[10,8.75;2.25,1;DELETE;DELETE LINE]" ..
"button_exit[2,8.75;2,1;SAVE;SAVE CODE]" .. "button_exit[2.25,8.75;2.25,1;SAVE;SAVE CODE]" ..
"button[0,8.75;2,1;UPDATE;UPDATE LINE]".. "button[0,8.75;2.25,1;UPDATE;UPDATE LINE] label[4.25,9; LINE " .. selection .."]"..
"textarea[0.25,8;10,1;input;;".. input .. "]" "textarea[0.25,8;10,1;input;;".. input .. "]"
return form return form
end end
@ -949,6 +979,11 @@ minetest.register_entity("basic_robot:robot",{
return; return;
end end
if data.object and data.object~=self.object then -- is there another one already?
self.object:remove();
return;
end
self.owner = data.owner; self.owner = data.owner;
self.authlevel = data.authlevel; self.authlevel = data.authlevel;
@ -1011,7 +1046,7 @@ minetest.register_entity("basic_robot:robot",{
minetest.set_node(pos, {name = "air"}); minetest.set_node(pos, {name = "air"});
--local privs = core.get_player_privs(self.owner);privs.interact = false; --local privs = core.get_player_privs(self.owner);privs.interact = false;
--core.set_player_privs(self.owner, privs); minetest.auth_reload() --core.set_player_privs(self.owner, privs); minetest.auth_reload()
minetest.kick_player(self.owner, "#basic_robot: stack overflow") minetest.kick_player(self.owner, "#basic_robot: stack overflow at " .. pos.x.. " " .. pos.y .. " " .. pos.z)
end end
local name = self.name; local name = self.name;
@ -1038,8 +1073,6 @@ minetest.register_entity("basic_robot:robot",{
end, end,
}) })
local spawn_robot = function(pos,node,ttl) local spawn_robot = function(pos,node,ttl)
if type(ttl) ~= "number" then ttl = 0 end if type(ttl) ~= "number" then ttl = 0 end
if ttl<0 then return end if ttl<0 then return end
@ -1076,7 +1109,9 @@ local spawn_robot = function(pos,node,ttl)
if id <= 0 then -- just compile code and run it, no robot entity spawn if id <= 0 then -- just compile code and run it, no robot entity spawn
local codechange = false; local codechange = false; -- was code changed by editing?
local poschange = false; -- are we running from different spawner?
if meta:get_int("codechange") == 1 then if meta:get_int("codechange") == 1 then
meta:set_int("codechange",0); meta:set_int("codechange",0);
codechange = true; codechange = true;
@ -1099,7 +1134,7 @@ local spawn_robot = function(pos,node,ttl)
if not data.obj then if not data.obj then
--create virtual robot that reports position and other properties --create virtual robot that reports position and other properties
local obj = {}; local obj = {};
function obj:getpos() return {x=pos.x,y=pos.y,z=pos.z} end function obj:get_pos() return {x=pos.x,y=pos.y,z=pos.z} end
function obj:getyaw() return 0 end function obj:getyaw() return 0 end
function obj:get_luaentity() function obj:get_luaentity()
local luaent = {}; local luaent = {};
@ -1113,8 +1148,10 @@ local spawn_robot = function(pos,node,ttl)
end end
end end
local hashpos = minetest.hash_node_position(pos)
if data.lastpos~= hashpos then data.lastpos = hashpos; poschange = true end
if not data.bytecode then if not data.bytecode or poschange then -- compile again
local script = meta:get_string("code"); local script = meta:get_string("code");
if data.authlevel<3 then -- not admin if data.authlevel<3 then -- not admin
@ -1181,7 +1218,6 @@ local spawn_robot = function(pos,node,ttl)
if data == nil then if data == nil then
basic_robot.data[name] = {}; basic_robot.data[name] = {};
data = basic_robot.data[name]; data = basic_robot.data[name];
--data.rom = {};
end end
data.owner = owner; data.owner = owner;
@ -1280,6 +1316,8 @@ local on_receive_robot_form = function(pos, formname, fields, sender)
return return
end end
end end
minetest.chat_send_player(name,"#ROBOT: code saved " .. os.date("%H:%M:%S"))
meta:set_string("code", code); meta:set_int("codechange",1) meta:set_string("code", code); meta:set_int("codechange",1)
end end
@ -1509,9 +1547,9 @@ minetest.register_on_player_receive_fields(
local title,text = basic_robot.commands.read_book(itemstack); local title,text = basic_robot.commands.read_book(itemstack);
title = title or ""; text = text or ""; title = title or ""; text = text or "";
local dtitle = minetest.formspec_escape(title); local dtitle = minetest.formspec_escape(title);
local form = "size [8,8] textarea[0.,0.;8.75,8.5;book; TITLE : " .. minetest.formspec_escape(title) .. ";" .. local form = "size [8,8] textarea[0.,0.15;8.75,8.35;book; TITLE : " .. minetest.formspec_escape(title) .. ";" ..
minetest.formspec_escape(text) .. "] button_exit[-0.25,7.5;1.25,1;OK;SAVE] ".. minetest.formspec_escape(text) .. "] button_exit[-0.25,7.5;1.25,1;OK;SAVE] "..
"button_exit[1.,7.5;2.75,1;LOAD;USE AS PROGRAM] field[4,8;4.5,0.5;title;title;"..dtitle.."]"; "button_exit[0.9,7.5;3,1;LOAD;USE AS PROGRAM] field[4,8;4.5,0.5;title;title;"..dtitle.."]";
minetest.show_formspec(player:get_player_name(), "robot_book_".. sel.. ":".. minetest.pos_to_string(libpos), form); minetest.show_formspec(player:get_player_name(), "robot_book_".. sel.. ":".. minetest.pos_to_string(libpos), form);
end end
@ -1662,9 +1700,11 @@ minetest.register_node("basic_robot:spawner", {
paramtype = "light", paramtype = "light",
param1=1, param1=1,
walkable = true, walkable = true,
alpha = 150,
after_place_node = function(pos, placer) after_place_node = function(pos, placer)
local meta = minetest.env:get_meta(pos)
local meta = minetest.get_meta(pos)
--local meta = minetest.env:get_meta(pos) -- THIS WORKS, DOES CHANGING THIS CAUSE META PROBLEM?
local owner = placer:get_player_name(); local owner = placer:get_player_name();
meta:set_string("owner", owner); meta:set_string("owner", owner);
@ -1681,11 +1721,38 @@ minetest.register_node("basic_robot:spawner", {
meta:set_string("infotext", "robot spawner (owned by ".. placer:get_player_name() .. ")") meta:set_string("infotext", "robot spawner (owned by ".. placer:get_player_name() .. ")")
meta:set_string("libpos",pos.x .. " " .. pos.y .. " " .. pos.z) meta:set_string("libpos",pos.x .. " " .. pos.y .. " " .. pos.z)
local inv = meta:get_inventory(); -- spawner inventory
inv:set_size("main",32);
inv:set_size("library",16); --4*4
robot_spawner_update_form(pos); robot_spawner_update_form(pos);
--[[
local meta = minetest:get_meta(pos)
local owner = placer:get_player_name();
meta:set_string("owner", owner);
local authlevel = get_authlevel(placer:get_player_name());
meta:set_int("authlevel",authlevel)
local sec_hash = minetest.get_password_hash("",authlevel .. owner .. basic_robot.password) -- 'digitally sign' authlevel using password
meta:set_string("sec_hash", sec_hash);
meta:set_string("code","");
meta:set_int("id",1); -- initial robot id
meta:set_string("name", placer:get_player_name().."1")
meta:set_string("infotext", "robot spawner (owned by ".. placer:get_player_name() .. ")")
--WTF: here it reads correct meta but later there is none!!
minetest.chat_send_all("D meta info " .. meta:get_string("infotext"))
meta:set_string("libpos",pos.x .. " " .. pos.y .. " " .. pos.z)
local inv = meta:get_inventory(); -- spawner inventory local inv = meta:get_inventory(); -- spawner inventory
inv:set_size("main",32); inv:set_size("main",32);
inv:set_size("library",16); --4*4 inv:set_size("library",16); --4*4
robot_spawner_update_form(pos);
--]]
end, end,
mesecons = {effector = { mesecons = {effector = {
@ -1694,6 +1761,11 @@ minetest.register_node("basic_robot:spawner", {
} }
}, },
effector = {
action_on = spawn_robot,
action_off = despawn_robot
},
on_receive_fields = on_receive_robot_form, on_receive_fields = on_receive_robot_form,
allow_metadata_inventory_put = function(pos, listname, index, stack, player) allow_metadata_inventory_put = function(pos, listname, index, stack, player)
@ -1718,6 +1790,7 @@ minetest.register_node("basic_robot:spawner", {
end, end,
can_dig = function(pos, player) can_dig = function(pos, player)
if not player then return end
if minetest.is_protected(pos, player:get_player_name()) then return false end if minetest.is_protected(pos, player:get_player_name()) then return false end
local meta = minetest.get_meta(pos); local meta = minetest.get_meta(pos);
if not meta:get_inventory():is_empty("main") or not meta:get_inventory():is_empty("library") then return false end if not meta:get_inventory():is_empty("main") or not meta:get_inventory():is_empty("library") then return false end
@ -1749,6 +1822,8 @@ end
-- remote control -- remote control
local write_keyevent = basic_robot.commands.write_keyevent;
minetest.register_craftitem("basic_robot:control", { minetest.register_craftitem("basic_robot:control", {
description = "Robot remote control", description = "Robot remote control",
inventory_image = "control.png", inventory_image = "control.png",
@ -1783,10 +1858,13 @@ minetest.register_craftitem("basic_robot:control", {
local pos = pointed_thing.under local pos = pointed_thing.under
if not pos then return end if not pos then return end
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 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 hppos = minetest.hash_node_position(ppos)
local data = basic_robot.data[name]; local rname = basic_robot.data.punchareas[hppos];
if data then data.keyboard = {x=pos.x,y=pos.y,z=pos.z, puncher = owner, type = 0} end local data = basic_robot.data[rname];
if data then
write_keyevent(data,pos, owner,0)
end
return return
end end
@ -1831,9 +1909,9 @@ minetest.register_craftitem("basic_robot:control", {
minetest.chat_send_player(owner, "#remote control: compile error " .. CompileError ) minetest.chat_send_player(owner, "#remote control: compile error " .. CompileError )
return return
end end
script = preprocess_code(script,basic_robot.call_limit[data.authlevel+1]);
setfenv( ScriptFunc, basic_robot.data[name].sandbox ) setfenv( ScriptFunc, basic_robot.data[name].sandbox )
local Result, RuntimeError = pcall( ScriptFunc ); local Result, RuntimeError = pcall( ScriptFunc );
if RuntimeError then if RuntimeError then
minetest.chat_send_player(owner, "#remote control: run error " .. RuntimeError ) minetest.chat_send_player(owner, "#remote control: run error " .. RuntimeError )
@ -1873,7 +1951,7 @@ minetest.register_entity(
if (self.oldvel.x~=0 and vel.x==0) or (self.oldvel.y~=0 and vel.y==0) or (self.oldvel.z~=0 and vel.z==0) then -- hit if (self.oldvel.x~=0 and vel.x==0) or (self.oldvel.y~=0 and vel.y==0) or (self.oldvel.z~=0 and vel.z==0) then -- hit
local data = basic_robot.data[self.name]; local data = basic_robot.data[self.name];
if data then if data then
data.fire_pos = self.object:getpos(); data.fire_pos = self.object:get_pos();
end end
self.object:remove() self.object:remove()
return return
@ -1888,7 +1966,7 @@ minetest.register_entity(
if not self.state then return nil end if not self.state then return nil end
local data = basic_robot.data[self.name]; local data = basic_robot.data[self.name];
if data then if data then
data.fire_pos = self.object:getpos(); data.fire_pos = self.object:get_pos();
end end
self.object:remove(); self.object:remove();
return nil return nil

4
mod.conf Normal file
View File

@ -0,0 +1,4 @@
name = basic_robot
depends = default
optional_depends =
description = your ingame robot companion. learn how to program and make games

View File

@ -105,7 +105,7 @@ minetest.register_on_player_receive_fields(
-- }) -- })
-- end -- end
local help_address = {}; -- array containing current page name for player local help_address = {}; -- table containing current page name for player [name] = {address = ..., sel = ..}
local help_pages = { local help_pages = {
["main"] = { ["main"] = {
" === ROBOT HELP - MAIN SCREEN === ","", " === ROBOT HELP - MAIN SCREEN === ","",
@ -145,7 +145,8 @@ local help_pages = {
" 7. [TECHNIC FUNCTIONALITY]", " 7. [TECHNIC FUNCTIONALITY]",
" 8. [CRYPTOGRAPHY]", " 8. [CRYPTOGRAPHY]",
" 9. [PUZZLE]", " 9. [PUZZLE]",
" 10.[COROUTINES] - easier alternative to finite state machines", " 10.[COROUTINES AND LIBRARIES AND ROM] - easier alternative to",
" finite state machines",
}, },
["MOVEMENT DIGGING PLACING NODE SENSING"] = { ["MOVEMENT DIGGING PLACING NODE SENSING"] = {
@ -201,7 +202,7 @@ local help_pages = {
" attack(target) attempts to attack target player if nearby", " attack(target) attempts to attack target player if nearby",
" grab(target) attempt to grab target player if nearby and returns", " grab(target) attempt to grab target player if nearby and returns",
" true if succesful", " true if succesful",
" player.getpos(name) return position of player, player.connected()", " player.get_pos(name) return position of player, player.connected()",
" returns list of connected players names", " returns list of connected players names",
}, },
@ -244,12 +245,14 @@ local help_pages = {
["KEYBOARD AND USER INTERACTIONS"] = { ["KEYBOARD AND USER INTERACTIONS"] = {
"back to [Commands reference]", "back to [Commands reference]",
"KEYBOARD","", "KEYBOARD","",
" EVENTS : place spawner at coordinates (r*i,2*r*j+1,r*k) to monitor", " EVENTS : first attach listener to robot with self.listen_punch",
" events. value of r is ".. basic_robot.radius, " 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 = .. }", " keyboard.get() returns table {x=..,y=..,z=..,puncher = .. , type = .. }",
" for keyboard event", " if there was keyboard event, nil if there was none",
" keyboard.set(pos,type) set key at pos of type 0=air,1-6,7-15,16-271,", " keyboard.set(pos,type) set key as a node in world at pos of type",
" limited to range 10 around spawner", " 0=air,1-6,7-15,16-271, limited to range 10 around spawner",
" keyboard.read(pos) return node name at pos", " keyboard.read(pos) return node name at pos",
}, },
@ -272,6 +275,9 @@ local help_pages = {
" materials", " materials",
" compress(input) - requires upgrades - energy intensive process", " compress(input) - requires upgrades - energy intensive process",
" transfer_power(amount,target_robot_name)", " 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"] = { ["CRYPTOGRAPHY"] = {
@ -307,17 +313,29 @@ local help_pages = {
" add_particle(def)" " add_particle(def)"
}, },
["COROUTINES"] = { ["COROUTINES AND LIBRARIES AND ROM"] = {
"back to [Commands reference]", "back to [Commands reference]",
"COROUTINES","", "COROUTINES","",
"robot can run code using lua coroutines. To enable this mode just put the word", "robot can run code using lua coroutines. To enable this mode just put",
"coroutine in the first 32 characters of your program. Example: ", "", "the word coroutine in the first 32 characters of your program. Example:",
" --testing program for coroutine", " --testing program for coroutine",
" for i = 1,5 do ", " for i = 1,5 do ",
" say(i); dig.forward(); move.forward()", " say(i); dig.forward(); move.forward()",
" pause()", " pause()",
" end", " 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",
}, },
@ -328,15 +346,18 @@ end
local robot_show_help = function(pname) --formname: robot_help local robot_show_help = function(pname) --formname: robot_help
local address = help_address[pname] or "main"; 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 ) --minetest.chat_send_all("D DISPLAY HELP for ".. address )
local pages = help_pages[address]; local pages = help_pages[address];
if not pages then return end
local content = table.concat(pages,",") local content = table.concat(pages,",")
local size = 9; local vsize = 8.75; local size = 9.5; local vsize = 8.75;
local form = "size[" .. size .. "," .. size .. "] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]"; 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.chat_send_all("D " .. form)
minetest.show_formspec(pname, "robot_help", form) minetest.show_formspec(pname, "robot_help", form)
return return
@ -346,19 +367,31 @@ end
robogui["robot_help"] = { robogui["robot_help"] = {
response = function(player,formname,fields) response = function(player,formname,fields)
local name = player:get_player_name() local name = player:get_player_name()
local fsel = fields.wiki; local fsel = fields.wiki;
if fsel and string.sub(fsel,1,3) == "DCL" then --minetest.chat_send_all("D " .. minetest.serialize(fields))
local sel = tonumber(string.sub(fsel,5)) or 1; -- selected line
local address = help_address[name] or "main";
local pages = help_pages[address]; local go = false;
if fsel then
local link = string.match(pages[sel] or "", "\\%[([%w%s]+)\\%]") help_address[name].sel = tonumber(string.sub(fsel or "",5)) or 1
if help_pages[link] then if string.sub(fsel,1,3) == "CHG" then return end -- just select topic, dont go there
help_address[name] = link; go = true -- we want to go to selected page
robot_show_help(name)
end
end 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, end,
getForm = function(player_name) getForm = function(player_name)

View File

@ -1,9 +1,33 @@
--battle bots
-- example program for team 4, other team is 5. each of players writes program and puts it in book,
-- book 1 program for team 4, book 2 program for team 5
--[[
DEMO PROGRAM: just move all your bots in direction 1,0 and attack while moving
for i = 1,#bots[TYPE] do -- move all bots of team 4 in direction {x=1,z=0}
if read_bots(TYPE,i) then -- is bot alive?
move_bot(i,1,0)
attack_bot(i,1,0) -- try to attack in move direction
end
end
--]]
if not s then if not s then
-- init -- init
bots = {[4] = {}, [5] = {}}; -- [type] = {{1,1,10}, {3,2,10}}; -- {x,y,hp} bots = {[4] = {}, [5] = {}}; -- [type] = {{1,1,10}, {3,2,10}}; -- {x,y,hp}
arena = {}; --[x][z] = {type, idx} arena = {}; --[x][z] = {type, idx}
for i = -10,10 do arena[i] = {} for j=-10,10 do arena[i][j] = {0,0} end end
centerpos = self.spawnpos(); centerpos.y = centerpos.y+2 centerpos = self.spawnpos(); centerpos.y = centerpos.y+2
for i = -10,10 do arena[i] = {} for j=-10,10 do
arena[i][j] = {0,0}
keyboard.set({x=centerpos.x+i,y=centerpos.y-1,z=centerpos.z+j},1) -- build arena
keyboard.set({x=centerpos.x+i,y=centerpos.y,z=centerpos.z+j},0)
end end
TYPE = 4; -- 4,5 defines which bots are on the move/attack TYPE = 4; -- 4,5 defines which bots are on the move/attack
DIR = 1 DIR = 1
s=0 s=0
@ -67,7 +91,7 @@ prog1, _ = _G.loadstring( script1 ); prog2, _ = _G.loadstring( script2 );
end end
end end
if t%10 == 0 then if t%10 == 0 then -- spawn new bot for each time every 10 seconds!
spawn_bot(0,-10,4) spawn_bot(0,-10,4)
spawn_bot(0,10,5) spawn_bot(0,10,5)
end end

View File

@ -1,4 +1,11 @@
-- COPY PASTE ROBOT by rnd: c1 c2 r = markers, c = copy p = paste -- COPY PASTE ROBOT by rnd: c1 c2 r = markers, c = copy p = paste, rotz = rotate 90 deg cw z-axis, s = set
-- command parameters :
-- s nodename node_step
-- copy:
-- c1,c2 set markers, r set reference, c = copy,
-- p = paste at player position (reference is put there)
-- rotate:
-- c1,c2 set area, rotz = rotate 90 deg around z-axis
if not paste then if not paste then
_G.minetest.forceload_block(self.pos(),true) _G.minetest.forceload_block(self.pos(),true)
@ -18,36 +25,38 @@ if not paste then
pos = pos, pos = pos,
expirationtime = 10, expirationtime = 10,
velocity = {x=0, y=0,z=0}, velocity = {x=0, y=0,z=0},
size = 18, size = 9,
texture = label..".png", texture = label..".png",
acceleration = {x=0,y=0,z=0}, acceleration = {x=0,y=0,z=0},
collisiondetection = true,
collision_removal = true,
}) })
end end
self.listen(1) self.listen(1)
self.label("COPY-PASTE MASTER v1.2 gold edition. commands: c1 c2 r c p") self.label("")
-- self.label("WorldEdit Bot\ncommands: c1 c2 r c p s rotz")
end end
speaker, msg = self.listen_msg() speaker, msg = self.listen_msg()
if speaker == "rnd" then if speaker == "_" or speaker == "test" then
local args = {}
for word in string.gmatch(msg,"%S+") do args[#args+1]=word end
local player = _G.minetest.get_player_by_name(speaker); local player = _G.minetest.get_player_by_name(speaker);
local p = player:getpos(); p.x = round(p.x); p.y=round(p.y); p.z = round(p.z); local p = player:getpos(); p.x = round(p.x); p.y=round(p.y); p.z = round(p.z);
if p.y<0 then p.y = p.y +1 end -- needed cause of minetest bug if p.y<0 then p.y = p.y +1 end -- needed cause of minetest bug
if msg == "c1" then if args[1] == "c1" then
paste.src1 = {x=p.x,y=p.y,z=p.z};say("marker 1 set at " .. p.x .. " " .. p.y .. " " .. p.z) paste.src1 = {x=p.x,y=p.y,z=p.z};say("marker 1 set at " .. p.x .. " " .. p.y .. " " .. p.z,speaker)
display_marker(p,"099") -- c display_marker(p,"099") -- c
elseif msg == "c2" then elseif args[1] == "c2" then
paste.src2 = {x=p.x,y=p.y,z=p.z};say("marker 2 set at " .. p.x .. " " .. p.y .. " " .. p.z) paste.src2 = {x=p.x,y=p.y,z=p.z};say("marker 2 set at " .. p.x .. " " .. p.y .. " " .. p.z,speaker)
display_marker(p,"099") -- c display_marker(p,"099") -- c
elseif msg == "r" then elseif args[1] == "r" then
paste.ref = {x=p.x,y=p.y,z=p.z};say("reference set at " .. p.x .. " " .. p.y .. " " .. p.z) paste.ref = {x=p.x,y=p.y,z=p.z};say("reference set at " .. p.x .. " " .. p.y .. " " .. p.z,speaker)
display_marker(p,"114") -- r display_marker(p,"114") -- r
elseif msg == "c" then -- copy elseif args[1] == "c" then -- copy
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z); local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z); local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
local count = 0; data = {}; local count = 0; data = {};
@ -65,7 +74,7 @@ if speaker == "rnd" then
end end
end end
say(count .. " nodes copied "); say(count .. " nodes copied ");
elseif msg == "p" then -- paste elseif args[1] == "p" then -- paste
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z); local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z); local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
local count = 0; p.x = p.x-paste.ref.x; p.y = p.y-paste.ref.y; p.z = p.z-paste.ref.z; local count = 0; p.x = p.x-paste.ref.x; p.y = p.y-paste.ref.y; p.z = p.z-paste.ref.z;
@ -85,6 +94,82 @@ if speaker == "rnd" then
end end
end end
end end
say(count .. " nodes pasted "); say(count .. " nodes pasted ",speaker);
elseif args[1] == "s" then -- set node
local nodename = args[2] or "air"
local step = args[3] or 1;
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
local count = 0;
for i = x1,x2,step do
for j = y1,y2,step do
for k = z1,z2,step do
minetest.set_node({x=i,y=j,z=k}, {name = nodename});
end
end
end
say((x2-x1+1)*(y2-y1+1)*(z2-z1+1) .. " nodes set to " .. nodename,speaker)
elseif args[1] == "rotz" then -- rotate around z axis, center of selection
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
local d = x2-x1; if z2-z1<d then z2 = z1+d else d = z2-z1; x2 = x1+d end -- will be rotated as square in xz
local rotzd = {
[0]=1,[1]=2,[2]=3,[3]=0,
[7]=12,[12]=9,[9]=18,[18]=7,
[8]=17,[17]=6,[6]=15,[15]=8,
[19]=4,[4]=13,[13]=10,[10]=19,
[20]=23,[23]=22,[22]=21,[21]=20,
}
local count = 0; local step = 1
local data = {}; -- copy first
for i = x1,x2 do
for j = y1,y2 do
for k = z1,z2 do
local node = _G.minetest.get_node({x=i,y=j,z=k});
minetest.swap_node({x=i,y=j,z=k},{name = "air"})
if node.name ~= "air" then
if not data[i] then data[i]= {} end
if not data[i][j] then data[i][j]= {} end
data[i][j][k] = node
count = count +1;
end
end
end
end
-- (x,z)->(z,-x)
-- square rotate around center: x,z -> x-dx/2, z-dz/2 ->z-dz/2,dx/2-x -> (z, dx-x)
-- (x,z) -> (z,x1+x2-x)
--[[
x1,z1
*
* x1,z1
add offset to put middle of square into 0,0 and then back..
x->x-x1-dx/2, z-z1-dz/2 -> z-z1-dz/2, x1-dx/2-x ->
z-z1-dz/2+x1+dx/2,x1-dx/2-x+z1+dz/2 =
z-z1+x1,z1+x1-x
--]]
for i = x1,x2,step do
for j = y1,y2,step do
for k = z1,z2,step do
local pdata;
if data[i] and data[i][j] and data[i][j][k] then
pdata = data[i][j][k]
end
if pdata then -- correct position, rotated 90deg, TODO!
local node = pdata
node.param2 = rotzd[node.param2] or 0;
minetest.swap_node({x=k+x1-z1,y=j,z=x1+z1-i}, node)
end
end
end
end
say(count .. " nodes rotated around z-axis");
end end
end end

View File

@ -0,0 +1,141 @@
-- 2d rubik cube slide puzzle by rnd in 40 mins
if not init then init = true
m=4;
n=m;
data = {};
nodelist = {};
for i = 1,m do
for j = 1,n do
nodelist[(i-1)*n+j] = "basic_robot:button_"..(64+(n-j+1-1)*n+i)
end
end
for i = 1,m do
data[i] = {};local dat = data[i];
for j = 1,n do
dat[j] = (i-1)*n+j;
end
end
spos = self.spawnpos(); spos.x=spos.x+1; spos.z=spos.z+1
render = function(t,mode) -- mode 1:row, 2: coloumn
if not mode then
for i=1,m do
for j = 1,n do
minetest.swap_node({x=spos.x+i,y=spos.y,z=spos.z+j},{name = nodelist[data[i][j]]})
end
end
return
end
if mode == 1 then -- row only
for i=1,m do
minetest.swap_node({x=spos.x+i,y=spos.y,z=spos.z+t},{name = nodelist[data[i][t]]})
end
return
else -- coloumn only
for j=1,n do
minetest.swap_node({x=spos.x+t,y=spos.y,z=spos.z+j},{name = nodelist[data[t][j]]})
end
return
end
end
pnames = find_player(4);
if not pnames then self.remove() end
player = minetest.get_player_by_name(pnames[1]);
check_rot_dir = function()
local vdir = player:get_look_dir()
local mode = 0;
if math.abs(vdir.x)<math.abs(vdir.z) then
if vdir.z>0 then
mode = 2
else
mode = -2
end
else
if vdir.x>0 then
mode = 1
else
mode = -1
end
end -- rotate in z dir
return mode
end
rotx = function(col,dir)
if dir > 0 then
local tmp = data[1][col]
for i = 1,m-1 do
data[i][col] = data[i+1][col]
end
data[m][col] = tmp
else
local tmp = data[m][col]
for i = m,2,-1 do
data[i][col] = data[i-1][col]
end
data[1][col] = tmp
end
end
rotz = function(row,dir)
if dir > 0 then
local tmp = data[row][1]
for j = 1,n-1 do
data[row][j] = data[row][j+1]
end
data[row][m] = tmp
else
local tmp = data[row][n]
for j = n,2,-1 do
data[row][j] = data[row][j-1]
end
data[row][1] = tmp
end
end
rndshuffle = function(steps)
for step = 1,steps do
local mode = math.random(4);
if mode <=2 then
local z = math.random(m);
if mode == 2 then mode = -1 end
rotx(z,mode)
else
local x = math.random(n);
if mode == 3 then mode = -2 else mode = 2 end
rotz(x,mode)
end
end
end
self.listen_punch(self.pos())
self.label("try to get all letters sorted started with A top left")
rndshuffle(m*n)
render()
end
event = keyboard.get()
if event then
--self.label(serialize(event))
local x = event.x-spos.x;
local z = event.z-spos.z;
local mode = check_rot_dir()
if x>0 and x<=m and z>0 and z<=n then
if math.abs(mode) == 1 then
rotx(z,-mode)
render(z,1)
else
rotz(x,-mode)
render(x,2)
end
end
end

View File

@ -1,3 +1,5 @@
-- battle minesweeper, 2 player
if not data then if not data then
m=10;n=10; m=10;n=10;
players = {}; players = {};
@ -13,6 +15,7 @@ if not data then
state = SIGNUP state = SIGNUP
t0 = _G.minetest.get_gametime();spawnpos = self.spawnpos() -- place mines t0 = _G.minetest.get_gametime();spawnpos = self.spawnpos() -- place mines
self.listen_punch(self.pos()); -- attach punch listener
data = {}; data = {};
init_game = function() init_game = function()

View File

@ -1,208 +1,211 @@
--black box by rnd, 03/18/2017 --black box by rnd, 03/18/2017
--https://en.wikipedia.org/wiki/Black_Box_(game) --https://en.wikipedia.org/wiki/Black_Box_(game)
if not data then if not data then
m=16;n=16; -- novice: 8x8, 4
atoms = 32 m=9;n=9;
attempts = 1;turn = 0; atoms = 16
spawnpos = self.spawnpos(); spawnpos.x = spawnpos.x-m/2; spawnpos.y = spawnpos.y+2; spawnpos.z = spawnpos.z-n/2 attempts = 1;turn = 0;
spawnpos = self.spawnpos(); spawnpos.x = spawnpos.x-math.floor(m/2); spawnpos.y = spawnpos.y+2; spawnpos.z = spawnpos.z-math.floor(n/2)
local players = find_player(5,spawnpos);
if not player then self.remove() else pname = players[1] end local players = find_player(5,spawnpos);
if not player then self.remove() else pname = players[1] end
self.spam(1);t0 = _G.minetest.get_gametime();
data = {}; self.listen_punch(self.pos()) -- attach punch listener
for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end self.spam(1);t0 = _G.minetest.get_gametime();
data = {};
for i=1,atoms do -- put in atoms randomly for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end
data[math.random(m)][math.random(n)] = 1
end for i=1,atoms do -- put in atoms randomly
data[math.random(m)][math.random(n)] = 1
atoms = 0 end
for i = 1,m do for j = 1,n do if data[i][j]==1 then atoms = atoms + 1 end end end
atoms = 0
render_board = function(mode) -- mode 0 : render without solution, 1: render solution for i = 1,m do for j = 1,n do if data[i][j]==1 then atoms = atoms + 1 end end end
for i = 1,m do for j = 1,n do -- render game
if mode == 0 or data[i][j] == 0 then render_board = function(mode) -- mode 0 : render without solution, 1: render solution
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then for i = 1,m do for j = 1,n do -- render game
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2) if mode == 0 or data[i][j] == 0 then
end if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:buttongray" then
else keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},15) -- 2 gray
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},3) end
end else
end end keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},4) --on 3
end end
end end
get_dirl = function(dir) end
local dirl; -- direction left
if dir[1] > 0.5 then dirl = {0,-1} get_dirl = function(dir)
elseif dir[1] < -0.5 then dirl = {0,1} local dirl; -- direction left
elseif dir[2] > 0.5 then dirl = {-1,0} if dir[1] > 0.5 then dirl = {0,-1}
elseif dir[2] < -0.5 then dirl = {1,0} elseif dir[1] < -0.5 then dirl = {0,1}
end elseif dir[2] > 0.5 then dirl = {-1,0}
return dirl elseif dir[2] < -0.5 then dirl = {1,0}
end end
return dirl
read_pos = function(x,z) end
if x<1 or x>m or z<1 or z>n then return nil end
return data[x][z] read_pos = function(x,z)
end if x<1 or x>m or z<1 or z>n then return nil end
return data[x][z]
newdir = function(x,z,dir) -- where will ray go next end
local retdir = {dir[1],dir[2]};
local xf = x+dir[1]; local zf = z+dir[2] -- forward newdir = function(x,z,dir) -- where will ray go next
local dirl = get_dirl(dir) local retdir = {dir[1],dir[2]};
local xf = x+dir[1]; local zf = z+dir[2] -- forward
local nodef = read_pos(xf,zf) local dirl = get_dirl(dir)
local nodel = read_pos(xf + dirl[1],zf + dirl[2])
local noder = read_pos(xf - dirl[1],zf - dirl[2]) local nodef = read_pos(xf,zf)
if nodef == 1 then local nodel = read_pos(xf + dirl[1],zf + dirl[2])
retdir = {0,0} -- ray hit something local noder = read_pos(xf - dirl[1],zf - dirl[2])
elseif nodel == 1 and noder ~= 1 then if nodef == 1 then
retdir = {-dirl[1],-dirl[2]} retdir = {0,0} -- ray hit something
elseif nodel ~= 1 and noder == 1 then elseif nodel == 1 and noder ~= 1 then
retdir = {dirl[1],dirl[2]} retdir = {-dirl[1],-dirl[2]}
elseif nodel == 1 and noder == 1 then elseif nodel ~= 1 and noder == 1 then
retdir = {-dir[1],-dir[2]} retdir = {dirl[1],dirl[2]}
end elseif nodel == 1 and noder == 1 then
return retdir retdir = {-dir[1],-dir[2]}
end end
return retdir
shootray = function(x,z,dir) end
--say("ray starts " .. x .. " " .. z .. " dir " .. dir[1] .. " " .. dir[2])
local xp = x; local zp = z; shootray = function(x,z,dir)
local dirp = {dir[1],dir[2]}; --say("ray starts " .. x .. " " .. z .. " dir " .. dir[1] .. " " .. dir[2])
local maxstep = m*n; local xp = x; local zp = z;
local dirp = {dir[1],dir[2]};
for i = 1,maxstep do local maxstep = m*n;
dirp = newdir(xp,zp,dirp);
if dirp[1]==0 and dirp[2]==0 then return -i end -- hit for i = 1,maxstep do
xp=xp+dirp[1];zp=zp+dirp[2]; dirp = newdir(xp,zp,dirp);
if xp<1 or xp>m or zp<1 or zp>n then return i,{xp,zp} end -- out if dirp[1]==0 and dirp[2]==0 then return -i end -- hit
end xp=xp+dirp[1];zp=zp+dirp[2];
return 0 -- hit if xp<1 or xp>m or zp<1 or zp>n then return i,{xp,zp} end -- out
end end
return 0 -- hit
count = 0; -- how many letters were used up end
border_start_ray = function(x,z)
local rdir count = 0; -- how many letters were used up
if x==0 then rdir = {1,0} border_start_ray = function(x,z)
elseif x == m+1 then rdir = {-1,0} local rdir
elseif z == 0 then rdir = {0,1} if x==0 then rdir = {1,0}
elseif z == n+1 then rdir = {0,-1} elseif x == m+1 then rdir = {-1,0}
end elseif z == 0 then rdir = {0,1}
if rdir then elseif z == n+1 then rdir = {0,-1}
local result,out = shootray(x,z,rdir); end
if result >= 0 then if rdir then
local result,out = shootray(x,z,rdir);
if out then if result >= 0 then
if out[1]==x and out[2]==z then -- got back where it originated, reflection
keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},1); if out then
else if out[1]==x and out[2]==z then -- got back where it originated, reflection
if result<=1 then keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},1);
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},6); -- immediate bounce off else
else if result<=1 then
local nodename = "basic_robot:button_"..(65+count); keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},6); -- immediate bounce off
_G.minetest.set_node( else
{x=spawnpos.x+out[1],y=spawnpos.y+1,z=spawnpos.z+out[2]}, local nodename = "basic_robot:button_"..(65+count);
{name = nodename, param2 = 1}) _G.minetest.set_node(
_G.minetest.set_node( {x=spawnpos.x+out[1],y=spawnpos.y+1,z=spawnpos.z+out[2]},
{x=spawnpos.x+x,y=spawnpos.y+1,z=spawnpos.z+z}, {name = nodename, param2 = 1})
{name = nodename, param2 = 1}) _G.minetest.set_node(
count = count + 1; {x=spawnpos.x+x,y=spawnpos.y+1,z=spawnpos.z+z},
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4); {name = nodename, param2 = 1})
keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},4); count = count + 1;
end keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4);
end keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},4);
end end
elseif result<0 then end
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3); -- hit end
end elseif result<0 then
end keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3); -- hit
end end
end
-- initial border loop and marking end
--render blue border -- initial border loop and marking
for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+0},5) keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+n+1},5) end
for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y,z=spawnpos.z+j},5) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y,z=spawnpos.z+j},5) end --render blue border
for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+0},7) keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+n+1},7) end
for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+0},0) keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+n+1},0) end for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y,z=spawnpos.z+j},7) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y,z=spawnpos.z+j},7) end
for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y+1,z=spawnpos.z+j},0) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y+1,z=spawnpos.z+j},0) end
for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+0},0) keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+n+1},0) end
for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y+1,z=spawnpos.z+j},0) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y+1,z=spawnpos.z+j},0) end
z=0 -- bottom
for x = 1,m do
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then z=0 -- bottom
border_start_ray(x,z) for x = 1,m do
end if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:buttonblue" then
end border_start_ray(x,z)
end
x=m+1 -- right end
for z = 1,n do
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then x=m+1 -- right
border_start_ray(x,z) for z = 1,n do
end if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:buttonblue" then
end border_start_ray(x,z)
end
z=n+1 -- top end
for x = m,1,-1 do
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then z=n+1 -- top
border_start_ray(x,z) for x = m,1,-1 do
end if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:buttonblue" then
end border_start_ray(x,z)
end
x=0 -- left end
for z = n,1,-1 do
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then x=0 -- left
border_start_ray(x,z) for z = n,1,-1 do
end if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:buttonblue" then
end border_start_ray(x,z)
end
check_solution = function() end
for i = 1,m do
for j = 1,n do check_solution = function()
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}) == "basic_robot:buttonFF8080" then -- red for i = 1,m do
if data[i][j]~=1 then return false end for j = 1,n do
else if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}) == "basic_robot:buttonred" then -- red
if data[i][j]~=0 then return false end if data[i][j]~=1 then return false end
end else
end if data[i][j]~=0 then return false end
end end
return true end
end end
return true
--render board end
render_board(0)
keyboard.set({x=spawnpos.x,y=spawnpos.y,z=spawnpos.z-1},4) --render board
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z-1},5) render_board(0)
self.label("BLACKBOX with " .. atoms .. " atoms") keyboard.set({x=spawnpos.x,y=spawnpos.y,z=spawnpos.z-1},9)
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z-1},7)
end self.label("BLACKBOX with " .. atoms .. " atoms")
event = keyboard.get(); end
if event then
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z; event = keyboard.get();
if x<1 or x>m or z<1 or z>n then if event then
if event.type == 4 then local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
if check_solution() then --self.label(serialize(event))
say("#BLACKBOX : CONGRATULATIONS! " .. event.puncher .. " found all atoms after " .. attempts .. " tries."); self.remove() if x<1 or x>m or z<1 or z>n then
else if event.type == 9 then
say("#BLACKBOX : " .. event.puncher .. " failed to detect atoms after " .. attempts .. " attempts.") if check_solution() then
attempts = attempts+1 say("#BLACKBOX : CONGRATULATIONS! " .. event.puncher .. " found all atoms after " .. attempts .. " tries."); self.remove()
end else
elseif event.type == 5 then say("#BLACKBOX : " .. event.puncher .. " failed to detect atoms after " .. attempts .. " attempts.")
say("#BLACKBOX : DISPLAYING SOLUTION",pname) attempts = attempts+1
render_board(1) end
self.remove() elseif event.type == 7 then
end say("#BLACKBOX : DISPLAYING SOLUTION",pname)
else -- interior punch render_board(1)
nodetype = 2; self.remove()
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button808080" then end
nodetype = 3 else -- interior punch
end nodetype = 4;
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},nodetype); if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:buttonred" then
end nodetype = 15
end
end keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},nodetype);
end
end
::END:: ::END::

121
scripts/games/checkers.lua Normal file
View File

@ -0,0 +1,121 @@
--checkers by rnd, 1.5 hr
if not init then init=true
spos = self.spawnpos()
self.listen_punch(self.pos()) -- attach punch listener
sizex = 8; sizez= 8
gamepieces = {
{ -- player1: regular piece, queen
"basic_robot:buttonFFFF80","basic_robot:buttonFF8080"
},
{ -- player2
"basic_robot:button80FF80","basic_robot:button8080FF"
}
}
show_mark = function(pos)
minetest.add_particle(
{
pos = pos,
expirationtime = 5,
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,
}
)
end
build_game = function()
for i = 1,sizez do
for j = 1,2 do
minetest.swap_node({x=spos.x+j,y=spos.y,z=spos.z+i},{name = "basic_robot:button_131"})
minetest.swap_node({x=spos.x+j,y=spos.y+1,z=spos.z+i},{name = "air"})
end
minetest.swap_node({x=spos.x+3,y=spos.y,z=spos.z+i},{name = "basic_robot:button_".. (48+i), param2 = 3})
end
for i = 1,sizex do
minetest.swap_node({x=spos.x+3+i,y=spos.y,z=spos.z},{name = "basic_robot:button_".. (64+i)})
minetest.swap_node({x=spos.x+3+i,y=spos.y,z=spos.z+sizez+1},{name = "basic_robot:button_".. (64+i), param2 = 2})
end
local white = true;
for i = 1,sizex do
for j = 1,sizez do
if white then
minetest.swap_node({x=spos.x+3+i,y=spos.y,z=spos.z+j},{name = "basic_robot:button_0"})
else
minetest.swap_node({x=spos.x+3+i,y=spos.y,z=spos.z+j},{name = "basic_robot:button808080"})
end
white = not white
minetest.swap_node({x=spos.x+3+i,y=spos.y+1,z=spos.z+j},{name = "air"})
end
white = not white
end
minetest.swap_node({x=spos.x+1,y=spos.y+1,z=spos.z+1},{name = gamepieces[1][1]})
minetest.swap_node({x=spos.x+1,y=spos.y+2,z=spos.z+1},{name = gamepieces[1][2]})
minetest.swap_node({x=spos.x+1,y=spos.y+1,z=spos.z+sizez},{name = gamepieces[2][1]})
minetest.swap_node({x=spos.x+1,y=spos.y+2,z=spos.z+sizez},{name = gamepieces[2][2]})
for i=1,sizex,2 do
minetest.swap_node({x=spos.x+4+i,y=spos.y+1,z=spos.z+1},{name = gamepieces[1][1]})
minetest.swap_node({x=spos.x+3+i,y=spos.y+1,z=spos.z+2},{name = gamepieces[1][1]})
minetest.swap_node({x=spos.x+4+i,y=spos.y+1,z=spos.z+sizez-1},{name = gamepieces[2][1]})
minetest.swap_node({x=spos.x+3+i,y=spos.y+1,z=spos.z+sizez},{name = gamepieces[2][1]})
end
end
msgs = {1} --{idx, msg1, msg2,msg3, msg4, msg5}; -- up to 5 ingame messages displayed
add_msg = function(text)
local idx = msgs[1] or 1;
msgs[idx+1] = text;idx = idx+1; if idx>5 then idx = 1 end msgs[1] = idx
end
show_msgs = function() -- last message on top
local out = {}; local idx = msgs[1] or 1;
for i = idx,2,-1 do out[#out+1] = msgs[i] or "" end
for i = 6, idx+1,-1 do out[#out+1] = msgs[i] or "" end
self.label(table.concat(out,"\n"))
end
build_game()
punchpos = nil; -- pos of last punched piece
step = 0;
self.label("checkers\npunch piece then punch board to move\n"..
"RULES\n\n1. move only diagonal and forward. capture pieces by jumping over them.\nif you can capture you must\n"..
"2. once you reach end of board you get king. it can move backward too"
)
end
event = keyboard.get()
if event then
--self.label(serialize(event))
if event.y==spos.y+1 then -- piece was punched to be moved
punchpos = {x=event.x,y=event.y,z=event.z}
elseif event.y==spos.y and event.x~=spos.x+3 then -- board was punched
if not punchpos then return end
step = step+1
local x1 = punchpos.x-spos.x-3; local z1 = punchpos.z-spos.z
local x2 = event.x-spos.x-3; local z2 = event.z-spos.z
add_msg(minetest.colorize("red","MOVE " .. step) .. ": " .. event.puncher .. " " .. string.char(64+x1) .. z1 .. " to " .. string.char(64+x2) .. z2); show_msgs()
local nodename = minetest.get_node(punchpos).name
minetest.swap_node(punchpos,{name = "air"})
show_mark(punchpos)
-- promotion of pieces to queen when reaching the opposite side
if nodename == gamepieces[1][1] and event.z==spos.z+sizez and event.x>spos.x+3 then
nodename = gamepieces[1][2]
elseif nodename == gamepieces[2][1] and event.z==spos.z+1 and event.x>spos.x+3 then
nodename = gamepieces[2][2]
end
minetest.swap_node({x=event.x,y=event.y+1,z=event.z},{name = nodename})
show_mark({x=event.x,y=event.y+2,z=event.z})
punchpos = nil
end
end

View File

@ -1,60 +1,67 @@
-- CONNECT, coded in 20 minutes by rnd -- CONNECT, coded in 20 minutes by rnd
if not data then if not data then
m=8;n=8;turn = 0; num = 4; m=10;n=10;turn = 0; num = 4;
self.spam(1);t0 = _G.minetest.get_gametime();
spawnpos = self.spawnpos() -- place mines self.spam(1);t0 = _G.minetest.get_gametime();
state = 0; -- 0 signup 1 game spawnpos = self.spawnpos() -- place mines
players = {}; self.listen_punch(self.pos()) -- attach punch listener
data = {}; state = 0; -- 0 signup 1 game
for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end players = {};
for i = 1,m do for j = 1,n do -- render game data = {};
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2) for i = 1,m do for j = 1,n do -- render game
end if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:buttonlight_gray" then
end end keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},13)
end
get_count_in_dir = function(dir,x,y) end end
local r = num; -- num=4? in a row
local snode = data[x][y];local count = 1; get_count_in_dir = function(dir,x,y)
for j = 1,2 do local r = num; -- num=4? in a row
for i = 1,r-1 do local snode = data[x][y];local count = 1;
local x1 = x + dir[1]*i;local y1 = y + dir[2]*i; for j = 1,2 do
if not data[x1] or not data[x1][y1] then break end; if data[x1][y1]~= snode then break end for i = 1,r-1 do
count = count +1 local x1 = x + dir[1]*i;local y1 = y + dir[2]*i;
end if not data[x1] or not data[x1][y1] then break end; if data[x1][y1]~= snode then break end
dir[1]=-dir[1];dir[2]=-dir[2]; count = count +1
end end
return count dir[1]=-dir[1];dir[2]=-dir[2];
end end
return count
get_count = function(x,y) end
local c1 = get_count_in_dir({0,1},x,y); local c2 = get_count_in_dir({1,0},x,y)
local c3 = get_count_in_dir({1,1},x,y); local c4 = get_count_in_dir({1,-1},x,y) get_count = function(x,y)
if c2>c1 then c1 = c2 end; if c3>c1 then c1 = c3 end; if c4>c1 then c1 = c4 end local c1 = get_count_in_dir({0,1},x,y); local c2 = get_count_in_dir({1,0},x,y)
return c1 local c3 = get_count_in_dir({1,1},x,y); local c4 = get_count_in_dir({1,-1},x,y)
end if c2>c1 then c1 = c2 end; if c3>c1 then c1 = c3 end; if c4>c1 then c1 = c4 end
return c1
self.label("CONNECT 4 : GREEN starts play. 2 players punch to join game.") end
end
self.label("TRY TO GET FIRST " .. num .. " IN A ROW! : GREEN starts play. 2 players punch to join game.")
event = keyboard.get(); end
if event then
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z; event = keyboard.get();
if x<1 or x>m or z<1 or z>n then if event then
elseif event.type == 2 then --if event.type == 2 then local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
if state == 0 then if x<1 or x>m or z<1 or z>n then
if #players<2 then players[#players+1] = event.puncher elseif event.type == 13 then --if event.type == 2 then
else state = 1 end if state == 0 then
if #players==2 then state = 1 end if #players<2 then players[#players+1] = event.puncher -- 1 is green, turn 0 = green
end else state = 1 end
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4+turn); if #players==2 then state = 1 end
data[x][z] = 4+turn; end
if get_count(x,z) == num then say("CONGRATULATIONS! " .. event.puncher .. " has "..num .. " in a row"); self.remove(); goto END end if event.puncher == players[1] then -- green
turn = 1-turn if turn~=0 then return end -- ignore if not player turn
if state == 1 then else
local msg = ""; if turn == 0 then msg = "GREEN " else msg = "BLUE" end if turn~=1 then return end
self.label(msg .. " : " .. players[turn+1]) end
end keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4+turn);
end data[x][z] = 4+turn;
end if get_count(x,z) == num then say("CONGRATULATIONS! " .. event.puncher .. " has "..num .. " in a row"); self.remove(); goto END end
turn = 1-turn
if state == 1 then
local msg = ""; if turn == 0 then msg = "GREEN " else msg = "BLUE" end
self.label(msg .. " : " .. players[turn+1])
end
end
end
::END:: ::END::

View File

@ -0,0 +1,64 @@
--cyberpunk 2077 'breach protocol puzzle' generator
--by rnd, 20 min
if not init then init = true
n=4; -- size of square
steps = n*n; -- length of sequence
tries = n*10; -- how many tries in current row/col before giving up
tb = {};
for i = 1,n do
tb[i] ={}; local tbi = tb[i]
for j = 1,n do
tbi[j] = (i-1)*n+j
end
end
--make random path col/row/col... starting at random position
row = true;
posi = 1; -- row
posj = 1; -- col
path = {}
used = {}; -- [num] = true, when taken
for i = 1, steps do
if row then
local tmp = posj;
local s = 0
while (tmp == posj or used[tb[posi][tmp]]) and s < tries do
tmp = math.random(n);
s=s+1
end
if s == tries then say("stuck at lenght " .. #path) break end
posj = tmp
else
local tmp = posi;
local s = 0
while (tmp == posi or used[tb[tmp][posj]]) and s < tries do
tmp = math.random(n);
s=s+1
end
if s == tries then say("stuck at lenght " .. #path) break end
posi = tmp
end
row = not row
path[#path+1] = tb[posi][posj];
used[path[#path]] = true
end
local ret = {};
for i = 1,n do
for j = 1,n do
ret[#ret+1] = string.format("%02d",(i-1)*n+j).." ";
end
ret[#ret+1] = "\n"
end
self.label(table.concat(path," ") .. "\n\n"..table.concat(ret))
end

View File

@ -1,5 +1,13 @@
-- 'hacking' game from Fallout, by rnd
if not init then if not init then
init = true init = true
max_guesses = 4 -- how many guesses player gets
n = 40; -- how many options
pass_length=5
charset_size=10 -- a,b,c,d,e,...?
cols = 4;
rows = math.ceil(n/cols);
generate_random_string = function(n,m) generate_random_string = function(n,m)
local ret = {}; local ret = {};
@ -18,21 +26,18 @@ if not init then
end end
get_form = function() get_form = function()
local n = #passlist; local n = #passlist;
local frm = "label[0,0;" .. intro_msg .. "] " .. "label[0,8.5;" .. msg .. "] " local frm = "label[0,0;" .. intro_msg .. "] " .. "label[0,8.5;" .. msg .. "] "
local k = 0
for i = 1,10 do for i=0,cols-1 do
frm = frm .. "button[0,".. (i)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] " for j = 1,rows do
end k=k+1; if k>n then break end
frm = frm .. "button[" .. 2*i.. ",".. j*0.75 ..";2,1;" .. k .. ";".. passlist[k] .. "] "
for i = 11,n do end
frm = frm .. "button[2,".. (i-11+1)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] " end
end local form = "size[" .. 2*cols .. "," .. 9 .. "]" .. frm;
return form
local form = "size[6," .. 9 .. "]" .. frm;
return form
end end
_G.math.randomseed(os.time()) _G.math.randomseed(os.time())
@ -40,15 +45,17 @@ if not init then
msg = "" --TEST\nTEST\nTEST"; msg = "" --TEST\nTEST\nTEST";
passlist = {}; passdict = {} passlist = {}; passdict = {}
n = 20; -- how many options
count = 0; count = 0;
while count< n do while count< n do
local pass = generate_random_string(5,5); -- password length, charset size local pass = generate_random_string(pass_length,charset_size); -- password length, charset size
if not passdict[pass] then passlist[#passlist+1] = pass; passdict[pass] = true; count = count + 1 end if not passdict[pass] then passlist[#passlist+1] = pass; passdict[pass] = true; count = count + 1 end
end end
correct = math.random(n) correct = math.random(n)
-- say(passlist[correct])
guesses = 0 guesses = 0
max_guesses = 4
rom.data = {}; rom.data = {};
if not rom.data then rom.data = {} end if not rom.data then rom.data = {} end
@ -57,7 +64,7 @@ if not init then
local players = find_player(4); local players = find_player(4);
if not players then say("#fallout hacking game: no players") self.remove() end if not players then say("#fallout hacking game: no players") self.remove() end
pname = players[1]; pname = players[1];
say("#fallout hacking game, player " .. pname) minetest.chat_send_player(pname,"#fallout hacking game, player " .. pname)
--if rom.data[pname] then say("password is locked out!") self.remove() end --if rom.data[pname] then say("password is locked out!") self.remove() end
@ -76,18 +83,19 @@ sender,fields = self.read_form()
if selected>0 then if selected>0 then
guesses = guesses + 1 guesses = guesses + 1
if selected == correct then if selected == correct then
say("password " .. passlist[correct] .. " is correct! " .. guesses .. " guesses.") minetest.chat_send_all("#FALLOUT HACKING: " .. pname .. " guessed the password " .. passlist[correct] .. " after " .. guesses .. " guesses.")
self.show_form(pname, "size[1,1] label[0,0.5;" .. minetest.colorize("lawngreen", "ACCESS GRANTED") .. "]") self.show_form(pname, "size[3,1] label[0,0.5;" .. minetest.colorize("lawngreen", "ACCESS GRANTED") .. "]")
self.remove() self.remove()
--correct: do something with player --correct: do something with player
else else
if guesses == 3 then msg = msg .. "\n" end
msg = msg .. " " .. minetest.colorize("yellow",guesses .. ". " .. passlist[selected]) .. " (" .. get_similiarity(passlist[correct], passlist[selected]) .. " match)" msg = msg .. " " .. minetest.colorize("yellow",guesses .. ". " .. passlist[selected]) .. " (" .. get_similiarity(passlist[correct], passlist[selected]) .. " match)"
self.show_form(pname, get_form()) self.show_form(pname, get_form())
end end
if guesses>=max_guesses then if guesses>=max_guesses then
msg = minetest.colorize("red","A C C E S S D E N I E D!") msg = minetest.colorize("red","A C C E S S D E N I E D!")
self.show_form(pname, get_form()) self.show_form(pname, get_form())
say("too many false guesses. password locked out!") rom.data[pname] = 1; self.remove() minetest.chat_send_player(pname,"too many false guesses. password locked out!") rom.data[pname] = 1; self.remove()
end end
end end
if fields.quit then self.remove() end if fields.quit then self.remove() end

95
scripts/games/go.lua Normal file
View File

@ -0,0 +1,95 @@
--go by rnd
if not init then init=true
spos = self.spawnpos()
self.listen_punch(self.pos()) -- attach punch listener
sizex = 9; sizez = 9
gamepieces = {
{ -- player black
"basic_robot:buttonFF8080"
},
{ -- player white - blue
"basic_robot:button8080FF"
}
}
show_mark = function(pos)
minetest.add_particle(
{
pos = pos,
expirationtime = 10,
velocity = {x=0, y=0,z=0},
size = 5,
texture = "default_apple.png",
acceleration = {x=0,y=0,z=0},
collisiondetection = true,
collision_removal = true,
}
)
end
build_game = function()
for i = 1,sizex do
minetest.swap_node({x=spos.x+i,y=spos.y,z=spos.z+sizez+1},{name = "basic_robot:button_"..(64+i), param2=2})
minetest.swap_node({x=spos.x+i,y=spos.y,z=spos.z},{name = "basic_robot:button_"..(64+i)})
for j = 1,sizez do
minetest.swap_node({x=spos.x+i,y=spos.y,z=spos.z+j},{name = "basic_robot:button808080"})
minetest.swap_node({x=spos.x+i,y=spos.y+1,z=spos.z+j},{name = "air"})
end
end
for j = 1,sizez do
minetest.swap_node({x=spos.x,y=spos.y,z=spos.z+j},{name = "basic_robot:button_".. (48+j), param2=3})
end
end
msgs = {1} --{idx, msg1, msg2,msg3, msg4, msg5}; -- up to 5 ingame messages displayed
add_msg = function(text)
local idx = msgs[1] or 1;
msgs[idx+1] = text;idx = idx+1; if idx>5 then idx = 1 end msgs[1] = idx
end
show_msgs = function() -- last message on top
local out = {}; local idx = msgs[1] or 1;
for i = idx,2,-1 do out[#out+1] = msgs[i] or "" end
for i = 6, idx+1,-1 do out[#out+1] = msgs[i] or "" end
self.label(table.concat(out,"\n"))
end
build_game()
punchnode = nil; -- name of piece to place
step = 0;
turn = 0;
self.label("go\n\n" ..
"RULES:\n\n"..
"1. liberty of block is free position next to block (not diagonally). block\n"..
"is alive if it has liberties left. group of connected (not diagonally) blocks\n"..
"is alive if at least one block in group is alive.\n"..
"2. dead blocks are immediately removed from game.\n"..
"3.to get score count how much territory your blocks occupy, including blocks\n"..
"themselves. If not clear which blocks are dead, continue game until it is clear\n\n"..
"punch board to place your red/blue piece. blue starts first.")
end
event = keyboard.get()
if event then
--self.label(serialize(event))
local x = event.x-spos.x; local z = event.z-spos.z;
if event.y~= spos.y then return end
if x<1 or x>sizex or z<1 or z>sizez then return end
turn = 1- turn
step = step+1
local x2 = event.x-spos.x; local z2 = event.z-spos.z
if turn == 0 then
add_msg(minetest.colorize("red",step) .. ": " .. event.puncher .. " " .. string.char(64+x2) .. z2);
else
add_msg(minetest.colorize("blue",step) .. ": " .. event.puncher .. " " .. string.char(64+x2) .. z2);
end
show_msgs()
minetest.swap_node({x=event.x,y=event.y+1,z=event.z},{name = gamepieces[turn+1][1]})
punchnode = nil
show_mark({x=event.x,y=event.y+2,z=event.z})
end

View File

@ -1,106 +1,140 @@
--HIDE AND SEEK game robot, by rnd --HIDE AND SEEK game robot, by rnd
if not gamemaster then if not gamemaster then
timeout = 10; timeout = 20;
gamemaster = "rnd" fardist = 300
player_list = {}; gamemaster = "rnd"
_G.minetest.chat_send_all("# HIDE AND SEEK .. say #hide to join play")
s=0;t=0; count = 0; _G.minetest.forceload_block(self.pos(),true)
_G.minetest.forceload_block(self.pos(),true) self.listen(1);
self.listen(1); self.label(colorize("yellow","HIDE&SEEK")) centerpos = {x=160,y= 606,z= 227};
end
init_game = function()
speaker,msg = self.listen_msg(); self.label(colorize("yellow","HIDE&SEEK .. waiting for players .. say #hide to join"))
_G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK .. say #hide to join play, when all joined say #start to start game."))
if s==0 then s=0;t=0; count = 0;
if msg =="#hide" then player_list = {}
player_list[speaker]={}; end
_G.minetest.chat_send_all("# HIDE AND SEEK: " .. speaker .. " joined the game")
local player = _G.minetest.get_player_by_name(speaker); reset_name = function(name)
if player then if name then
player:setpos({x=0,y=5,z=0});player:set_properties({nametag_color = "0x0"}) local player = minetest.get_player_by_name(name)
end if not player then return end
player:set_properties({nametag_color = "white"})
end return
if msg == "#start" and speaker == gamemaster then s = 0.5 _G.minetest.chat_send_all("# HIDE AND SEEK STARTS in " .. timeout .. " SECONDS!") end end
local players = _G.minetest.get_connected_players();
elseif s==0.5 then for _,player in pairs(players) do
t=t+1 local name = player:get_player_name();
if t==timeout then local data = player_list[name];
t=0;s = 1; count = 0; if data then
for pname,_ in pairs(player_list) do player:set_properties({nametag_color = "white"})
local player = _G.minetest.get_player_by_name(pname); end
if player then end
player_list[pname].hp = player:get_hp(); end
player_list[pname].pos = player:getpos()
player_list[pname].t = 0; show_players = function()
count = count+1 local ret = {}
end for k,v in pairs(player_list) do
end ret[#ret+1]=k
if count == 1 then end
gamemaster = false; _G.minetest.chat_send_all("# HIDE AND SEEK only 1 player, aborting.") self.label("hide and seek, players: " .. table.concat(ret,", "))
else end
_G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK STARTS NOW WITH " .. count .. " PLAYERS. You are out if: 1.your health changes, 2. leave spawn. If stay in same area for too long or you will be exposed.")) init_game()
end end
end speaker,msg = self.listen_msg();
elseif s==1 then
players = _G.minetest.get_connected_players(); if s==0 then
count = 0; if msg =="#hide" then
for _,player in pairs(players) do player_list[speaker]={};
local name = player:get_player_name(); _G.minetest.chat_send_all("# HIDE AND SEEK: " .. speaker .. " joined the game")
local data = player_list[name]; local player = _G.minetest.get_player_by_name(speaker);
if data then if player then
count=count+1 player:setpos(centerpos);player:set_properties({nametag_color = "0x0"})
local pos = player:getpos(); end
local dist = math.max(math.abs(pos.x),math.abs(pos.y),math.abs(pos.z));
if dist>50 or (not _G.minetest.get_player_by_name(name)) then end
_G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! went too far away " ) if msg == "#start" and speaker == gamemaster then s = 0.5 _G.minetest.chat_send_all("# HIDE AND SEEK STARTS in " .. timeout .. " SECONDS!") end
player:set_properties({nametag_color = "white"})
player_list[name] = nil; elseif s==0.5 then
end t=t+1
if data.hp ~= player:get_hp() then if t==timeout then
_G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! his health changed!" ) t=0;s = 1; count = 0;
player:set_properties({nametag_color = "white"}) for pname,_ in pairs(player_list) do
player_list[name] = nil; local player = _G.minetest.get_player_by_name(pname);
end if player then
player_list[pname].hp = player:get_hp();
--expose campers player_list[pname].pos = player:getpos()
local p = data.pos; player_list[pname].t = 0;
dist = math.max(math.abs(pos.x-p.x),math.abs(pos.y-p.y),math.abs(pos.z-p.z)); count = count+1
--say( name .. " dist " .. dist .. " t " .. data.t) end
if dist<8 then end
data.t = data.t+1; if count == 1 then
if not data.camp then gamemaster = false; _G.minetest.chat_send_all("# HIDE AND SEEK only 1 player, aborting.")
if data.t>15 and not data.camp then else
_G.minetest.chat_send_player(name, "# HIDE AND SEEK: move in 5s or be exposed") _G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK STARTS NOW WITH " .. count .. " PLAYERS. You are out if: 1.your health changes, 2. leave spawn. If stay in same area for too long or you will be exposed."))
data.camp = true end
end
elseif data.t>=20 then end
pos.x=math.ceil(pos.x);pos.y=math.ceil(pos.y);pos.z=math.ceil(pos.z); elseif s==1 then
_G.minetest.chat_send_all("# HIDE AND SEEK: " .. name .. " is camping at " .. pos.x .. " " .. pos.z) players = _G.minetest.get_connected_players();
data.camp = false; data.t = 0 count = 0;
end for _,player in pairs(players) do
else local name = player:get_player_name();
data.t = 0; data.pos = player:getpos(); data.camp = false local data = player_list[name];
end if data then
count=count+1
end local pos = player:getpos();
end local dist = math.max(math.abs(pos.x-centerpos.x),math.abs(pos.y-centerpos.y),math.abs(pos.z-centerpos.z));
if dist>fardist or (not _G.minetest.get_player_by_name(name)) then
self.label(count) _G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! went too far away " )
reset_name(name)
if count<=1 then show_players()
if count==1 then player_list[name] = nil;
for name,_ in pairs(player_list) do end
player0=_G.minetest.get_player_by_name(name) if data.hp < player:get_hp() then
_G.minetest.chat_send_all(colorize("red","****** HIDE AND SEEK: ".. name .. " wins ******")) _G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! his health changed!" )
player0:set_properties({nametag_color = "white"}) player:set_properties({nametag_color = "white"})
gamemaster = false; player:setpos(centerpos)
end player_list[name] = nil;
else show_players()
_G.minetest.chat_send_all("# HIDE AND SEEK: no players left") end
gamemaster = false;
end --expose campers
end local p = data.pos;
dist = math.max(math.abs(pos.x-p.x),math.abs(pos.y-p.y),math.abs(pos.z-p.z));
end --say( name .. " dist " .. dist .. " t " .. data.t)
if dist<8 then
data.t = data.t+1;
if not data.camp then
if data.t>15 and not data.camp then
_G.minetest.chat_send_player(name, "# HIDE AND SEEK: move in 5s or be exposed")
data.camp = true
end
elseif data.t>=20 then
pos.x=math.ceil(pos.x);pos.y=math.ceil(pos.y);pos.z=math.ceil(pos.z);
_G.minetest.chat_send_all("# HIDE AND SEEK: " .. name .. " is camping at " .. pos.x .. " " .. pos.z)
data.camp = false; data.t = 0
end
else
data.t = 0; data.pos = player:getpos(); data.camp = false
end
end
end
if count<=1 then
if count==1 then
for name,_ in pairs(player_list) do
_G.minetest.chat_send_all(colorize("red","****** HIDE AND SEEK: ".. name .. " wins ******"))
reset_name(name)
init_game();
end
else
_G.minetest.chat_send_all("# HIDE AND SEEK: no players left")
init_game()
end
end
end

View File

@ -0,0 +1,165 @@
--HIDE AND SEEK game robot
if not init then init = true
_G.minetest.forceload_block(self.pos(),true)
timeout = 30;
gamepos = {x=0,y=11,z=0} -- game of hide and seek is played around here
maxgamedist = 100; -- how far around gamepos player can go before kicked out of game
gameoutpos = {x=0,y=20,z=0} -- position for players that go out of game
player_list = {};
s=0;t=0; count = 0;
prize = ""
announce = function(text) -- show message to all players in game only
for name,_ in pairs(player_list) do
_G.minetest.chat_send_player(name, text)
end
end
get_players = function()
local msg = "";
for name,_ in pairs(player_list) do
msg = msg .. " " .. name
end
return msg
end
init_game = function()
local msg = get_players();
announce(colorize("red","# HIDE AND SEEK : hide from everyone else who is playing. Winner gets DIAMONDS\nsay join to join play. say #start to start game."..
" players: " .. msg))
s=0;t=0;
end
--show initial message for all players
--minetest.chat_send_all(colorize("red","# HIDE AND SEEK : hide from everyone else who is playing. Winner gets DIAMONDS\nsay join to join play. say #start to start game.")
init_game()
_G.minetest.forceload_block(self.pos(),true)
self.listen(1); self.label(colorize("yellow","HIDE&SEEK"))
end
speaker,msg = self.listen_msg();
if s==0 then
t = t +1
if t%30 == 0 then
init_game();
end
if msg =="join" then
player_list[speaker]={};
announce(colorize("red","# HIDE AND SEEK: " .. speaker .. " joined the game"));
announce("players: " .. get_players())
local player = _G.minetest.get_player_by_name(speaker);
count = count + 1
if player then
player:setpos(gamepos);player:set_properties({nametag_color = "0x0"})
player:set_hp(20)
local inv = player:get_inventory();inv:set_list("main",{})
end
end
if msg == "#start" and count>1 then s = 0.5 announce(colorize("red","# HIDE AND SEEK STARTS in " .. timeout .. " SECONDS!")) end
elseif s==0.5 then
t=t+1
if t==timeout then
t=0;s = 1; count = 0;
for pname,_ in pairs(player_list) do
local player = _G.minetest.get_player_by_name(pname);
if player then
player_list[pname].hp = player:get_hp();
player_list[pname].pos = player:getpos()
player_list[pname].t = 0;
count = count+1
end
end
if count == 1 then
init = false; announce("# HIDE AND SEEK only 1 player, aborting.")
else
prize = "default:diamond " .. (count-1);
announce(colorize("red","# HIDE AND SEEK STARTS NOW WITH " .. count .. " PLAYERS."..
"You are out if: 1.your health changes, 2. leave game area. If stay in same area for too long or you will be exposed."))
announce(colorize("red","# WINNER WILL GET " .. prize))
end
end
elseif s==1 then
players = _G.minetest.get_connected_players();
count = 0;
for _,player in pairs(players) do
local name = player:get_player_name();
local data = player_list[name];
if data then
count=count+1
local pos = player:getpos();
local dist = math.max(math.abs(pos.x-gamepos.x),math.abs(pos.y-gamepos.y),math.abs(pos.z-gamepos.z));
if dist>maxgamedist or (not _G.minetest.get_player_by_name(name)) then
announce("# HIDE AND SEEK: ".. name .. " is OUT! went too far away " )
player:set_properties({nametag_color = "white"})
player_list[name] = nil;
announce("remaining players: " .. get_players())
end
if data.hp ~= player:get_hp() then
announce("# HIDE AND SEEK: ".. name .. " is OUT! his health changed!" )
player:set_properties({nametag_color = "white"})
player_list[name] = nil;
announce("remaining players: " .. get_players())
player:setpos(gameoutpos)
end
--expose campers
local p = data.pos;
dist = math.max(math.abs(pos.x-p.x),math.abs(pos.y-p.y),math.abs(pos.z-p.z));
--say( name .. " dist " .. dist .. " t " .. data.t)
if dist<8 then
data.t = data.t+1;
if not data.camp then
if data.t>25 and not data.camp then
_G.minetest.chat_send_player(name, "# HIDE AND SEEK: move in 5s or be exposed")
data.camp = true
end
elseif data.t>=30 then
pos.x=math.ceil(pos.x);pos.y=math.ceil(pos.y);pos.z=math.ceil(pos.z);
announce("# HIDE AND SEEK: " .. name .. " is camping at " .. pos.x .. " " .. pos.z)
data.camp = false; data.t = 0
end
else
data.t = 0; data.pos = player:getpos(); data.camp = false
end
end
end
self.label(count)
if count<=1 then
if count==1 then
for name,_ in pairs(player_list) do
local player0=_G.minetest.get_player_by_name(name)
if player0 then
announce(colorize("red","****** HIDE AND SEEK: ".. name .. " wins ******"))
local inv = player0:get_inventory();
inv:add_item("main",_G.ItemStack(prize))
player0:set_properties({nametag_color = "white"})
player0:setpos(gameoutpos)
end
s=2
end
else
announce("# HIDE AND SEEK: no players left")
s=2
end
end
elseif s==2 then
player_list = {}
init_game()
end

View File

@ -0,0 +1,23 @@
-- high scores for multiple levels of game
if not init then init = true
init_score = function(levels, tops, default_value) -- [level] = {{name,score}, ...}
local data = {} for i = 1, levels do data[i] = {} for j = 1,tops do data[i][j] = {"-",default_value} end end return data
end
add_score = function(data,name,score,level)
local datal = data[level]; local tops = #datal;
local j;for i = 1,tops do if score>datal[i][2] then j = i break end end
if not j then return end; for i=tops,j+1,-1 do datal[i][1] = datal[i-1][1];datal[i][2] = datal[i-1][2] end
datal[j] = {name,score}
end
_,text = book.read(1); data = minetest.deserialize(text)
if not data then data = init_score(1,5,-999) end
add_score(data,"pl1",-50,1)
add_score(data,"pl2",-40,1)
book.write(1,"score", minetest.serialize(data))
self.label(serialize(data))
end

View File

@ -0,0 +1,162 @@
--coroutine
--lights out by dopik
if not init then init = true
self.listen_punch(self.pos());
end
size = 5
score = 1000 --highest score possible
function generateField()
local tbl = {}
for i = 1, size^2 do
tbl[i] = math.random(0,1)
end
local field = {}
for i = 1, size^2 do
local x,z = (i-1) % size, math.floor((i-1) / size)
local val = tbl[i]
val = val + (x > 0 and tbl[(x-1) + (z*size) +1] or 0)
val = val + (x+1 < size and tbl[(x+1) + (z*size) +1] or 0)
val = val + (z > 0 and tbl[x + ((z-1)*size) +1] or 0)
val = val + (z+1 < size and tbl[x + ((z+1)*size) +1] or 0)
field[i] = val % 2
end
return field
end
function placeField(field)
for i = 1, size^2 do
local dx,dz = (i-1) % size +1, math.floor((i-1) / size) +1
local pos = self.spawnpos()
pos.x = pos.x + dx
pos.z = pos.z + dz
keyboard.set(pos, field[i]*6 + 1)
end
end
function hitKey(pos)
local c
if keyboard.read(pos) == "basic_robot:buttonwhite" then
c = true
keyboard.set(pos, 7)
elseif keyboard.read(pos) == "basic_robot:buttonblue" then
keyboard.set(pos, 1)
end
pos.x = pos.x - 1
if keyboard.read(pos) == "basic_robot:buttonwhite" then
c = true
keyboard.set(pos, 7)
elseif keyboard.read(pos) == "basic_robot:buttonblue" then
keyboard.set(pos, 1)
end
pos.x = pos.x + 2
if keyboard.read(pos) == "basic_robot:buttonwhite" then
c = true
keyboard.set(pos, 7)
elseif keyboard.read(pos) == "basic_robot:buttonblue" then
keyboard.set(pos, 1)
end
pos.x = pos.x - 1
pos.z = pos.z - 1
if keyboard.read(pos) == "basic_robot:buttonwhite" then
c = true
keyboard.set(pos, 7)
elseif keyboard.read(pos) == "basic_robot:buttonblue" then
keyboard.set(pos, 1)
end
pos.z = pos.z + 2
if keyboard.read(pos) == "basic_robot:buttonwhite" then
c = true
keyboard.set(pos, 7)
elseif keyboard.read(pos) == "basic_robot:buttonblue" then
keyboard.set(pos, 1)
end
score = math.max(0, score - 1)
if not c then
return won()
end
end
function won()
for i = 1, size^2 do
local pos = self.spawnpos()
pos.x = pos.x + 1 + i % size
pos.z = pos.z + 1 + math.floor(i / size)
if keyboard.read(pos) == "basic_robot:buttonblue" then
return false
end
end
return win()
end
function win()
puzzle.chat_send_player(pname, "\27(c@#ff0)###Lights Out### \27(c@#fff)You won!")
puzzle.chat_send_player(pname, string.concat({"\27(c@#ff0)###Lights Out### \27(c@#fff)Your score is \27(c@#ff0) ", score}))
for i = 1, size^2 do
local dx,dz = (i-1) % size +1, math.floor((i-1) / size) +1
local pos = self.spawnpos()
pos.x = pos.x + dx
pos.z = pos.z + dz
puzzle.set_node(pos, {name="default:dirt"})
end
self.remove()
end
function init()
local players = find_player(5, self.spawnpos())
if not players then
self.remove()
return
end
pname = players[1]
local field = generateField(size)
placeField(field)
puzzle.chat_send_player(pname, "\27(c@#ff0)###Lights Out### \27(c@#fff)Game started")
self.label("Turn all lights off (=white) to win\nPunch a light to switch it on/off")
return main()
end
function main()
local event = keyboard.get()
local pos = self.spawnpos()
if event then
if event.x <= pos.x + size and event.x > pos.x
and event.z <= pos.z + size and event.z > pos.z
and event.y == pos.y then
hitKey({x=event.x,y=event.y, z=event.z})
end
end
pause()
return main()
end
init()

View File

@ -1,89 +1,132 @@
-- 'Mensch argere Dich nicht'. modified by rnd
if not init then if not init then
msg =
msg = "Sorry!/Mensch argere Dich nicht, modified by rnd\n\n" ..
"'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".. "1. goal of game is put all 4 of your pieces in your home area by moving clockwise around on white path\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" .. "2. start of game is player trying to move his piece out to start spot next to home by trying 3x to get 6 from 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" .. "3. after that player uses dice at start of turn. he moves his playing pieces ( those\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" .. "on board) so many positions as dice said. negative number means moving back.\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".. "4. if player can move his piece he must. if his piece goes backward all the way to start\n"..
"his 'start' field.\n" .. "he must put it back out.If piece lands on opponent piece then opponent piece is put out to initial start.\n"..
"Variation which is played by most players: A player who has no pieces in the game has 3 tries to throw a six" "5. if dice shows 6 he can move 6 or put another piece out if possible. player can move\n"..
self.label(msg) "pieces within home area if possible.\n"..
"6. if dice shows 0 you play as one of your opponents for a turn."
init = true; self.label(msg)
state = 1; -- game on
step = 0; init = true;
punchstate = 1; -- first punch state = 1; -- game on
punchpos = {} step = 0;
pos = self.spawnpos() punchstate = 1; -- first punch
dice = 0 punchpos = {}
spawns = {{2,2,"basic_robot:buttonFF8080"},{2,11,"basic_robot:button8080FF"},{11,11,"basic_robot:button80FF80"},{11,2,"basic_robot:buttonFFFF80"}} pos = self.spawnpos()
self.listen_punch(self.pos()) -- attach punch listener
for i = 1,12 do for j = 1,12 do dice = 0
minetest.swap_node({x=pos.x+i,y=pos.y+1,z=pos.z+j},{name = "air"}) spawns = {
end end {2,2,1,2,2,"basic_robot:buttonFF8080"}, -- xstart,zstart,ystart, dimx, dimz, nodename
{2,11,1,2,2,"basic_robot:button8080FF"},
for k = 1,#spawns do {11,11,1,2,2,"basic_robot:button80FF80"},
for i = 0,1 do for j = 0,1 do {11,2,1,2,2,"basic_robot:buttonFFFF80"}, --4 bases
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 {7,3,0,1,4,"basic_robot:buttonFF8080"}, -- red final
end {3,7,0,4,1,"basic_robot:button8080FF"}, -- blue final
{7,8,0,1,4,"basic_robot:button80FF80"}, -- green final
keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7) {8,7,0,4,1,"basic_robot:buttonFFFF80"}, -- yellow final
end {1,1,0,5,5,"basic_robot:button808080"},{6,1,0,3,1,"basic_robot:button808080"},
{9,1,0,5,5,"basic_robot:button808080"},{13,6,0,1,3,"basic_robot:button808080"},
if state == 0 then {1,9,0,5,5,"basic_robot:button808080"},{1,6,0,1,3,"basic_robot:button808080"},
elseif state == 1 then {9,9,0,5,5,"basic_robot:button808080"},{6,13,0,3,1,"basic_robot:button808080"},
event = keyboard.get();
if event then {2,2,0,2,2,"basic_robot:buttonFFFFFF"},
x = event.x-pos.x; y = event.y-pos.y; z = event.z-pos.z {2,11,0,2,2,"basic_robot:buttonFFFFFF"},
--say("type " .. event.type .. " pos " .. x .. " " .. y .. " " .. z) {11,11,0,2,2,"basic_robot:buttonFFFFFF"},
if x == 7 and y == 1 and z == 7 then {11,2,0,2,2,"basic_robot:buttonFFFFFF"},
_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) -- build board
step = step + 1; for i = 1,12 do for j = 1,12 do
msg = colorize("red","<Mensch argere dich nicht>") .. " STEP " .. step .. ": " ..event.puncher .. " threw dice = " .. dice; minetest.swap_node({x=pos.x+i,y=pos.y,z=pos.z+j},{name = "basic_robot:buttonFFFFFF"})
minetest.chat_send_all(msg) minetest.swap_node({x=pos.x+i,y=pos.y+1,z=pos.z+j},{name = "air"})
self.label(msg) end end
punchstate = 1
elseif punchstate == 1 then for k = 1,#spawns do
if y == 1 and event.type ~= 2 and event.type<7 then for i = 0,spawns[k][4]-1 do for j = 0,spawns[k][5]-1 do
punchpos = 2; minetest.chat_send_player(event.puncher,colorize("red","<Mensch argere dich nicht>") .. " punch place on board where to move ") minetest.swap_node({x=pos.x+i+spawns[k][1],y=pos.y+spawns[k][3],z=pos.z+j+spawns[k][2]},{name = spawns[k][6]})
punchpos = {x=event.x,y=event.y,z=event.z} end end
punchstate = 2 end
end
elseif punchstate == 2 then keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7)
if y == 0 and event.type ~= 2 then
if x<2 or x>12 or z<2 or z>12 then msgs = {1} --{idx, msg1, msg2,msg3, msg4, msg5}; -- up to 5 ingame messages displayed
else add_msg = function(text)
local nodename = minetest.get_node(punchpos).name; local idx = msgs[1] or 1;
minetest.swap_node({x=event.x, y = event.y+1, z=event.z},{name = nodename}) msgs[idx+1] = text;idx = idx+1; if idx>5 then idx = 1 end msgs[1] = idx
minetest.swap_node(punchpos,{name = "air"}) end
punchstate = 1; dice = 0 show_msgs = function()
minetest.add_particle( local out = {};
{ local idx = msgs[1] or 1;
pos = punchpos, for i = idx,2,-1 do out[#out+1] = msgs[i] or "" end
expirationtime = 15, for i = 6, idx+1,-1 do out[#out+1] = msgs[i] or "" end
velocity = {x=0, y=0,z=0}, self.label(table.concat(out,"\n"))
size = 18, end
texture = "default_apple.png",
acceleration = {x=0,y=0,z=0}, end
collisiondetection = true,
collision_removal = true, if state == 0 then
} elseif state == 1 then
) event = keyboard.get();
msg = colorize("red","<Mensch argere dich nicht>") .. " " .. event.puncher .. " moved."; if event then
minetest.chat_send_all(msg) x = event.x-pos.x; y = event.y-pos.y; z = event.z-pos.z
self.label(msg) --say("type " .. event.type .. " pos " .. x .. " " .. y .. " " .. z)
end if x == 7 and y == 1 and z == 7 then
end _G.math.randomseed(os.time())
dice = -3+math.random(9);
keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7+math.abs(dice))
end if dice<0 then
end keyboard.set({x=pos.x+7,y=pos.y+2,z=pos.z+7},7+11)
else
keyboard.set({x=pos.x+7,y=pos.y+2,z=pos.z+7},0)
end
step = step + 1;
msg = minetest.colorize("red","STEP ".. step) .. ": " ..event.puncher .. " threw dice = " .. dice;
add_msg(msg); show_msgs()
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 = event.puncher .. " moved.";
add_msg(msg); show_msgs()
end
end
end
end
end end

View File

@ -1,83 +1,92 @@
-- minesweeper -- minesweeper
if not data then if not data then
m=24;n=22; minescount = m*n/5; m=24;n=22; minescount = m*n/5;
reward = 30; reward = 30;
if not find_player(4) then error("minesweeper: no players near") end if not find_player(6) then error("minesweeper: no players near") end
self.spam(1) self.spam(1)
t0 = _G.minetest.get_gametime(); t0 = _G.minetest.get_gametime();
data = {}; spawnpos = self.spawnpos() -- place mines data = {}; spawnpos = self.spawnpos() -- place mines
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end self.listen_punch(spawnpos); -- attach punch listener
if not data[1] then data[1] = {} end if not data[2] then data[2] = {} end -- create 2x2 safe area
data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0; for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
if not data[1] then data[1] = {} end if not data[2] then data[2] = {} end -- create 2x2 safe area
minescount = 0; data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0;
for i = 1,m do for j = 1,n do -- render game
if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then minescount = 0;
puzzle.set_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},{name = "basic_robot:button808080"}) for i = 1,m do for j = 1,n do -- render game
end if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
end end if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:buttonlight_grey" then
puzzle.set_node({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},{name = "basic_robot:button80FF80"}) puzzle.set_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},{name = "basic_robot:buttonlight_grey"})
end
get_mine_count = function(i,j) end end
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0 puzzle.set_node({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},{name = "basic_robot:buttondark_green"})
for k = -1,1 do for l = -1,1 do puzzle.set_node({x=spawnpos.x+2,y=spawnpos.y,z=spawnpos.z},{name = "basic_robot:buttonblue"})
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
end end get_mine_count = function(i,j)
return count if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
end for k = -1,1 do for l = -1,1 do
chk_mines = function() if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
local count = minescount; end end
for i=1,m do for j=1,n do return count
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})=="basic_robot:buttonFF8080" and data[i] and data[i][j]==1 then end
count=count-1 chk_mines = function()
end local count = minescount;
end end for i=1,m do for j=1,n do
return count if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})=="basic_robot:buttonred" and data[i] and data[i][j]==1 then
end count=count-1
say("minesweeper " .. m .. "x" ..n .. " with " .. minescount .. " mines ") end
self.label("find all hidden mines! mark mine by standing on top of block and punch,\notherwise it will uncover the block (and possibly explode).") end end
return count
end end
say("minesweeper " .. m .. "x" ..n .. " with " .. minescount .. " mines ")
event = keyboard.get(); self.label("find all hidden mines! mark mine by standing on top of block and punch,\notherwise it will uncover the block (and possibly explode).")
if event then
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z; end
if x<1 or x>m or z<1 or z>n then
if x == 0 and z == 1 then event = keyboard.get();
local count = chk_mines(); if event then
if count == 0 then
t0 = _G.minetest.get_gametime() - t0; local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
say("congratulations! " .. event.puncher .. " discovered all mines in " .. t0 .. " s") if x<1 or x>m or z<1 or z>n then
_G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward if x == 1 and z == 0 then
else local count = chk_mines();
reward = reward*(1-(count/minescount))^(1.5); reward = math.floor(reward); if count>minescount/2 then
say("FAIL! " .. count .. " mines remaining. You get " .. reward .. " gold for found mines") say("fail, more than 50% of mines remaining ")
_G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward return
end end
self.remove() if count == 0 then
end t0 = _G.minetest.get_gametime() - t0;
else --if event.type == 2 then say("congratulations! " .. event.puncher .. " discovered all mines in " .. t0 .. " s")
local ppos = player.getpos(event.puncher) _G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward
if ppos and math.abs(ppos.x-event.x)<0.5 and math.abs(ppos.z-event.z)<0.5 then -- just mark mine else
if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:button808080" then reward = reward*(1-(count/minescount))^(1.5); reward = math.floor(reward);
puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:button808080"}) say("FAIL! " .. count .. " mines remaining. You get " .. reward .. " gold for found mines")
else _G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward
puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:buttonFF8080"}) end
end self.remove()
else end
if data[x] and data[x][z]==1 then else --if event.type == 2 then
say("boom! "..event.puncher .. " is dead ");puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:buttonFF8080"}); local ppos = player.getpos(event.puncher)
local player_ = puzzle.get_player(event.puncher); if ppos and math.abs(ppos.x-event.x)<0.5 and math.abs(ppos.z-event.z)<0.5 then -- just mark mine
player_:setpos({x=spawnpos.x-1,y=spawnpos.y+1,z=spawnpos.z-1}); if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:buttonlight_grey" then
self.remove() puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:buttonlight_grey"})
else else
local count = get_mine_count(x,z); puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:buttonred"})
if count == 0 then puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:button80FF80"}) end
else puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:button"..count}) end else
end if data[x] and data[x][z]==1 then
end say("boom! "..event.puncher .. " is dead ");puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:buttonred"});
end local player_ = puzzle.get_player(event.puncher);
player_:setpos({x=spawnpos.x-1,y=spawnpos.y+1,z=spawnpos.z-1});
self.remove()
else
local count = get_mine_count(x,z);
if count == 0 then puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:buttongreen"})
else puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:button"..count}) end
end
end
end
end end

View File

@ -1,264 +1,256 @@
-- nonogram game, created in 1hr 40 min by rnd -- nonogram game, created in 1hr 40 min by rnd
-- INIT -- INIT
if not grid then if not grid then
n=6 n=9
solved = false -- do we render solution or blank? solved = false -- do we render solution or blank?
-- _G.math.randomseed(3) -- _G.math.randomseed(3)
self.spam(1) self.spam(1)
function get_score_from_string(score)
--say(score)
local scores = {}; init_score = function(levels, tops, default_value) -- [level] = {{name,score}, ...}
local j=1; --j k l local data = {} for i = 1, levels do data[i] = {} for j = 1,tops do data[i][j] = {"-",default_value} end end return data
for i=0,5 do -- 0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999 end
j = string.find(score," ", j+1);
local k = string.find(score," ", j+1); add_score = function(data,name,score,level)
local l = string.find(score," ", k+1); local datal = data[level]; local tops = #datal; local j;for i = 1,tops do
if i==5 then l = string.len(score)+1 end if score>datal[i][2] then j = i break end end
scores[i] = {string.sub(score,j+1,k-1),tonumber(string.sub(score,k+1,l-1))}; if not j then return false end; for i=tops,j+1,-1 do datal[i][1] = datal[i-1][1];datal[i][2] = datal[i-1][2] end
j=l datal[j] = {name,score} return true
end end
return scores
end _,scores_string = book.read(1); scores = minetest.deserialize(scores_string)
if not scores then scores = init_score(n-1,n-1,-999) end -- 5 levels, 5 top records
if not rom.score then _,rom.score = book.read(1) end
if not rom.score then rom.score = "0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999" end t0 = _G.minetest.get_gametime()
highscore = get_score_from_string(rom.score) local intro ="numbers at beginning of each row (coloumn) tell how many\nred blocks are together in each row ( coloumn )." ..
--self.label(string.gsub(_G.dump(highscore), "\n","")) "\npunch gray blocks to toggle them and reveal hidden red blocks.\npunch green to check solution. If you give up punch blue.";
self.label(intro)
function get_score_string(scores)
local out = "" grid = {}
for i = 0,5 do spawnpos = self.spawnpos();
out = out .. i .. " " .. offsetx = 10 - math.ceil(n/2); offsetz = math.floor(n/2);
scores[i][1] .. " " .. spawnpos.x = spawnpos.x - offsetx; spawnpos.z = spawnpos.z - offsetz;
scores[i][2] .. " " spawnpos.y = spawnpos.y+3
end
return out for i=1,n do
end grid[i]={};
for j=1,n do
t0 = _G.minetest.get_gametime() grid[i][j]=math.random(2)-1
local intro ="numbers at beginning of each row (coloumn) tell how many\nred blocks are together in each row ( coloumn )." .. end
"\npunch gray blocks to toggle them and reveal hidden red blocks.\npunch green to check solution. If you give up punch blue."; end
self.label(intro)
getcounts = function(grid)
grid = {} local rowdata = {};
spawnpos = self.spawnpos(); for i=1,n do
offsetx = 10 - math.ceil(n/2); offsetz = math.floor(n/2); rowdata[i]={}; local data = rowdata[i];
spawnpos.x = spawnpos.x - offsetx; spawnpos.z = spawnpos.z - offsetz; local s=0;local c=0;
spawnpos.y = spawnpos.y+3 for j = 1, n do
if s == 0 and grid[i][j]==1 then s=1;c=0 end
for i=1,n do if s == 1 then
grid[i]={}; if grid[i][j]==1 then
for j=1,n do c=c+1
grid[i][j]=math.random(2)-1 if j == n then data[#data+1]=c end
end else
end data[#data+1]=c; s=0
end
getcounts = function(grid)
local rowdata = {}; end
for i=1,n do end
rowdata[i]={}; local data = rowdata[i]; end
local s=0;local c=0; local coldata = {};
for j = 1, n do for j=1,n do
if s == 0 and grid[i][j]==1 then s=1;c=0 end coldata[j]={}; local data = coldata[j];
if s == 1 then local s=0;local c=0;
if grid[i][j]==1 then for i = 1, n do
c=c+1 if s == 0 and grid[i][j]==1 then s=1;c=0 end
if j == n then data[#data+1]=c end if s == 1 then
else if grid[i][j]==1 then
data[#data+1]=c; s=0 c=c+1
end if i == n then data[#data+1]=c end
else
end data[#data+1]=c; s=0
end end
end
local coldata = {}; end
for j=1,n do end
coldata[j]={}; local data = coldata[j]; end
local s=0;local c=0; return rowdata,coldata
for i = 1, n do end
if s == 0 and grid[i][j]==1 then s=1;c=0 end
if s == 1 then read_field = function()
if grid[i][j]==1 then local grid = {};
c=c+1 for i = 1, n do
if i == n then data[#data+1]=c end grid[i]={};
else for j = 1,n do
data[#data+1]=c; s=0 local typ = keyboard.read({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+i});
end if typ == "basic_robot:buttonlight_grey" then grid[i][j] = 0 else grid[i][j] = 1 end
end
end end
end return grid
end end
return rowdata,coldata
end rowdata,coldata = getcounts(grid)
read_field = function() check_solution = function()
local grid = {}; local rdata,cdata;
for i = 1, n do rdata,cdata = getcounts(read_field())
grid[i]={}; for i = 1,#rdata do
for j = 1,n do if #rdata[i]~=#rowdata[i] then return false end
local typ = keyboard.read({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+i}); for j = 1, #rdata[i] do
if typ == "basic_robot:button808080" then grid[i][j] = 0 else grid[i][j] = 1 end if rdata[i][j]~=rowdata[i][j] then return false end
end end
end end
return grid
end for i = 1,#cdata do
if #cdata[i]~=#coldata[i] then return false end
rowdata,coldata = getcounts(grid) for j = 1, #rdata[i] do
if cdata[i][j]~=coldata[i][j] then return false end
check_solution = function() end
local rdata,cdata; end
rdata,cdata = getcounts(read_field()) return true
for i = 1,#rdata do end
if #rdata[i]~=#rowdata[i] then return false end
for j = 1, #rdata[i] do get_difficulty = function()
if rdata[i][j]~=rowdata[i][j] then return false end local easy = 0;
end for k = 1, n do
end local sum=0
for i = 1,#rowdata[k]-1 do
for i = 1,#cdata do sum = sum + rowdata[k][i]+1;
if #cdata[i]~=#coldata[i] then return false end end
for j = 1, #rdata[i] do if #rowdata[k]>0 then sum = sum + rowdata[k][#rowdata[k]] else sum = n end
if cdata[i][j]~=coldata[i][j] then return false end if sum == n then easy = easy + 1 end
end end
end
return true for k = 1, n do
end local sum=0
for i = 1,#coldata[k]-1 do
get_difficulty = function() sum = sum + coldata[k][i]+1;
local easy = 0; end
for k = 1, n do if #coldata[k]>0 then sum = sum + coldata[k][#coldata[k]] else sum = n end
local sum=0 if sum == n then easy = easy + 1 end
for i = 1,#rowdata[k]-1 do end
sum = sum + rowdata[k][i]+1; easy = n-1-easy;
end if easy < 0 then easy = 0 end
if #rowdata[k]>0 then sum = sum + rowdata[k][#rowdata[k]] else sum = n end return easy
if sum == n then easy = easy + 1 end end
end
-- render game
for k = 1, n do for i=1,n do
local sum=0 for j =1,n do
for i = 1,#coldata[k]-1 do keyboard.set({x=spawnpos.x-n+j,y=spawnpos.y,z=spawnpos.z+i},0) -- clear
sum = sum + coldata[k][i]+1; keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+2*n-i+1},0) -- clear
end local typ;
if #coldata[k]>0 then sum = sum + coldata[k][#coldata[k]] else sum = n end if grid[j][i]==0 then typ = 13 else typ = 2 end
if sum == n then easy = easy + 1 end if not solved then typ = 13 end
end keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ) --board
easy = 5-easy; end
if easy < 0 then easy = 0 end end
return easy
end --render counts rows
for i=1,n do
-- render game length = #rowdata[i]
for i=1,n do for k = 1,length do
for j =1,n do keyboard.set({x=spawnpos.x-length+k,y=spawnpos.y,z=spawnpos.z+i},rowdata[i][k]+17)
keyboard.set({x=spawnpos.x-n+j,y=spawnpos.y,z=spawnpos.z+i},0) -- clear end
keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+2*n-i+1},0) -- clear end
local typ; --render counts coloumns
if grid[j][i]==0 then typ = 2 else typ = 3 end for j=1,n do
if not solved then typ = 2 end length = #coldata[j]
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ) --board for k = 1,length do
end keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+k+n},coldata[j][k]+17)
end end
end
--render counts rows keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},9) -- game check button
for i=1,n do keyboard.set({x=spawnpos.x+2,y=spawnpos.y,z=spawnpos.z},7) -- game check button
length = #rowdata[i]
for k = 1,length do local players = find_player(6,spawnpos)
keyboard.set({x=spawnpos.x-length+k,y=spawnpos.y,z=spawnpos.z+i},rowdata[i][k]+7) if not players then error("nonogram: no players near") end
end local pname = players[1];
end
--render counts coloumns self.listen_punch(self.pos()) -- attach punch listener
for j=1,n do
length = #coldata[j] --self.label()
for k = 1,length do
keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+k+n},coldata[j][k]+7) --self.label(string.gsub(_G.dump(read_field()),"\n","") )
end difficulty = get_difficulty()
end reward = 0; limit = 0;
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},4) -- game check button
keyboard.set({x=spawnpos.x+2,y=spawnpos.y,z=spawnpos.z},5) -- game check button if difficulty == 5 then limit = 120 reward = 10
elseif difficulty == 4 then limit = 115 reward = 9 -- 60s
local players = find_player(4,spawnpos) elseif difficulty == 3 then limit = 100 reward = 8
if not players then error("minesweeper: no players near") end elseif difficulty == 2 then limit = 80 reward = 7
local pname = players[1]; elseif difficulty <= 1 then limit = 70 reward = 6
end
if not scores[difficulty] then scores[difficulty] = {{"-",-999}} end
--self.label() if not scores[difficulty][1] then scores[difficulty][1] = {"-",-999} end
--self.label(string.gsub(_G.dump(read_field()),"\n","") ) minetest.chat_send_player(pname, "nonogram difficulty " .. difficulty .. ". you will get " .. reward .. " gold if you solve it in faster than " .. limit .."s" ..
difficulty = get_difficulty() ". Current record " .. -scores[difficulty][1][2] .. " by " .. scores[difficulty][1][1])
reward = 0; limit = 0;
if difficulty == 5 then limit = 120 reward = 10 end
elseif difficulty == 4 then limit = 115 reward = 9 -- 60s
elseif difficulty == 3 then limit = 100 reward = 8 event = keyboard.get()
elseif difficulty == 2 then limit = 80 reward = 7 if event then
elseif difficulty <= 1 then limit = 70 reward = 6 if event.y == spawnpos.y and event.z == spawnpos.z then
end if event.x == spawnpos.x+1 then -- check solution
minetest.chat_send_player(pname, "nonogram difficulty " .. difficulty .. ". you will get " .. reward .. " gold if you solve it in faster than " .. limit .."s" .. if check_solution() then
". Current record " .. highscore[difficulty][2] .. " by " .. highscore[difficulty][1]) t = _G.minetest.get_gametime(); t = t- t0;
local msg = "";
end keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},2)
msg = n .. "x" .. n .. " nonogram (difficuly " .. difficulty .. ") solved by " .. event.puncher .. " in " .. t .. " seconds. "
event = keyboard.get()
if event then if t < limit then
if event.y == spawnpos.y and event.z == spawnpos.z then msg = msg .. " He gets " .. reward .. " gold for quick solve.";
if event.x == spawnpos.x+1 then -- check solution else
if check_solution() then reward = reward*2*(1-2*(t-limit)/limit)/2; if reward<0 then reward = 0 end
t = _G.minetest.get_gametime(); t = t- t0; reward = math.floor(reward);
local msg = ""; msg = msg .. " Your time was more than " .. limit .. ", you get " .. reward .. " gold ";
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},2) end
msg = n .. "x" .. n .. " nonogram (difficuly " .. difficulty .. ") solved by " .. event.puncher .. " in " .. t .. " seconds. "
-- highscore
if t < limit then if add_score(scores,event.puncher,-t,difficulty) then
msg = msg .. " He gets " .. reward .. " gold for quick solve."; say("nonogram difficulty " .. difficulty .. ": new record " .. t .. " s !")
else local sdata = scores[difficulty];
reward = reward*2*(1-2*(t-limit)/limit)/2; if reward<0 then reward = 0 end local out = {"TOP 5 PLAYERS/TIMES: "}; for i=1,#sdata do out[#out+1] = sdata[i][1] .. " " .. -sdata[i][2].."," end say(table.concat(out," "))
reward = math.floor(reward); book.write(1,"scores", minetest.serialize(scores))
msg = msg .. " Your time was more than " .. limit .. ", you get " .. reward .. " gold "; end
end
-- highscore if reward>0 then
if t<highscore[difficulty][2] then local player = _G.minetest.get_player_by_name(event.puncher);
say("nonogram: new record " .. t .. " s ! old record " .. highscore[difficulty][2] .. "s by " .. highscore[difficulty][1]) if player then
highscore[difficulty] = {event.puncher, t} local inv = player:get_inventory();
rom.score = get_score_string(highscore) inv:add_item("main",_G.ItemStack("default:gold_ingot " .. reward))
book.write(1,"scores", rom.score) end
end end
minetest.chat_send_player(event.puncher,msg)
if reward>0 then
local player = _G.minetest.get_player_by_name(event.puncher); self.remove()
if player then
local inv = player:get_inventory(); else self.label("FAIL") end
inv:add_item("main",_G.ItemStack("default:gold_ingot " .. reward)) elseif event.x == spawnpos.x+2 then -- solve
end minetest.chat_send_player(event.puncher,"you gave up on game, displaying solution")
end for i=1,n do
minetest.chat_send_player(event.puncher,msg) for j =1,n do
local typ;
self.remove() if grid[j][i]==0 then typ = 13 else typ = 2 end
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ)
else self.label("FAIL") end end
elseif event.x == spawnpos.x+2 then -- solve end
minetest.chat_send_player(event.puncher,"you gave up on game, displaying solution") self.remove()
for i=1,n do end
for j =1,n do else
local typ; local i = event.x-spawnpos.x;local j = event.z-spawnpos.z;
if grid[j][i]==0 then typ = 2 else typ = 3 end if i>0 and i<=n and j>0 and j<=n then
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ) local typ = keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j});
end local newtyp;
end if typ ~= "basic_robot:buttonlight_grey" then newtyp = 13
self.remove() else newtyp = 2
end end
else keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},newtyp);
local i = event.x-spawnpos.x;local j = event.z-spawnpos.z; end
if i>0 and i<=n and j>0 and j<=n then end
local typ = keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j});
local newtyp;
if typ == "basic_robot:button808080" then newtyp = 3
else newtyp = 2
end
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},newtyp);
end
end
end end

View File

@ -1,112 +1,112 @@
-- paint canvas by rnd, 2018 -- paint canvas by rnd, 2018
if not init then if not init then
colors = { colors = {
"black","blue","brown","cyan","dark_green","dark_grey","green","grey", "white","yellow","orange","red","magenta","purple","blue","cyan",
"magenta","orange","pink","red","violet","white","yellow" "green","dark_green","brown","tan","light_grey","medium_grey","dark_grey","black"
} }
invcolors = {}; for i = 1,#colors do invcolors[colors[i]] = i end invcolors = {}; for i = 1,#colors do invcolors[colors[i]] = i end
color = 1; color = invcolors["black"];
size = 16; size = 16;
init = true init = true
local ent = _G.basic_robot.data[self.name()].obj:get_luaentity(); local ent = _G.basic_robot.data[self.name()].obj:get_luaentity();
ent.timestep = 0.5 ent.timestep = 0.125
players = find_player(5); if not players then self.remove() end players = find_player(5); if not players then self.remove() end
player = _G.minetest.get_player_by_name(players[1]) player = _G.minetest.get_player_by_name(players[1])
self.label("-> " .. players[1]) self.label("-> " .. players[1])
spos = self.spawnpos(); spos.y=spos.y+1; spos = self.spawnpos(); spos.y=spos.y+1;
canvasn = "basic_robot:buttonwhite"
canvasn = "wool:white" reset_canvas = function()
reset_canvas = function() for i = 1, size do
for i = 1, size do for j = 1, size do
for j = 1, size do minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn})
minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn}) end
end end
end end
end reset_canvas()
reset_canvas()
save_image = function()
save_image = function() local ret = {};
local ret = {}; for i = 1, size do
for i = 1, size do for j = 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,19)
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;
local pcolor = invcolors[nname] or 1; ret[#ret+1]= string.char(96+pcolor)
ret[#ret+1]= string.char(96+pcolor) end
end end
end
return table.concat(ret,"") return table.concat(ret,"")
end end
load_image = function(image) load_image = function(image)
if not image then return end if not image then return end
local ret = {}; local k = 0; local ret = {}; local k = 0;
for i = 1, size do for i = 1, size do
for j = 1, size do for j = 1, size do
k=k+1; k=k+1;
local pcolor = colors[string.byte(image,k)-96] or "black"; 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}) minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = "basic_robot:button"..pcolor})
end end
end end
end end
--draw buttons --draw buttons
for i = 1,#colors do for i = 1,#colors do
minetest.set_node({x=spos.x +i , y = spos.y , z = spos.z },{name = "wool:"..colors[i]}) minetest.set_node({x=spos.x +i , y = spos.y , z = spos.z },{name = "basic_robot:button"..colors[i]})
end 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 +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"}) 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}; 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}; T0 = {x=spos.x+0.5,y=spos.y+0.5,z=spos.z-0.5*vn.z};
get_intersect = function(vn, T0, p, v) get_intersect = function(vn, T0, p, v)
local a = (T0.x-p.x)*vn.x + (T0.y-p.y)*vn.y + (T0.z-p.z)*vn.z; local a = (T0.x-p.x)*vn.x + (T0.y-p.y)*vn.y + (T0.z-p.z)*vn.z;
local b = vn.x*v.x + vn.y*v.y + vn.z*v.z local b = vn.x*v.x + vn.y*v.y + vn.z*v.z
if b<=0 then return nil end if b<=0 then return nil end
if a<=0 then return nil end if a<=0 then return nil end
local t = a / b local t = a / b
return {x = p.x+v.x*t, y= p.y+v.y*t, z = p.z+v.z*t} return {x = p.x+v.x*t, y= p.y+v.y*t, z = p.z+v.z*t}
end end
end end
if player:get_player_control().LMB then -- player interacts with 'virtual canvas gui' if player:get_player_control().LMB then -- player interacts with 'virtual canvas gui'
local v = player:get_look_dir(); local v = player:get_look_dir();
local p = player:get_pos(); p.y = p.y + 1.5 local p = player:get_pos(); p.y = p.y + 1.5
local c = get_intersect(vn,T0,p,v); local c = get_intersect(vn,T0,p,v);
if c then if c then
local x = c.x - T0.x; local y = c.y - T0.y local x = c.x - T0.x; local y = c.y - T0.y
if x>0 and x<size and y>-2 and y<size then if x>0 and x<size and y>-2 and y<size then
if y>0 then -- above: painting if y>0 then -- above: painting
c.z = c.z+0.5 c.z = c.z+0.5
minetest.set_node(c, {name = "wool:" .. colors[color]}) minetest.set_node(c, {name = "basic_robot:button" .. colors[color]})
elseif y>-1 then -- color selection elseif y>-1 then -- color selection
x = 1+math.floor(x) x = 1+math.floor(x)
if colors[x] then if colors[x] then
color = x; color = x;
self.label(colors[x]) self.label(colors[x])
end end
else -- save,load button row else -- save,load button row
x = 1+math.floor(x) x = 1+math.floor(x)
if x==1 then if x==1 then
self.label("SAVED.") self.label("SAVED.")
book.write(1,"ROBOT_IMAGE",save_image()) book.write(1,"ROBOT_IMAGE",save_image())
elseif x==2 then elseif x==2 then
local _,image = book.read(1) local _,image = book.read(1)
load_image(image); load_image(image);
self.label("LOADED.") self.label("LOADED.")
end end
end end
end end
end end
end end

221
scripts/games/rubik.lua Normal file
View File

@ -0,0 +1,221 @@
-- Rubik Cube
if not init then init = true
self.listen_punch(self.pos())
pname = find_player(5); if not pname then say("no players!");self.remove() end; pname = pname[1];
player = minetest.get_player_by_name(pname)
say("rubik cube. rotation is indicated by your view direction")
n=3;
-- top left first
yz1 = { -- size n^2, side looking toward x-
7,1,8,
1,1,1,
9,1,1
}
yz2 = { -- size n^2, side looking toward x+
7,2,8,
2,2,2,
9,2,2
}
xy1 = { -- size n^2, side looking toward z-
7,3,8,
3,3,3,
9,3,3
}
xy2 = { -- size n^2, side looking toward z+
7,4,8,
4,4,4,
9,4,4,
}
xz1 = { -- size n^2, side looking toward y-
7,5,8,
5,5,5,
9,5,5,
}
xz2 = { -- size n^2, side looking toward y+
7,6,8,
6,6,6,
9,6,6,
}
surfdata = {
["yz1"]={
rotations = {
[0] = "face cw",
[1] = "face ccw",
[2] = "horizontal +",
[3] = "horizontal -",
[4] = "vertical +",
[5] = "vertical -",
}
}
}
blocks = {
"basic_robot:button808080",
"basic_robot:buttonFF8080",
"basic_robot:button80FF80",
"basic_robot:button8080FF",
"basic_robot:buttonFFFF80",
"basic_robot:buttonFFFFFF",
"basic_robot:button_48", --7
"basic_robot:button_49", --8
"basic_robot:button_50", --9
}
render_cube = function()
local p = self.pos();
for y=1,n do
for z=1,n do
minetest.swap_node({x=p.x+1,y=p.y+y+1,z=p.z+z},{name = blocks[yz1[n*(y-1)+z]]})
end
end
for y=1,n do
for z=1,n do
minetest.swap_node({x=p.x+n+2,y=p.y+y+1,z=p.z+z},{name = blocks[yz2[n*(y-1)+z]]})
end
end
for x=1,n do
for y=1,n do
minetest.swap_node({x=p.x+x+1,y=p.y+y+1,z=p.z},{name = blocks[xy1[n*(x-1)+y]]})
end
end
for x=1,n do
for y=1,n do
minetest.swap_node({x=p.x+x+1,y=p.y+y+1,z=p.z+n+1},{name = blocks[xy2[n*(x-1)+y]]})
end
end
for x=1,n do
for z=1,n do
minetest.swap_node({x=p.x+x+1,y=p.y+1,z=p.z+z},{name = blocks[xz1[n*(x-1)+z]]})
end
end
for x=1,n do
for z=1,n do
minetest.swap_node({x=p.x+x+1,y=p.y+n+2,z=p.z+z},{name = blocks[xz2[n*(x-1)+z]]})
end
end
end
rotccw = function(tab)
local newtab = {}
local n = (#tab)^0.5
for i = 1,n do
for j = 1,n do
newtab[(i-1)*n+j] = tab[(j-1)*n+n-i+1]
end
end
return newtab
end
rotcw = function(tab)
local newtab = {}
local n = (#tab)^0.5
for i = 1,n do
for j = 1,n do
newtab[(j-1)*n+n-i+1] = tab[(i-1)*n+j]
end
end
return newtab
end
viewlim = 0.9; --how close to perpendicular need to look to rotate face
p = self.pos();
render_cube()
end
event = keyboard.get()
if event then
-- rotate depending where player looks, if he looks close to normal to surface rotate cw or ccw
local view = player:get_look_dir()
-- which way? face or horizontal or vertical?
local rot = 0; -- rotation mode
local face = "";
local x=1;local y=1;
if event.x == p.x+1 then
face= "yz1"
if math.abs(view.x)>viewlim then
rot = 0; -- face cw
yz1 = rotcw(yz1); -- face cw rotate
--slice x=1 rotate:
--[[{
xz2,row 1,rev
xy1,row 1,rev
xz1,row 1,ord
xy2,col 3,ord
}
note this same rotation can be reused for faces xz1,xy1,xz1,xy2
maybe:
rotslice(
{xz2,1=row mode, 1 = row index, -1 = reverse},
{xy1,1, 1, -1},
{xz1,1, 1, 1 = ordinary},
{xy2,2=col mode,3 = col index, 1}
)
--]]
elseif math.abs(view.y)<math.abs(view.z) then -- horizontal
if view.z>0 then rot = 2 else rot = 3 end
else
if view.y>0 then rot = 4 else rot = 5 end
end
self.label("face: " .. face ..", rotation: " .. surfdata[face].rotations[rot])
render_cube()
elseif event.x == p.x+n+2 then
self.label("yz2")
if math.abs(view.x)>viewlim then
yz2 = rotccw(yz2);
end
render_cube()
elseif event.z == p.z then
self.label("xy1")
if math.abs(view.z)>viewlim then
xy1 = rotcw(xy1);
end
render_cube()
elseif event.z == p.z+n+1 then
self.label("xy2")
if math.abs(view.z)>viewlim then
xy2 = rotccw(xy2);
end
render_cube()
elseif event.y == p.y+1 then
if math.abs(view.y)>viewlim then
xz1 = rotccw(xz1);
end
render_cube()
self.label("xz1")
elseif event.y == p.y+n+2 then
self.label("xz2")
if math.abs(view.y)>viewlim then
xz2 = rotcw(xz2);
end
render_cube()
end
--self.label(serialize(event))
end
-- ideas:
-- use whole full cube : array with n^3 elements, only border rendered.
-- PROS: easy to get slices then and rotate them! CONS: much more memory used for larger cubes
--

View File

@ -1,56 +1,59 @@
-- simple box pushing game, rnd -- simple box pushing game, rnd
if not init then if not init then
spos = self.spawnpos(); spos.x = spos.x +5; spos.z = spos.z +5; spos = self.spawnpos(); spos.x = spos.x +5; spos.z = spos.z +5;
for i = 1, 2 do for i = 1, 2 do -- set up 4 boxes
for j = 1,2 do for j = 1,2 do
puzzle.set_node({x=spos.x+i,y=spos.y,z=spos.z+j}, {name = "basic_robot:buttonFFFFFF"}) puzzle.set_node({x=spos.x+i,y=spos.y,z=spos.z+j}, {name = "basic_robot:buttonwhite"})
end end
end end
init = true init = true
players = find_player(5); players = find_player(5);
if not players then say("no players nearby") self.remove() end if not players then say("no players nearby") self.remove() end
say("BOX PUSH demo. punch the white box to move it around ") self.label("BOX PUSH demo. punch the white box to move it around.\npush it into blue block to make it disappear.")
pushables = {[1] = true} -- button types pushables = {[1] = true} -- button types
canpushnodes = {["air"] = 1, ["basic_robot:button8080FF"] = 2} -- 1 push node, 2 absorb node canpushnodes = {["air"] = 1, ["basic_robot:buttonblue"] = 2} -- 1 push node, 2 absorb node
end
self.listen_punch(self.pos()) -- robot will now read punch button events in 32x32 box area
event = keyboard.get() end
if event then
local boxtype = event.type event = keyboard.get()
if pushables[boxtype] then
player = puzzle.get_player(event.puncher) if event then -- there was punch event
local pos = player:getpos(); local boxtype = event.type
local boxpos = {x = event.x, y = event.y, z = event.z}; if pushables[boxtype] then
local diff = { pos.x-boxpos.x, pos.z-boxpos.z}; player = puzzle.get_player(event.puncher)
local pos = player:getpos();
local newx,newz local boxpos = {x = event.x, y = event.y, z = event.z};
if math.abs(diff[1])>math.abs(diff[2]) then -- punch in x-direction local diff = { pos.x-boxpos.x, pos.z-boxpos.z};
newx = boxpos.x - (diff[1]>0 and 1 or -1)
newz = boxpos.z local newx,newz
else if math.abs(diff[1])>math.abs(diff[2]) then -- punch in x-direction
newx = boxpos.x newx = boxpos.x - (diff[1]>0 and 1 or -1)
newz = boxpos.z - (diff[2]>0 and 1 or -1) newz = boxpos.z
end else
newx = boxpos.x
local newnode = puzzle.get_node({x=newx, y= boxpos.y, z= newz}).name newz = boxpos.z - (diff[2]>0 and 1 or -1)
end
local canpush = canpushnodes[newnode] local newnode = puzzle.get_node({x=newx, y= boxpos.y, z= newz}).name
if canpush then
local oldnode = puzzle.get_node(boxpos).name
puzzle.set_node(boxpos,{name= "air"}) -- remove node local canpush = canpushnodes[newnode]
if canpush == 1 then -- simply move the box if canpush then
newnode = oldnode local oldnode = puzzle.get_node(boxpos).name
elseif canpush == 2 then -- absorb the box puzzle.set_node(boxpos,{name= "air"}) -- remove node
newnode = newnode if canpush == 1 then -- simply move the box
end newnode = oldnode
elseif canpush == 2 then -- absorb the box
puzzle.set_node({x=newx, y= boxpos.y, z= newz}, {name = newnode}) newnode = newnode
end end
end
--say(serialize(event)) puzzle.set_node({x=newx, y= boxpos.y, z= newz}, {name = newnode})
end
end
--say(serialize(event))
end end

View File

@ -0,0 +1,24 @@
if not data then
m=50;n=50; minescount = m*n/10;
t0 = _G.minetest.get_gametime();
data = {}; spawnpos = self.spawnpos();
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
get_mine_count = function(i,j)
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
for k = -1,1 do for l = -1,1 do
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
end end
return count
end
for i = 1,m do for j = 1,n do
if get_mine_count(i,j) > 0 or (data[i] and data[i][j] == 1) then
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "basic_robot:buttonFFFFFF"})
else
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "default:dirt"})
end
end end
end
self.remove()

View File

@ -19,7 +19,7 @@ if not init then
board[i]={}; board[i]={};
for j = 1,n do for j = 1,n do
k=k+1 k=k+1
board[i][j]=7+ret[k] -- 7 numbers, 82 letters board[i][j]=17+ret[k] -- 7 numbers, 82 letters
end end
end end
board[math.random(n)][math.random(n)] = 0 board[math.random(n)][math.random(n)] = 0
@ -71,7 +71,8 @@ if not init then
create_board(size) create_board(size)
render_board() render_board()
self.listen_punch(self.pos()) -- attach punch listener
end end
event = keyboard.get(); event = keyboard.get();

1398
scripts/games/sokoban.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,189 +1,234 @@
-- SOKOBAN GAME, by rnd, robots port -- SOKOBAN GAME, by rnd, robots port
if not sokoban then
if not sokoban then load_player_progress = true -- true will disable level loading and load saved player progress
sokoban = {}; sokoban = {};
local players = find_player(8); local players = find_player(8);
if not players then error("sokoban: no player near") end if not players then error("sokoban: no player near") end
name = players[1]; name = players[1];
-- self.show_form(name, _, text = book.read(1)
-- "size[2,1.25]".. gamedata = minetest.deserialize(text);
-- "label[0,0;SELECT LEVEL 1-90]"..
-- "field[0.25,1;1,1;LVL;LEVEL;1]".. if gamedata and gamedata[name] then
-- "button_exit[1.25,0.75;1,1;OK;OK]" say("#sokoban: welcome back " .. name .. ", loading level " .. gamedata[name].lvl+1)
-- ) else
state = 1 -- will wait for form receive otherwise game play say("sokoban: welcome new player " .. name)
self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks") end
player_ = puzzle.get_player(name); -- get player entity - player must be present in area if not load_player_progress then
player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}) -- reset player self.show_form(name,
"size[3,1.25]"..
"label[0,0;SELECT LEVEL 1-90]"..
self.spam(1) "field[0.25,1;1.25,1;LVL;LEVEL;1]"..
sokoban.push_time = 0 "button_exit[1.25,0.75;1,1;OK;OK]"
sokoban.blocks = 0;sokoban.level = 0; sokoban.moves=0; )
imax = 0; jmax = 0 self.read_form() -- clear inputs
end
sokoban.load=0;sokoban.playername =""; sokoban.pos = {};
SOKOBAN_WALL = "moreblocks:cactus_brick" state = 1 -- will wait for form receive otherwise game play
SOKOBAN_FLOOR = "default:silver_sandstone" self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks")
SOKOBAN_GOAL = "default:aspen_tree"
SOKOBAN_BOX = "basic_robot:buttonFFFFFF" player_ = puzzle.get_player(name); -- get player entity - player must be present in area
player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}) -- reset player
load_level = function(lvl)
local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1; self.spam(1)
sokoban.pos = pos; self.listen_punch(self.pos()); -- attach punch listener
sokoban.playername = name sokoban.push_time = 0
sokoban.blocks = 0;sokoban.level = 0; sokoban.moves=0;
if lvl == nil then return end imax = 0; jmax = 0
if lvl <0 or lvl >89 then return end
sokoban.load=0;sokoban.playername =""; sokoban.pos = {};
local file = _G.io.open(minetest.get_modpath("basic_robot").."\\scripts\\sokoban.txt","r") SOKOBAN_WALL = "moreblocks:cactus_brick"
if not file then return end SOKOBAN_FLOOR = "default:silver_sandstone"
local str = ""; local s; local p = {x=pos.x,y=pos.y,z=pos.z}; local i,j;i=0; SOKOBAN_GOAL = "default:aspen_tree"
local lvl_found = false SOKOBAN_BOX = "basic_robot:buttonwhite"
while str~= nil do
str = file:read("*line");
if str~=nil and str =="; "..lvl then lvl_found=true break end load_level = function(lvl)
end
if not lvl_found then file:close();return end local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1;
sokoban.pos = pos;
sokoban.blocks = 0;sokoban.level = lvl+1; sokoban.moves=0; sokoban.playername = name
imax=0; jmax = 0;
while str~= nil do if lvl == nil then return end
str = file:read("*line"); if lvl <0 or lvl >89 then return end
if str~=nil then
if string.sub(str,1,1)==";" then local file = _G.io.open(minetest.get_modpath("basic_robot").."/scripts/games/sokoban.txt","r")
imax=i; if not file then return end
file:close(); local str = ""; local s; local p = {x=pos.x,y=pos.y,z=pos.z}; local i,j;i=0;
player_:set_physics_override({jump=0}) local lvl_found = false
player_:set_eye_offset({x=0,y=20,z=0},{x=0,y=0,z=0}); while str~= nil do
return str = file:read("*line");
end if str~=nil and str =="; "..lvl then lvl_found=true break end
i=i+1; end
if string.len(str)>jmax then jmax = string.len(str) end -- determine max dimensions if not lvl_found then file:close();return end
for j = 1,string.len(str) do
p.x=pos.x+i;p.y=pos.y; p.z=pos.z+j; s=string.sub(str,j,j); sokoban.blocks = 0;sokoban.level = lvl+1; sokoban.moves=0;
p.y=p.y-1; imax=0; jmax = 0;
if puzzle.get_node(p).name~=SOKOBAN_FLOOR then puzzle.set_node(p,{name=SOKOBAN_FLOOR}); end -- clear floor while str~= nil do
p.y=p.y+1; str = file:read("*line");
if s==" " and puzzle.get_node(p).name~="air" then puzzle.set_node(p,{name="air"}) end if str~=nil then
if s=="#" and puzzle.get_node(p).name~=SOKOBAN_WALL then puzzle.set_node(p,{name=SOKOBAN_WALL}) end if string.sub(str,1,1)==";" then
if s=="$" then puzzle.set_node(p,{name=SOKOBAN_BOX});sokoban.blocks=sokoban.blocks+1 end imax=i;
if s=="." then p.y=p.y-1;puzzle.set_node(p,{name=SOKOBAN_GOAL}); p.y=p.y+1;puzzle.set_node(p,{name="air"}) end file:close();
--starting position player_:set_physics_override({jump=0})
if s=="@" then player_:set_eye_offset({x=0,y=20,z=0},{x=0,y=0,z=0});
player_:setpos({x=p.x,y=p.y-0.5,z=p.z}); -- move player to start position return
--p.y=p.y-1;puzzle.set_node(p,{name="default:glass"}); end
puzzle.set_node(p,{name="air"}) i=i+1;
p.y=p.y+1;puzzle.set_node(p,{name="air"}) if string.len(str)>jmax then jmax = string.len(str) end -- determine max dimensions
--p.y=p.y+2;puzzle.set_node(p,{name="default:ladder"}) for j = 1,string.len(str) do
end p.x=pos.x+i;p.y=pos.y; p.z=pos.z+j; s=string.sub(str,j,j);
if s~="@" then p.y = pos.y+2;puzzle.set_node(p,{name="air"}); -- ceiling default:obsidian_glass p.y=p.y-1;
else --p.y=pos.y+2;puzzle.set_node(p,{name="default:ladder"}) if puzzle.get_node(p).name~=SOKOBAN_FLOOR then puzzle.set_node(p,{name=SOKOBAN_FLOOR}); end -- clear floor
end -- roof above to block jumps p.y=p.y+1;
if s==" " and puzzle.get_node(p).name~="air" then puzzle.set_node(p,{name="air"}) end
end if s=="#" and puzzle.get_node(p).name~=SOKOBAN_WALL then puzzle.set_node(p,{name=SOKOBAN_WALL}) end
end if s=="$" then puzzle.set_node(p,{name=SOKOBAN_BOX});sokoban.blocks=sokoban.blocks+1 end
end if s=="." then p.y=p.y-1;puzzle.set_node(p,{name=SOKOBAN_GOAL}); p.y=p.y+1;puzzle.set_node(p,{name="air"}) end
--starting position
file:close(); if s=="@" then
end player_:set_pos({x=p.x,y=p.y-0.5,z=p.z}); -- move player to start position
--p.y=p.y-1;puzzle.set_node(p,{name="default:glass"});
clear_game = function() puzzle.set_node(p,{name="air"})
local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1; p.y=p.y+1;puzzle.set_node(p,{name="air"})
for i = 1, 20 do --p.y=p.y+2;puzzle.set_node(p,{name="default:ladder"})
for j = 1,20 do end
local node = minetest.get_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}).name if s~="@" then p.y = pos.y+2;puzzle.set_node(p,{name="air"}); -- ceiling default:obsidian_glass
if node ~= "default:silver_sandstone" then minetest.set_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}, {name = "default:silver_sandstone"}) end else --p.y=pos.y+2;puzzle.set_node(p,{name="default:ladder"})
node = minetest.get_node({x=pos.x+i,y=pos.y,z=pos.z+j}).name end -- roof above to block jumps
if node ~= "air" then minetest.set_node({x=pos.x+i,y=pos.y,z=pos.z+j}, {name = "air"}) end
end end
end end
end end
end file:close();
end
if state == 1 then clear_game = function()
clear_game() local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1;
load_level(20) for i = 1, 20 do
state = 0 for j = 1,20 do
self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks") local node = minetest.get_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}).name
else if node ~= "default:silver_sandstone" then minetest.set_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}, {name = "default:silver_sandstone"}) end
node = minetest.get_node({x=pos.x+i,y=pos.y,z=pos.z+j}).name
local ppos = player_:getpos() if node ~= "air" then minetest.set_node({x=pos.x+i,y=pos.y,z=pos.z+j}, {name = "air"}) end
if math.abs(ppos.y-sokoban.pos.y)~= 0.5 then minetest.chat_send_player(name,colorize("red", "SOKOBAN: " .. name .. " QUITS ! ")); end
player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}); self.remove() end end
end
event = keyboard.get();
force_load = function()
if event then local lvl = 0 -- 1st level at idx 0 - sokoban level 1
clear_game()
local pname = event.puncher if gamedata and gamedata[name] then lvl = gamedata[name].lvl end -- load next level to play
if pname ~= name then goto quit end load_level(lvl)
local pos = {x=event.x, y = event.y, z = event.z}; state = 0
local p=player.getpos(pname);local q={x=pos.x,y=pos.y,z=pos.z} self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand can't pull. goal is to get all white boxes pushed on aspen blocks")
p.x=p.x-q.x;p.y=p.y-q.y;p.z=p.z-q.z
if math.abs(p.y+0.5)>0 then goto quit end end
if math.abs(p.x)>math.abs(p.z) then -- determine push direction
if p.z<-0.5 or p.z>0.5 or math.abs(p.x)>1.5 then goto quit end if load_player_progress then
if p.x+q.x>q.x then q.x= q.x-1 force_load() -- this prevents selecting custom level and loads players progress if any
else q.x = q.x+1 end
end
else end
if p.x<-0.5 or p.x>0.5 or math.abs(p.z)>1.5 then goto quit end
if p.z+q.z>q.z then q.z= q.z-1
else q.z = q.z+1 if state == 1 then -- wait to load game
end sender,fields = self.read_form()
end if fields then
if fields.OK then
local lvl = tonumber(fields.LVL or 1)-1;
if minetest.get_node(q).name=="air" then -- push crate clear_game()
sokoban.moves = sokoban.moves+1 load_level(lvl)
local old_infotext = minetest.get_meta(pos):get_string("infotext"); state = 0
minetest.set_node(pos,{name="air"}) self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks")
minetest.set_node(q,{name=SOKOBAN_BOX}) else
minetest.sound_play("default_dig_dig_immediate", {pos=q,gain=1.0,max_hear_distance = 24,}) -- sound of pushing self.remove()
local meta = minetest.get_meta(q); end
q.y=q.y-1; end
if minetest.get_node(q).name==SOKOBAN_GOAL then
if old_infotext~="GOAL REACHED" then
sokoban.blocks = sokoban.blocks -1; else -- game
end
meta:set_string("infotext", "GOAL REACHED") local ppos = player_:get_pos()
else if math.abs(ppos.y-sokoban.pos.y)~= 0.5 then minetest.chat_send_player(name,colorize("red", "SOKOBAN: " .. name .. " QUITS ! "));
if old_infotext=="GOAL REACHED" then player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}); self.remove() end
sokoban.blocks = sokoban.blocks +1
end event = keyboard.get();
--meta:set_string("infotext", "push crate on top of goal block")
end if event then
end
local pname = event.puncher
if sokoban.blocks~=0 then -- how many blocks left if pname ~= name then goto quit end
--say("move " .. sokoban.moves .. " : " ..sokoban.blocks .. " crates left "); local pos = {x=event.x, y = event.y, z = event.z};
else if minetest.get_node(pos).name == "air" then return end -- ignore move, block wasnt punched
say("games: ".. name .. " just solved sokoban level ".. sokoban.level .. " in " .. sokoban.moves .. " moves."); local p=player.getpos(pname);local q={x=pos.x,y=pos.y,z=pos.z}
player_:set_physics_override({jump=1}) p.x=p.x-q.x;p.y=p.y-q.y;p.z=p.z-q.z
player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0}) if math.abs(p.y+0.5)>0 then goto quit end
if math.abs(p.x)>math.abs(p.z) then -- determine push direction
local player = _G.minetest.get_player_by_name(event.puncher); if p.z<-0.5 or p.z>0.5 or math.abs(p.x)>1.5 then goto quit end
if player then if p.x+q.x>q.x then q.x= q.x-1
local inv = player:get_inventory(); else q.x = q.x+1
inv:add_item("main",_G.ItemStack("skyblock:sokoban 4 ")) end
end else
if p.x<-0.5 or p.x>0.5 or math.abs(p.z)>1.5 then goto quit end
local i,j; if p.z+q.z>q.z then q.z= q.z-1
for i = 1,imax do else q.z = q.z+1
for j=1,jmax do end
minetest.set_node({x= sokoban.pos.x+i,y=sokoban.pos.y,z=sokoban.pos.z+j}, {name = "air"}); -- clear level end
end
end
if minetest.get_node(q).name=="air" then -- push crate
sokoban.playername = ""; sokoban.level = 1 sokoban.moves = sokoban.moves+1
end local old_infotext = minetest.get_meta(pos):get_string("infotext");
::quit:: minetest.set_node(pos,{name="air"})
end minetest.set_node(q,{name=SOKOBAN_BOX})
minetest.sound_play("default_dig_dig_immediate", {pos=q,gain=1.0,max_hear_distance = 24,}) -- sound of pushing
local meta = minetest.get_meta(q);
q.y=q.y-1;
if minetest.get_node(q).name==SOKOBAN_GOAL then
if old_infotext~="GOAL REACHED" then
sokoban.blocks = sokoban.blocks -1;
end
meta:set_string("infotext", "GOAL REACHED")
else
if old_infotext=="GOAL REACHED" then
sokoban.blocks = sokoban.blocks +1
end
--meta:set_string("infotext", "push crate on top of goal block")
end
end
if sokoban.blocks~=0 then -- how many blocks left
--say("move " .. sokoban.moves .. " : " ..sokoban.blocks .. " crates left ");
else
say("games: ".. name .. " just solved sokoban level ".. sokoban.level .. " in " .. sokoban.moves .. " moves.");
if not gamedata then gamedata = {} end
gamedata[name] = {lvl = sokoban.level}; -- increased level
book.write(1,"sokoban players", minetest.serialize(gamedata))
player_:set_physics_override({jump=1})
player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0})
local player = _G.minetest.get_player_by_name(event.puncher);
if player then
local inv = player:get_inventory();
inv:add_item("main",_G.ItemStack("default:gold_ingot " .. 2*sokoban.level))
end
local i,j;
for i = 1,imax do
for j=1,jmax do
minetest.set_node({x= sokoban.pos.x+i,y=sokoban.pos.y,z=sokoban.pos.z+j}, {name = "air"}); -- clear level
end
end
sokoban.playername = ""; sokoban.level = 1
end
::quit::
end
end end

View File

@ -79,6 +79,7 @@ render_lights(); render_switches(true)
self.label("GOAL OF GAME: punch buttons with numbers in correct order to turn all blocks to 0") self.label("GOAL OF GAME: punch buttons with numbers in correct order to turn all blocks to 0")
self.listen_punch(self.pos()) -- attach punch listener
--self.label(serialize(switches)) --self.label(serialize(switches))
end end

113
scripts/games/wordle.lua Normal file
View File

@ -0,0 +1,113 @@
--wordle game by rnd (2022), made in 15 minutes
-- 5 letter word is picked randomly
-- you have 6 tries to guess the word, write it in and it will color letters:
-- green = correct letter, correct place, yellow = correct letter, wrong place,
-- gray = letter not in word!
if not init then init = true
-- load this from file..
wordlist = {
"pulse", "audio", "solar", "bacon", "laser", "pizza", "maybe", "guess", "stuff",
"seven", "world", "about", "again", "heart", "water", "happy", "sixty", "board",
"month", "angel", "death", "green", "music", "fifty", "three", "party", "piano",
"mouth", "woman", "sugar", "amber", "dream", "apple", "laugh", "tiger", "faith",
"earth", "river", "money", "peace", "forty", "words", "smile", "abate", "house",
"alone", "watch", "lemon", "south", "erica", "anime", "after", "santa", "admin",
"jesus", "china", "blood", "megan", "thing", "light", "david", "cough", "story",
"power", "india", "point", "today", "anger", "night", "glory", "april", "candy",
"puppy", "above", "phone", "vegan", "forum", "irish", "birth", "other", "grace",
"queen", "pasta", "plant", "smart", "knife", "magic", "jelly", "black", "media",
--100
"honor", "cycle", "truth", "zebra", "train", "bully", "brain", "mango", "under",
"dirty", "robot", "eight", "fruit", "panda", "truck", "field", "bible", "radio",
"dance", "voice", "smith", "sorry", "paris", "being", "lover", "never", "royal",
"venus", "metal", "penny", "honey", "color", "cloud", "scarf", "state", "value",
"mouse", "north", "bread", "daily", "paper", "beard", "alive", "place", "chair",
"badge", "worth", "crazy", "photo", "dress", "table", "cross", "clear", "white",
"march", "ocean", "belly", "ninja", "young", "range", "maria", "great", "sweet",
"karen", "scent", "beach", "space", "clock", "allah", "peach", "sound", "fever",
"youth", "union", "daisy", "plate", "eagle", "human", "start", "funny", "right",
"molly", "guard", "witch", "dough", "think", "image", "album", "socks", "catch",
--200
"sleep", "below", "organ", "peter", "cupid", "storm", "silly", "berry", "rhyme",
"carol", "olive", "leave", "whale", "james", "brave", "asian", "every", "arrow",
"there", "ebola", "later", "bacon", "local", "graph", "super", "obama", "brown",
"onion", "simon", "globe", "alley", "stick", "spain", "daddy", "scare", "quiet",
"touch", "clean", "liver", "lucky", "given", "lunch", "child", "clone", "glove",
"meter", "nancy", "plain", "solid", "uncle", "shout", "bored", "early", "video",
"brian", "cheer", "texas", "often", "sushi", "chaos", "tulip", "alien", "apart",
"fight", "coach", "force", "trust", "angle", "beast", "craft", "chess", "skull",
"order", "judge", "swing", "drive", "shine", "stand", "stage", "oscar", "ember",
"worry", "drama", "raven", "sight", "short", "botox", "unity", "horse", "trout",
--300
"devil", "spoon", "clown", "grand", "gnome", "binge", "paula", "award", "quick",
"cause", "close", "scout", "snail", "purse", "topic", "teeth", "sauce", "share",
"along", "worse", "movie", "reach", "giant", "quack", "shark", "first", "count",
"agent", "shelf", "grape", "drink", "skate", "wrong", "cream", "snake", "heavy",
"tooth", "heard", "idiot", "scary", "chain", "break", "valve", "agony", "salad",
"shell", "scope", "tupac", "track", "final", "crown", "group", "wagon", "doing",
"robin", "false", "small", "block", "brush", "salsa", "grain", "wings", "arian",
"allow", "habit", "stove", "tower", "stars", "total", "plane", "comet", "tweet",
"abide", "frown", "roman", "grant", "ready", "blast", "treat", "poppy", "biome",
"oasis", "roger", "ghost", "abode", "abort", "court", "petal", "flood", "cider",
--400
"orion", "extra", "pearl", "gator", "rough", "koala", "melon", "price", "alpha",
"smell", "chase", "fresh", "quest", "store", "grove", "round", "sense", "chest",
"fancy", "loose", "match", "pluto", "sport", "sheep", "crime", "grade", "pride",
"lance", "billy", "virus", "twerp", "kenya", "model", "ledge", "tired", "level",
"juice", "quart", "amish", "flame", "event", "offer", "twist", "actor", "maple",
"hinge", "proud", "boone", "nasty", "hyper", "paint", "press", "patch", "mercy",
"baker", "broom", "rhino", "putin", "greed", "inter", "curve", "giver", "flute",
"class", "hyena", "stock", "sting", "fable", "loved", "chant", "focus", "bench",
"birds", "brand", "otter", "goose", "ought", "boron", "dodge", "sloth", "eager",
"serve", "fella", "cover", "genre", "cable", "apron", "worst", "tommy", "egypt"
--500
}
word = wordlist[math.random(#wordlist)];
letters = {}
for i = 1,string.len(word) do letters[string.sub(word,i,i)] = true end
responses = {};
maxtries = 6
self.label("GUESSWORD " .. word)
self.label("WORDLE GAME\n\nINSTRUCTIONS:\ntry to guess 5 letter word by typing it in chat like\n\n:guess\n\nyou have " .. maxtries .. " tries.\n"..
"gray color indicates letter is not in word, yellow color indicates letter is\nin word but not in correct position. green color indicates correct letter at\ncorrect position")
self.listen(1)
end
speaker,msg = self.listen_msg()
if #responses == maxtries then
responses[#responses+1] = minetest.colorize("red","GAME OVER! correct word was " .. word)
self.label(table.concat(responses,"\n"))
end
if msg and #responses<maxtries and string.sub(msg,1,1)==":" and string.len(msg)-1 == string.len(word) then
msg = string.sub(msg,2)
local out = {}
local guessed = true
for i = 1,string.len(word) do
local c = string.sub(msg,i,i)
local color = "white"
if not letters[c] then
color = "gray"
elseif c == string.sub(word,i,i) then
color = "green"
else color = "yellow"
end
if color ~= "green" then guessed = false end
out[#out+1] = minetest.colorize(color, c)
end
responses[#responses+1] = (#responses+1) .. ". " .. table.concat(out)
if guessed == true then
responses[#responses+1] = "YOU WIN!"
for i = 1,maxtries-#responses do responses[#responses+1] = " " end
end
self.label(table.concat(responses,"\n"))
end

40
scripts/math/calender.lua Normal file
View File

@ -0,0 +1,40 @@
-- calender by rnd, 30 minutes with bugfixes
dayofweek = function(y,m,d)
local offsets = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
if m<3 then y=y-1 end
return (y+math.floor(y/4)-math.floor(y/100)+math.floor(y/400)+offsets[m]+d) % 7+1
end
y=2000
m=8
d=1
--say(dayofweek(y,m,d))
make_calender = function(y,m)
local start_day = dayofweek(y,m,1)
local months = {31,29,31,30,31,30,31,31,30,31,30,31};
if y%4==0 and (y%100~=0 or y%400 == 0) then months[2]= 30 end -- feb has 30 days on leap year
local out = minetest.colorize("red",m.."/"..y).."\nsun mon tue wed thu fri sat\n"
out = out .. string.rep("__ ",start_day-1)
local i = start_day;
local idx = 1;
while idx<=months[m] do
out = out .. string.format("%02d",idx) .. " ";
if i%7 ==0 then out = out .."\n" end
idx = idx+1
i=i+1
end
return out
end
ret = {}
for m = 1,12 do
ret[#ret+1]=make_calender(y,m)
end
self.label(table.concat(ret,"\n\n"))

View File

@ -0,0 +1,59 @@
-- mandelbrot by rnd,2022 , made in 30 mins
if not init then init = true
n=100
local itermin = 10; -- check quicker if diverge!
local itermax = 250; -- max iterations
local delta = 0.00001^2; -- convergence difference
nodes = {
"white","yellow","orange","red",
"magenta","purple","blue","cyan",
"green","dark_green","brown","tan",
"light_grey","medium_grey","dark_grey","black"
}
for i = 1,#nodes do nodes[i] = "basic_robot:button"..nodes[i] end
get_pixel = function()
local iter = 0
local zr=0 ; local zi=0 ;
for i = 1, itermin do
local zrn=zr^2-zi^2+cr;
local zin=2*zr*zi+ci
zr=zrn
zi=zin
end
if zr^2+zi^2>1 then return -1 end
local zrn=zr^2-zi^2+cr;
local zin=2*zr*zi+ci
if (zrn-zr)^2+(zin-zi)^2< delta then return 1 end
for i = 1, itermax do
local zrn=zr^2-zi^2+cr;
local zin=2*zr*zi+ci
if (zrn-zr)^2+(zin-zi)^2< delta then return i-1 end
zr=zrn
zi=zin
end
return itermax-1
end
pos = self.pos()
local nnodes = #nodes
for x=1,n do
for y=1,n do
cr=2*x/n-1; ci=2*y/n-1
local col = get_pixel()
if col>0 then
col = math.floor(nnodes*col/itermax)
minetest.swap_node({x=pos.x+x,y=pos.y,z=pos.z+y},{name = nodes[col+1]})
end
end
end
self.remove()
end

View File

@ -0,0 +1,35 @@
--ALL UNICODE by rnd, 2021
-- range to 32-1550, then 7400-10000
--cyrillic 0410-042F: 1040-1071
--cyrilic 0430-044F: 1072-1103
if not init then init = true
function utf8(decimal)
local bytemarkers = { {0x7FF,192}, {0xFFFF,224}, {0x1FFFFF,240} }
if decimal<128 then return string.char(decimal) end
local charbytes = {}
for bytes,vals in ipairs(bytemarkers) do
if decimal<=vals[1] then
for b=bytes+1,2,-1 do
local mod = decimal%64
decimal = (decimal-mod)/64
charbytes[b] = string.char(128+mod)
end
charbytes[1] = string.char(vals[2]+decimal)
break
end
end
return table.concat(charbytes)
end
ret= {"ALL UNICODE\n0 = "}
for i = 1550, 4000 do
if i%25==0 then ret[#ret+1] = "\n"..(i).. " = " end
ret[#ret+1] = utf8(i)
end
self.label(table.concat(ret))
end

View File

@ -0,0 +1,27 @@
-- coal maker mod idea in 30 minutes by rnd
-- build dirt box around 3x3x3 area filled with wood, remove one boundary wood (lower one) and start fire there. start robot then!
check_firebox = function(pos)
local p = minetest.find_node_near(pos, 5, "fire:basic_flame") -- locate fire nearby!
if not p or minetest.get_node(p).name ~= "fire:basic_flame" then say("light fire first!"); self.remove() end
d=3; -- inner size of box, area filled with wood
local dirs = {{-1,0,0},{1,0,0},{0,0,-1},{0,0,1}};local dir1,dir2; -- position of vertices on dirt box
for i = 1,#dirs do
local dir = dirs[i];
if minetest.get_node({x=p.x+d*dir[1],y=p.y+d*dir[2],z=p.z+d*dir[3]}).name == "default:dirt" and
minetest.get_node({x=p.x+(d-1)*dir[1],y=p.y+(d-1)*dir[2],z=p.z+(d-1)*dir[3]}).name == "default:wood" then dir2 = dirs[i]; break end
end
if not dir2 then say("error, place fire in correct place in correctly built dirt box!") self.remove() end
dir1 = {dir2[3], dir2[2], -dir2[1]};
local v1 = {x=p.x-(d-1)*dir1[1]-dir2[1],y=p.y-1,z=p.z-(d-1)*dir1[3]-dir2[3]}
local v2 = {x=p.x+(d-1)*dir1[1]+(d)*dir2[1],y=p.y+d,z=p.z+(d-1)*dir1[3]+(d)*dir2[3]}
local res = minetest.find_nodes_in_area(v1,v2,{"default:wood","default:dirt"},true);
if (#(res["default:dirt"] or {})) == 97 and #(res["default:wood"] or {})==26 then
say("all ok. making charcoal now!")
minetest.swap_node(p,{name = "air"}) -- turn off fire!
else say("fail! check that you built dirt box/wood correctly!")
end
end
check_firebox(self.pos())
self.remove()

View File

@ -0,0 +1,50 @@
if not name then
name = "rnd"
player = minetest.get_player_by_name(name)
texs = {
"flowers_chrysanthemum_green.png",
"flowers_dandelion_white.png",
"flowers_dandelion_yellow.png",
"flowers_geranium.png",
"flowers_mushroom_brown.png",
"flowers_mushroom_red.png",
"flowers_rose.png",
"flowers_tulip.png",
"flowers_tulip_black.png",
"flowers_viola.png",
}
local ent = _G.basic_robot.data[self.name()].obj:get_luaentity();
ent.timestep = 0.25
_G.minetest.forceload_block(self.pos(),true)
add_part = function(pos)
local vdir = player:get_look_dir()
local speed = 8
vdir.x=vdir.x*speed
vdir.y=vdir.y*speed
vdir.z=vdir.z*speed
minetest.add_particle(
{
pos = {x=pos.x, y=pos.y+1.5, z=pos.z},
velocity = vdir, --{x=0, y=0, z=0},
acceleration = {x=0, y=-1, z=0},
expirationtime = 15,
size = 10,
collisiondetection = true,
collision_removal = false,
object_collision = false,
vertical = false,
texture = texs[math.random(#texs)],
glow = 0
})
end
p1 = player:getpos();
end
p2 = player:getpos()
local dist = ((p2.x-p1.x)^2+(p2.y-p1.y)^2+(p2.z-p1.z)^2)^0.5
if player:get_wielded_item():to_string()=="flowers:dandelion_yellow" then add_part(p2) end
--if dist>1 then p1 = player:getpos() add_part(p1) end

View File

@ -0,0 +1,121 @@
--rnd music robot v12/10/2019, 22:00
--tuning by Jozet
--@F2 E D C G3 G F2 E D C G3 G F2 A A F E G G E D E F D C3 C
if not init then
_G.minetest.forceload_block(self.pos(),true)
song = nil
self.listen(1)
local ent = _G.basic_robot.data[self.name()].obj:get_luaentity();
dt = 0.001
ent.timestep = dt -- time step
pitches = { -- definitions of note pitches
0.59,
0.62,
0.67,
0.7,
0.74,
0.79,
0.845,
0.89,
0.945,
1,
1.05,
1.12,
1.19,
1.25,
1.34,
1.41,
1.48,
1.58,
1.68,
1.78,
1.88,
2.0,
2.15,
2.28
}
notenames = { -- definition of note names
C = 1,
Db = 2,
D = 3,
Eb = 4,
E = 5,
F = 6,
Gb = 7,
G = 8,
Ab = 9,
A = 10,
Bb = 11,
B = 12,
["2C"] = 13,
["2Db"] = 14,
["2D"] = 15,
["2Eb"] = 16,
["2E"] = 17,
["2F"] = 18,
["2Gb"] = 19,
["2G"] = 20,
["2Ab"] = 21,
["2A"] = 22,
["2Bb"] = 23,
["2B"] = 24
}
say("available notes : C Db D Eb E F Gb G Ab A Bb B 2C 2Db 2D 2Eb 2E 2F 2Gb 2G 2Ab 2A 2Bb 2B . example: @A5 A A A A10 A A A , number after note name denotes change of tempo. To replay last song do @R")
songdata = {}
t=0 -- current timer
idx = 0 -- current note to play
tempo = 1 -- default pause
parse_song = function()
songdata = {} -- reset song data
for notepart in string.gmatch(song,"([^ ]+)") do -- parse song
if notepart then
local note,duration;
note,duration = _G.string.match(notepart,"(%d*%a+)(%d*)")
if not duration or duration == "" then duration = 0 end
songdata[#songdata+1] = {notenames[note], tonumber(duration)}
end
end
tempo = 3; -- default tempo
t=0
idx = 0 -- reset current idx
end
init = true
end
if not song then
speaker,msg = self.listen_msg()
if msg and string.find(msg,"@") then
song = string.sub(msg,2)
if song ~= "R" then -- R for replay
parse_song()
self.label("playing song by " .. speaker.. ", " .. song )
else
idx = 0; t = 0; -- try play again!
end
end
elseif t<=1 then -- play next note!
idx = idx+1
if idx>#songdata then
self.label("song " .. song .. ". ended.")
song = nil
else
if songdata[idx][2]>0 then tempo = songdata[idx][2] end
t = tempo;
self.sound( "piano",{
pos = self.pos(), gain = 1000.0, pitch = pitches[ songdata[idx][1] ],
max_hear_distance = 1000000
})
end
else
t=t-1
end

View File

@ -0,0 +1,108 @@
if not get_hash then
get_hash = function(s,p)
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
hashdb = {}; --array with entries: [hash] = list of possible hits
insert = function(key,value)
local hash = get_hash(key,10011);
local data = hashdb[hash]; if data == nil then hashdb[hash] = {}; data = hashdb[hash] end
data[#data+1]={key,value};
return hash
end
lookup = function(key)
local hash = get_hash(key,10011);
if not hash then return end
local data = hashdb[hash];
if not data then return nil end
for i = 1,#data do
if data[i][1]==key then return data[i][2] end
end
return nil
end
analyse = function()
local count = 0; local maxlen = 0; local maxidx = 1; local n = #hashdb;
for i = 1, 10000 do
local data = hashdb[i];
if data then
local length = #data;
if length > maxlen then maxlen = length; maxidx = i end
count = count + 1
end
end
if maxlen>0 then
local data = hashdb[maxidx];
say("number of used hash entries is " .. count .. ", average " .. (step/count) .. " entries per hash, "..
" max length of list is " .. maxlen .. " at hash ".. maxidx )--.. " : " ..
--string.gsub(_G.dump(data),"\n","") )
end
end
-- LOAD DICTIONARY WORDS into hashtable
lang = "german"
fname = "F:\\games\\rpg\\minetest-0415server\\mods\\basic_translate\\"..lang;
local f = _G.assert(_G.io.open(fname, "r"));local dicts = f:read("*all");f:close()
step = 0; maxwords = 10000;
i=0
dict = {}; -- for comparison
while(step<maxwords) do
step=step+1
i1 = string.find(dicts,"\t",i+1)
i2 = string.find(dicts,"\n",i+1)
if not i2 then break end
local word = string.sub(dicts, i+1,i1-1);
local tword = string.sub(dicts, i1+1,i2-1);
insert(word, tword) -- load into hashtable
dict[word]=tword
i=i2
end
self.listen(1)
self.spam(1)
say(step .. " words loaded")
end
-- handle chat event
speaker,msg = self.listen_msg()
if msg then
if msg == "?*" then
local n = 1000000; local msg = "hello"
local ret = "";
local t1 = os.clock();
for i = 1, n do ret = lookup(msg) end; t1 = os.clock()-t1;
local t2 = os.clock();
for i = 1, n do ret = dict[msg] end
t2 = os.clock()-t2;
say(t1 .. " " .. t2)
elseif msg == "??" then
analyse()
elseif string.sub(msg,1,1)=="?" then
msg = string.sub(msg,2);
local ret = lookup(msg);
if ret then say("found entry for " .. msg .. " : " .. ret) else say("entry not found") end
end
end

View File

@ -0,0 +1,187 @@
-- "natural language" programming demo by rnd, 2021
-- input is lines of 'natural' language, will be translated into lua code
--[[
move forward 3
turn left
dig left
if see dirt turn left and move forward
subroutine circle commands..
--> TRANSFORMED into lua :
for i = 1,3 do move.forward(); pause(); end
turn.left(); pause();
dig.left(); pause()
if read_node.forward()=="dirt" then turn.left();pause(); move.forward(); pause() end
TODO: integrate into robots, maybe command: code.natural(..)
--]]
if not init then init = true
prog = [[
subroutine walk
if see air move forward and move down
if see dirt move up
if see wood turn right
subroutine end
walk
]]
subroutines = {}; -- [subname] = true , so we know its subroutine
nodenames = -- translation of block names into minetest
{
["dirt"] = "default:dirt",
["cobble"] = "default:cobble",
["stone"] = "default:stone",
["wood"] = "default:wood",
["water"] = "default:water_source",
}
keywords = {
["quit"] = function() return "break;" end,
["move"] = {
["forward"] = function() return "if move.forward() then pause();paused=true; end;" end,
["backward"] = function() return "if move.backward() then pause();paused=true; end;" end,
["left"] = function() return "if move.left() then pause();paused=true; end;" end,
["right"] = function() return "if move.right() then pause();paused=true; end;" end,
["up"] = function() return "if move.up() then pause();paused=true; end;" end,
["down"] = function() return "if move.down() then pause();paused=true; end;" end,
},
["turn"] = {
["left"] = function() return "turn.left(); pause();paused=true;" end,
["right"] = function() return "turn.right(); pause();paused=true;" end,
["random"] = function() return "if math.random(2)==1 then turn.right() else turn.left() end; pause(); paused=true;" end
},
["dig"] = function() return "dig.forward(); pause();" end, --TODO: remember to set robot energy to large value at start
["place"] = function(line)
local pattern1 = "place";
local i = string.find(line,pattern1)+ string.len(pattern1)+1
local nodename = string.sub(line, i) -- what are we placing?
if nodenames[nodename] then nodename = nodenames[nodename] end -- translate name
return "place.forward('" .. nodename .. "'); pause();"
end,
["if"] = {
["see"] = function(line)
local pattern1 = "see";
local pattern2 = "and" .. " "; -- important, space after 'and'
local i = string.find(line,pattern1) + string.len(pattern1) + 1
--nodename command
local j = string.find(line," ",i)
local nodename, command
nodename = string.sub(line,i,j-1)
if nodenames[nodename] then nodename = nodenames[nodename] end -- translate name
-- maybe command has several parts separated by 'and' ?
--cmd1 and cmd2 and cmd3
j = j+1; local cmds = {}; local found = false
while true do
local k = string.find(line,pattern2,j+1)
if not k then-- no more AND
if found then
cmds[#cmds+1] = string.sub(line,j + string.len(pattern2)) break
else
cmds[#cmds+1] = string.sub(line,j) break
end
end
if found then
cmds[#cmds+1] = string.sub(line,j+string.len(pattern2),k-1)
else
cmds[#cmds+1] = string.sub(line,j,k-1)
end
found = true
j=k
end
for i = 1,#cmds do
cmds[i] = parse_line(cmds[i])
end
return "if read_node.forward()=='" ..nodename .. "' then " .. table.concat(cmds," ") .. " end"
end,
},
}
parse_line = function(line)
local struct = keywords;
for word in string.gmatch(line,"%S+") do
local matched = struct[word]
local issub = subroutines[word]
if matched or issub then
--say(word .. " = " .. type(matched))
if type(matched) == "table" then
struct = matched; -- climb deeper into structure
else
local instruction;
if issub then
instruction = word.."();"
else
instruction = matched(line)
end
-- do we have need to repeat instruction?
local i = string.find(line,word) + string.len(word) + 1
local snum = tonumber(string.sub(line,i)) or 1 -- repeating?
if snum>1 and snum<10 then
return "for i = 1,"..snum .." do " .. instruction .. " end;"
end
return instruction
end
else
say("error in line: " .. line .. ", unknown command " .. word) return ""
end
end
end
parse_prog = function(code)
local out = {};
local subdef = false; -- are we defining subroutine?
local subname;
local subcmds = {}
local pattern1 = "subroutine"
for line in string.gmatch(code,"[^\n]+") do -- line by line
local i = string.find(line,pattern1)
if i then -- do we define new subroutine command?
local j = i+string.len(pattern1)+1
local sname = string.sub(line,j)
if subdef and sname == "end" then -- end of subroutine
subdef = false
if not keywords[subname] then
out[#out+1 ] = "function " .. subname .. "()\n" .. table.concat(subcmds,"\n") .. "\nend"
subroutines[subname] = true;
else
-- error, subroutine name is reserved keyword
end
subcmds = {};
else
subdef = true -- all commands will now register with subroutine
subname = sname
end
else -- normal command
if subdef then
subcmds[#subcmds+1] = parse_line(line)
else
out[#out+1] = parse_line(line)
end
end
end
return "--coroutine NaturalLanguage autogenerated\nwhile true do paused = false; "..table.concat(out,"\n") .."if not paused then pause() end end"
end
parsed_prog = parse_prog(prog)
self.label(prog .. "\n\n==>\n\n" .. parsed_prog)
code.set(parsed_prog) -- actually run code by robot
end

View File

@ -0,0 +1,158 @@
-- Natural language compiler, outputs lua code
-- (C) rnd 2021
-- TODO: add IF: if condition arg1 ACTION1 else ACTION2 ?
-- ADD SUBROUTINE: sub NAME = enter subroutine definition mode, sub end = ends definition mode
if not init then init = true
dtext = {};
dout = function(text) dtext[#dtext+1] = text end
text =
[[
if cond1 value action1 and action2 and action3 else actiona1 and actiona2
]]
translate = { -- dictionary of used words
["forward"] = "forward",
["backward"] = "backward",
["left"] = "left",
["right"] = "right",
["random"] = "random",
["dirt"] = "default:dirt",
["wood"]= "default:wood",
["cobble"] = "default:cobble",
}
cmds = {
["if"] = function(code,ibeg,iend) -- if COND value ACTION1 and ACTION2 and ... ACTIONn else(optional) ACTION1 and ... and ACTIONm
-- COND: 'see' nodename (block right in front), 'var = value' value of variable?,...
local ELSE = " else"
local AND = " and"
local i,j,condtype,value;
condtype, j = get_next_word(code,ibeg,iend)
local out = {};
dout(condtype)
if condtype== "see" then
value, j = get_next_word(code,j,iend)
value = translate(value or "");
if not minetest.registered_nodename(value) then say("error: unknown block name " .. value .. "used in 'if'") return "" end
out[#out+1] = "if read_node.forward() == " .. value .. " then ";
else
say("error: unknown condition " .. condtype .. " used in 'if'") return "" end
end
-- now after j left: ACTION1 else(optional) ACTION2, ACTION can be multiple, separated by ' and '
-- parse and before else
local k = j;
lcoal cmds = {}
while k do
k = string.find(code,AND,j)
if k and k<ielse then
cmds[#cmds+1] = string.sub(code,j,k-1)
j=k+1;
else
break
end
end
dout(table.concat(cmds,"\n"))
--j=...
out[#out+1] = "end"
return table.concat(out," "),j
end,
["move"] = function(code,ibeg,iend)
-- move forward count
local i,direction,count
direction,i = get_next_word(code,ibeg,iend)
direction = translate[direction];
if not direction then say("error: unknown direction used in 'turn'") return "" end
return "move."..direction .."(); pause();",i;
end,
["turn"] = function(code,ibeg,iend)
-- move forward count
local i,direction,count
direction,i = get_next_word(code,ibeg,iend)
direction = translate[direction];
if not direction then say("error: unknown direction used in 'turn'") return "" end
local c;
if direction == "random" then
c = "if math.random(2) == 1 then turn.left() else turn.right() end;";
else
c = "turn."..direction .."();"
end
return c.." pause();",i
end,
["dig"] = function() return "dig.forward(); pause();" end,
["place"] = function(code,ibeg,iend)
-- place nodename
local nodename,i
nodename,i = get_next_word(code,ibeg,iend)
nodename = translate[nodename]
if not nodename then say("error: unknown nodename used in 'place'") return "" end
return "place.forward('" .. nodename .. "'); pause();",i
end,
}
-- given position ibeg in string find next word, return it and then return position immediately after word.
-- word is defined as a sequence of alphanumeric characters (%w)
-- example 'hello world', ibeg = 1. -> 'hello', 6
get_next_word = function(code, ibeg,iend) -- attempt to return next word, starting from position ibeg. returns word, index after word
if not ibeg or not iend then return end
local j = string.find(code,"%w",ibeg); -- where is start of word?
if not j or j>iend then return "", iend+1 end -- no words present
ibeg = j;
j = string.find(code,"%W",j);--where is end of word?
if not j or j>iend then return string.sub(code,ibeg,iend-1),iend+1 end
return string.sub(code,ibeg,j-1), j
end
parse_code = function(code)
local out = {};
local ibeg,iend,word;
local clen = string.len(code)
local step =0
iend = 1; ibeg = 1;
while step < 10 do
if ibeg>clen then break end
step = step+1
iend = string.find(code, "\n", ibeg)
if not iend then iend = clen end -- get out of loop, no more lines to process
word, ibeg = get_next_word(code,ibeg,iend)
--dout("rem " .. string.sub(code,ibeg,iend))
--dout("Dword '" .. word .. "' " .. ibeg .. " " .. iend)
local cmd = cmds[word];
if cmd then out[#out+1],ibeg = cmd(code,ibeg,iend) end
if not ibeg then ibeg = iend+1 end
if ibeg<=iend then -- still some space remaining in line, last parameter is repetition
local count,i
count,i = get_next_word(code,ibeg,iend);
count = tonumber(count) or 1
if count>9 then count = 9 elseif count<1 then count=1 end
if count > 1 then out[#out] = "for i=1,"..count.. " do " .. out[#out] .. " end" end
end
ibeg = iend +1 -- go new line
end
return table.concat(out,"\n")
end
self.label(parse_code(text) .. "\n\n" .. table.concat(dtext,"\n"))
end

View File

@ -0,0 +1,40 @@
-- given position ibeg in string find next word, return it and then return position immediately after word.
-- word is a sequence of alphanumeric characters
-- example 'hello world', ibeg = 1. -> 'hello', 6
get_next_word = function(code, ibeg,iend) -- attempt to return next word, starting from position ibeg. returns word, index after word
if not ibeg or not iend then return end
local j = string.find(code,"%w",ibeg); -- where is start of word?
if not j or j>iend then return "", iend+1 end -- no words present
ibeg = j;
j = string.find(code,"%W",j);--where is end of word?
if not j or j>iend then return string.sub(code,ibeg,iend-1),iend+1 end
return string.sub(code,ibeg,j-1), j
end
text = [[
hello world
today
day night
]]
ibeg = 1; iend = string.find(text,"\n",ibeg) or string.len(text) -- where is next new line
say("INIT LINE " .. ibeg .. " " .. iend .. " LINE '" .. string.sub(text,ibeg,iend-1) .."'")
for i = 1,10 do
word, ibeg = get_next_word(text,ibeg,iend)
say("word '" .. word.."', end " .. ibeg)
if ibeg>=iend then -- newline!
--say("newline")
local j = ibeg;
iend = string.find(text,"\n", iend+1) -- find next newline
if not iend then say("END") iend = string.len(text) break end -- end of text!
say("LINE " .. ibeg .. " " .. iend .. " LINE '" .. string.sub(text,ibeg,iend-1) .."'")
end
end
self.remove()

View File

@ -0,0 +1,83 @@
if not perm2cycles then
perm2cycles = function(perm)
local n = #perm;
local i = 1; -- already reached number
local ret = {};
local visited = {};
local step = 0;
while (true) do
local cycle = {i}
local j=i;
while (true) do
step = step +1
if step > 2*n then return {} end
j=perm[j];
visited[j] = 1;
if j and j~=cycle[1] then cycle[#cycle+1]=j else break end
end
i=n+1;
for k=1,n do -- find smallest non visited yet
if not visited[k] and k<i then i = k end
end
ret[#ret+1] = cycle;
if i == n+1 then return ret end
end
end
random_permute = function(arr)
local n = #arr;
for i = n,3,-1 do
local j = math.random(i);
local tmp = arr[j]; arr[j] = arr[i]; arr[i] = tmp;
end
end
get_permutations = function(n)
free = {}; --[1] = {stack of free for 1 : head, a1,..., a_head}
isfree = {}; -- [i]=1 if item i free, 0 if not
current = 1; --{1,..., current element, ... , n}
selection = {};
local data = free[1]; for i=1,n do data[i]=i isfree[i]=1 end data[0] = n;
--1. pick free spot from free stack ( if not possible backtrack ) and move on onto next element ( if not possible stay at this one)
-- backtrack: current--
local data = free[current];
if data[0]<1 then -- backtrack
isfree[selection[current]] = 1;
current = current - 1;
if current <=0 then return end -- exhausted all options
else
local i = data[data[0]]; -- free possibility
selection[current] = i; isfree[i] = 0;
data[0]=data[0]-1; -- pop the stack
--try move forward
if current<n then
current = current+1;
-- get new free spots for new current
local data = free[current]; data = {};
for i = 1,n do if isfree[i] == 1 then data[#data+1]=i; break end end;
data[0]=#data;
if data[0] == 0 then -- nothing free, backtrack
isfree[selection[current]] = 1;
current = current - 1;
end
end
end
end
arr2string = function(arr)
return string.gsub(_G.dump(arr),"\n","")
end
local arr = {}; for i =1,10 do arr[i] =i end; random_permute(arr);
local cycles = perm2cycles(arr);
say("random permutation = " .. arr2string(arr) .. " => cycles = " .. arr2string(cycles) )
end

View File

@ -0,0 +1,104 @@
if not integer_sort then
-- sorts input according to keys, changes ordering to permutation corresponding to new order
integer_sort = function(input, keys, ordering,temp_ordering, m) -- input, keys same length n, m = how many keys, O(2*n+m)
local n = #input;
local freq = {} -- frequencies of keys
local kpos = {} -- position of keys in sorted array
for i=1,n do -- count how many occurences - O(n)
local key = keys[ordering[i]]+1;
freq[key]=(freq[key] or 0)+1
temp_ordering[i]=ordering[i];
end
local curpos = 1;kpos[1]=1;
for i =1,m-1 do -- determine positions of keys in final sorted array - O(m)
curpos = curpos + (freq[i] or 0);
kpos[i+1]=curpos -- {2=3, 3 = 6,..., n-1 = 321}
end
-- actually place values here
for i = 1,n do -- O(n)
local key = keys[temp_ordering[i]]+1;
local pos = kpos[key];
ordering[pos] = temp_ordering[i];
kpos[key]=kpos[key]+1; -- move to next spot for that key place
end
end
permutate = function(input,ordering)
local output = {};
for i =1,#input do
output[i] = input[ordering[i]]
end
return output
end
get_digits = function(Num,d,base) -- number, how many digits, what base
local digits = {};
local num = Num;
local r;
for i = 1, d do
r = num % base;
digits[#digits+1] = r;
num = (num-r)/base
end
return digits
end
dumparr = function(array)
if _G.type(array) ~= "table" then return array end
local ret = "{";
for i =1,#array-1 do
ret = ret .. dumparr(array[i]) .. ","
end
ret = ret .. dumparr(array[#array])
return ret .. "}"
end
radix_sort = function(input,d,base) -- array of numbers; base is also number of keys = m, d = how many steps of sorting = number of digits for single number
out = out .."\nRADIX SORT\n\narray to be sorted " .. dumparr(input)
local n = #input;
local ordering = {}; local temp_ordering = {}; for i = 1, n do ordering[i]=i end
local keys = {};
local keylist = {};
for i = 1,n do
keylist[i] = get_digits(input[i],d,base)
end
out = out .."\nlist of keys - ".. d .. " digits of base " .. base .. " expansion of numbers : \n" ..
dumparr(keylist)
for step = 1, d do
for i =1,n do
keys[i] = keylist[i][step];
end
integer_sort(input, keys, ordering, temp_ordering, base)
out = out .."\n"..step .. ". pass integer_sort : " .. dumparr(permutate(input,ordering))
end
out = out .. "\nradix sort final result : " .. dumparr(permutate(input,ordering))
end
--input = {"a","b","c","d","e"}
--keys = {5,3,4,1,2}
--ordering = {}; temp_ordering = {}; for i = 1, #input do ordering[i]=i end
--m=5;
--integer_sort(input, keys, ordering, temp_ordering,m);
--say(string.gsub(_G.dump(ordering),"\n",""))
--say(string.gsub(_G.dump(permutate(input,ordering)),"\n",""))
out = ""; self.label("")
input = {23,42,15,8,87};
radix_sort(input,5,3) -- d, base
self.display_text(out,60,3)
end

View File

@ -0,0 +1,54 @@
-- room finder by rnd (45 minutes)
-- given starting position it explores 3d world to find enclosed area of room, up to max 2000 nodes
if not init then init = true
local rpos = self.pos()
local radius = 16
bpos = minetest.find_node_near(rpos, radius,"beds:bed_bottom");bpos.y=bpos.y+1 -- bed
--say(bpos.x .. " " .. bpos.y .. " " .. bpos.z)
walls = { -- define possible walls here
["default:cobble"]=true, ["default:wood"] = true,
["default:obsidian_glass"]=true,["default:glass"] = true,
["doors:door_obsidian_glass_a"]=true,["doors:door_obsidian_glass_b"]=true,
["doors:hidden"] = true,
}
local find_room = function(bpos)
local cbdata = {bpos} -- explore boundary
local cdata = {[minetest.hash_node_position(bpos)] = true} -- db of room pos
local dirs = {{x=-1,y=0,z=0},{x=1,y=0,z=0},{x=0,y=-1,z=0},{x=0,y=1,z=0},{x=0,y=0,z=-1},{x=0,y=0,z=1}}
local ccount = 1
crawl_step = function()
local pos = cbdata[#cbdata];cbdata[#cbdata] = nil;
for i = 1,#dirs do
local p = {x=pos.x+dirs[i].x,y=pos.y+dirs[i].y,z=pos.z+dirs[i].z}
if not cdata[minetest.hash_node_position(p)] and not walls[minetest.get_node(p).name] then
cdata[minetest.hash_node_position(p)] = true
cbdata[#cbdata+1] = p
ccount = ccount +1
end
end
end
local maxsteps = 2000;
local step = 0
while #cbdata>0 and step<maxsteps do
step=step+1; crawl_step()
end
if #cbdata == 0 then say("found room around bed " .. bpos.x .. " " .. bpos.y .. " " .. bpos.z.. ", room size " .. ccount) else say("no room found. try to fix holes in walls!") end
end
find_room(bpos)
end
self.remove()

File diff suppressed because it is too large Load Diff

View File

@ -1,46 +1,48 @@
--rnd 2017 --rnd 2017
if not logdata then if not logdata then
self.label("chatlog bot"); self.label("");--chatlog bot");
_G.minetest.forceload_block(self.pos(),true) _G.minetest.forceload_block(self.pos(),true)
n = 250; n = 500;
idx = 1; idx = 1;
logdata = {}; authusers = {["rnd"] = 1} -- who is allowed to use ?log
insert = function(text) -- insert new message logdata = {};
idx = idx +1;
if idx > n then idx = 1 end insert = function(text) -- insert new message
logdata[idx] = text; idx = idx +1;
end if idx > n then idx = 1 end
logdata[idx] = text;
last = function(k,filter) -- return last k messages end
if k > n then k = 30 end
local i,j,ret; last = function(k,filter) -- return last k messages
i=idx;j=0; ret = "" if k > n then k = 30 end
local i,j,ret;
for j = 1,k do i=idx;j=0; ret = ""
if not logdata[i] then break end
if filter and not string.find(logdata[i], filter) then for j = 1,k do
else if not logdata[i] then break end
ret = ret .. logdata[i] .. "\n"; if filter and not string.find(logdata[i], filter) then
end else
i=i-1; if i < 1 then i = n end ret = ret .. logdata[i] .. "\n";
end end
return ret i=i-1; if i < 1 then i = n end
end end
return ret
self.listen(1) end
end
self.listen(1)
speaker, msg = self.listen_msg() end
if msg then
if string.sub(msg,1,4) == "?log" then speaker, msg = self.listen_msg()
local j = string.find(msg," ",6); if msg then
local k = tonumber(string.sub(msg,6) or "") or n; if string.sub(msg,1,4) == "?log" and authusers[speaker] then
local text; local j = string.find(msg," ",6);
if j then text = last(k,string.sub(msg,j+1)) else text = last(k) end local k = tonumber(string.sub(msg,6) or "") or n;
local form = "size[8,8]".. "textarea[0.,0;11.,9.5;text;chatlog;".. text .. "]" local text;
self.show_form(speaker, form) if j then text = last(k,string.sub(msg,j+1)) else text = last(k) end
else local form = "size[8,8]".. "textarea[0.,0;11.,9.5;text;chatlog;".. minetest.formspec_escape(text) .. "]"
insert(os.date("%X") .. " " .. speaker .. "> " .. msg) self.show_form(speaker, form)
end else
end insert(os.date("%X") .. " " .. speaker .. "> " .. msg)
end
end

View File

@ -0,0 +1,204 @@
-- COPY PASTE ROBOT by rnd: c1 c2 r = markers, c = copy p = paste, rotz = rotate 90 deg cw z-axis, s = set
-- command parameters :
-- s nodename node_step
-- copy:
-- c1,c2 set markers, r set reference, c = copy,
-- p = paste at player position (reference is put there)
-- rotate:
-- c1,c2 set area, rotz = rotate 90 deg around z-axis
-- replace:
-- c1,c2 set area, then 'replace node1 node2'
if not paste then
_G.minetest.forceload_block(self.pos(),true)
paste = {};
round = function(x)
if x>0 then
return math.floor(x+0.5)
else
return -math.floor(-x+0.5)
end
end
data = {};
display_marker = function(pos,label)
minetest.add_particle(
{
pos = pos,
expirationtime = 10,
velocity = {x=0, y=0,z=0},
size = 9,
texture = label..".png",
acceleration = {x=0,y=0,z=0},
})
end
self.listen(1)
self.label("")
-- self.label("WorldEdit Bot\ncommands: c1 c2 r c p s rotz")
end
speaker, msg = self.listen_msg()
if speaker == "rnd" or speaker == "noah" then
local args = {}
for word in string.gmatch(msg,"%S+") do args[#args+1]=word end
local player = _G.minetest.get_player_by_name(speaker);
local p = player:getpos(); p.x = round(p.x); p.y=round(p.y); p.z = round(p.z);
if p.y<0 then p.y = p.y +1 end -- needed cause of minetest bug
if args[1] == "c1" then
paste.src1 = {x=p.x,y=p.y,z=p.z};say("marker 1 set at " .. p.x .. " " .. p.y .. " " .. p.z,speaker)
display_marker(p,"puzzle_button_off") -- c
elseif args[1] == "c2" then
paste.src2 = {x=p.x,y=p.y,z=p.z};say("marker 2 set at " .. p.x .. " " .. p.y .. " " .. p.z,speaker)
display_marker(p,"puzzle_button_on") -- c
elseif args[1] == "r" then
paste.ref = {x=p.x,y=p.y,z=p.z};say("reference set at " .. p.x .. " " .. p.y .. " " .. p.z,speaker)
display_marker(p,"puzzle_diode") -- r
elseif args[1] == "c" then -- copy
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
local count = 0; data = {};
for i = x1,x2 do
for j = y1,y2 do
for k = z1,z2 do
local node = _G.minetest.get_node({x=i,y=j,z=k});
if node.name ~= "air" then
if not data[i] then data[i]= {} end
if not data[i][j] then data[i][j]= {} end
data[i][j][k] = {node, _G.minetest.get_meta({x=i,y=j,z=k}):to_table()}
count = count +1;
end
end
end
end
say(count .. " nodes copied ");
elseif args[1] == "p" then -- paste
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
local count = 0; p.x = p.x-paste.ref.x; p.y = p.y-paste.ref.y; p.z = p.z-paste.ref.z;
for i = x1,x2 do
for j = y1,y2 do
for k = z1,z2 do
local pdata;
if data[i] and data[i][j] and data[i][j][k] then
pdata = data[i][j][k]
end
if pdata then
count = count + 1
_G.minetest.set_node({x=i+p.x,y=j+p.y,z=k+p.z}, pdata[1]);
_G.minetest.get_meta({x=i+p.x,y=j+p.y,z=k+p.z}):from_table(pdata[2])
end
end
end
end
say(count .. " nodes pasted ",speaker);
elseif args[1] == "s" then -- set node
local nodename = args[2] or "air"
local randomized = (nodename=="random")
local btncolors = {
"white","yellow","orange","red","magenta","purple","blue","cyan",
"green","dark_green","brown","tan","light_grey","medium_grey","dark_grey","black"
}
for i = 1,#btncolors do btncolors[i] = "basic_robot:button"..btncolors[i] end
if not minetest.registered_nodes[nodename] and not randomized then return end
local step = args[3] or 1;
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
local count = 0;
for i = x1,x2,step do
for j = y1,y2,step do
for k = z1,z2,step do
if randomized then nodename = btncolors[math.random(16)] end
minetest.set_node({x=i,y=j,z=k}, {name = nodename});
end
end
end
say((x2-x1+1)*(y2-y1+1)*(z2-z1+1) .. " nodes set to " .. nodename,speaker)
elseif args[1] == "replace" then -- replace
local node1 = args[2]
local node2 = args[3]
local count = 0
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
for i = x1,x2 do
for j = y1,y2 do
for k = z1,z2 do
local node = _G.minetest.get_node({x=i,y=j,z=k});
if node.name == node1 then
_G.minetest.swap_node({x=i,y=j,z=k},{name = node2})
count = count +1;
end
end
end
end
say(count .. " nodes replaced ");
elseif args[1] == "rotz" then -- rotate around z axis, center of selection
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
local d = x2-x1; if z2-z1<d then z2 = z1+d else d = z2-z1; x2 = x1+d end -- will be rotated as square in xz
local rotzd = {
[0]=1,[1]=2,[2]=3,[3]=0,
[7]=12,[12]=9,[9]=18,[18]=7,
[8]=17,[17]=6,[6]=15,[15]=8,
[19]=4,[4]=13,[13]=10,[10]=19,
[20]=23,[23]=22,[22]=21,[21]=20,
}
local count = 0; local step = 1
local data = {}; -- copy first
for i = x1,x2 do
for j = y1,y2 do
for k = z1,z2 do
local node = _G.minetest.get_node({x=i,y=j,z=k});
minetest.swap_node({x=i,y=j,z=k},{name = "air"})
if node.name ~= "air" then
if not data[i] then data[i]= {} end
if not data[i][j] then data[i][j]= {} end
data[i][j][k] = node
count = count +1;
end
end
end
end
-- (x,z)->(z,-x)
-- square rotate around center: x,z -> x-dx/2, z-dz/2 ->z-dz/2,dx/2-x -> (z, dx-x)
-- (x,z) -> (z,x1+x2-x)
--[[
x1,z1
*
* x1,z1
add offset to put middle of square into 0,0 and then back..
x->x-x1-dx/2, z-z1-dz/2 -> z-z1-dz/2, x1-dx/2-x ->
z-z1-dz/2+x1+dx/2,x1-dx/2-x+z1+dz/2 =
z-z1+x1,z1+x1-x
--]]
for i = x1,x2,step do
for j = y1,y2,step do
for k = z1,z2,step do
local pdata;
if data[i] and data[i][j] and data[i][j][k] then
pdata = data[i][j][k]
end
if pdata then -- correct position, rotated 90deg, TODO!
local node = pdata
node.param2 = rotzd[node.param2] or 0;
minetest.swap_node({x=k+x1-z1,y=j,z=x1+z1-i}, node)
end
end
end
end
say(count .. " nodes rotated around z-axis");
end
end

View File

@ -1,16 +1,40 @@
if not init then if not init then
self.set_properties({
visual = "mesh", mesh = "character.b3d",
textures = {"character_45.png"},
visual_size = {x = 5, y = 5}
});
move.down()
animation = {
-- Standard animations.
stand = { x= 0, y= 79, },
lay = { x=162, y=166, },
walk = { x=168, y=187, },
mine = { x=189, y=198, },
walk_mine = { x=200, y=219, },
sit = { x= 81, y=160, },
}
bstate = "walk_mine"
bspeed = 5--15
self.set_animation(animation[bstate].x,animation[bstate].y, bspeed, 0)
init = true; self.listen(1); init = true; self.listen(1);
self.spam(1); self.label("help bot")
_G.minetest.forceload_block(self.pos(),true)
self.spam(1); self.label("") --help bot")
talk = function(msg) minetest.chat_send_all("<help bot> " .. msg) end talk = function(msg) minetest.chat_send_all("<help bot> " .. msg) end
keywords = { keywords = {
{"tp", 14}, {"tpr", 14},
{"tpy",19},
{"help", {"help",
{"robot",6},{"",1} {"robot",6},{"",1}
}, },
{"how", {"how",
{"play",1},{"robot", 6},{"stone",4},{"tree",3},{"wood",3},{"lava",5},{"cobble",4},{"dirt",10}, {"play",1},{"island",17},{"robot", 6},{"stone",4},{"tree",3},{"wood",3},{"lava",5},{"mossy",16},{"cobble",4},{"dirt",10},{"clay",23},
{"do i get",1},{"do i make",1}, {"to get",1} {"do i get",1},{"do i make",1}, {"to get",1},{"farm",15},
}, },
{"i need", {"i need",
{"wood",3} {"wood",3}
@ -19,40 +43,77 @@ if not init then
{"hello",2}, -- words matched must appear at beginning {"hello",2}, -- words matched must appear at beginning
{"hi",2}, {"hi",2},
{"back",7}, {"back",7},
{" hard",{"",9}}, -- word matched can appear anywhere {" 'hard",{"",9}}, -- word matched can appear anywhere
{" died", {"",9}}, {" died", {"",9}},
{" die",{"",8}}, {" dead",{"",8}}, {" die",{"",8}}, {" dead",{"",8}},
{"rnd",{"",11}}, -- {"rnd",{"",11}},
{"bye",{"",12}}, {"^bye",{"",12}},
{"!!",{"",9}}, {"!!",{"",9}},
{"calc", 13}, {"calc", 13},
{"day",18},
{"RESET_LEVEL",20},
{"YES",21},
} }
answers = { answers = {
"%s open inventory, click 'Quests' and do them to get more stuff", --1 "%s open inventory, click 'Quests' and do them to get more stuff", --1
"hello %s", "hello %s",
"do the dirt quest to get sticks then do sapling quest", "you can get wood from bush stems or trees. tree sapling needs to be planted on fertilized composter with 10 N nutrient",
"get pumice from lava and water. then search craft guide how to make cobble", "get pumice from lava and water. then search craft guide how to make cobble",
"you get lava as compost quest reward or with grinder", -- 5 "you get lava as dig tree quest reward. warning - put lava away from flammable blocks. full composter does not burn - its safe.", -- 5
"you have to write a program so that robot knows what to do. for list of commands click 'help' button inside robot.", "you have to write a program so that robot knows what to do. for list of commands click 'help' button inside robot.",
"wb %s", "wb %s",
"dont die, you lose your stuff and it will reset your level on level 1", "dont die, you lose your stuff and it will reset your level on level 1",
"you suck %s!", -- 9 "you suck %s!", -- 9
"to get dirt craft composter and use it with leaves", -- 10 "to get dirt craft composter, put gravel on it, punch it and wait until its ready. then punch again to get dirt.", -- 10
"rnd is afk. in the meantime i can answer your questions", "rnd is afk. in the meantime i can answer your questions",
"bye %s", "bye %s",
function(speaker,msg) -- 13, calc function(speaker,msg) -- 13, calc
local expr = string.sub(msg,5); if string.find(expr,"%a") then return end local expr = string.sub(msg,5);
local res = _G.loadstring("return " .. expr)(); say(expr .. " = " .. res) if string.find(expr,"%a") then return end;
if string.find(expr,"{") then return end;
local exprfunc = _G.loadstring("return " .. expr);
local res = exprfunc and exprfunc() or say("error in expression: " .. expr);
if type(res) == "number" then say(expr .. " = " .. res) end
end, end,
function(speaker,msg) -- 14,tp function(speaker,msg) -- 14,tpr
local p1 = minetest.get_player_by_name(speaker); tpr[1] = speaker
local p2 = minetest.get_player_by_name(string.sub(msg,4)); tpr[2] = string.sub(msg,5) or "";
minetest.chat_send_player(tpr[2], "#TPR: " .. speaker .. " wants to teleport to you. say \\tpy")
end,
"to make farm craft composter, put leaves on it,punch and wait until its ready. Then right click composter to see if it has enough nutrients. If yes, plant your crops on top.", -- 15
"put cobble near water and wait.", --16
"you get your own island when you finish all quests on level 1. before that your island will be reused by other players.", --17
function(speaker,msg) -- 18, day
minetest.set_timeofday(0.25); say("time set to day")
end,
function(speaker,msg) -- 19,tpy
if tpr[2] ~= speaker then return end;
local p1 = minetest.get_player_by_name(tpr[1]);
local p2 = minetest.get_player_by_name(tpr[2]);tpr[2] = ""
if p1 and p2 then if p1 and p2 then
p1:setpos(p2:getpos()) p1:setpos(p2:getpos())
end end
end, end,
function(speaker,msg) -- 20,RESET_LEVEL
say("this will reset your skyblock level to beginning of level 1. are you sure " .. speaker .. "? say YES")
qa[1] = speaker; qa[2] = 22
end,
function(speaker,msg) -- 21,yes to some question
if qa[1] ~= speaker then return end
answers[qa[2]](speaker,msg)
qa[1] = ""
end,
function(speaker,msg) -- 22 reset level
say("your level is reset " .. speaker .. ". have a nice day.")
end,
"you get clay by grinding dirt in grinder ( basic_machines ). Either craft grinder from constructor or get as level 3 1st quest reward" -- 23
} }
tpr = {"",""} -- used for teleport
qa = {"",1}; -- speaker
end end
speaker,msg = self.listen_msg(); speaker,msg = self.listen_msg();
@ -71,7 +132,6 @@ if msg then
end end
end end
end end
end end
end end

View File

@ -0,0 +1,300 @@
--[[
help bot 2
'how to make/craft X' = 'how' 'make' 'x'
pattern match:
word1 word2 ... wordn
word1 - start of pattern
patterns = {
["pattern_start"] = {
{ -- list of possible sequences
{{"pattern2", ..},{"pattern3", ..}, ..},"response"} -- { patternsequence, "response"}
},
...
}
--]]
if not init then init = true
_G.minetest.forceload_block(self.pos(),true)
patterns = {
["calc"] = { {{}, "calc"} },
["day"] = { {{}, "day"} },
["day4ever"] = { {{}, "day4ever"} },
["crazy_mode"] = { {{}, "crazy_mode"} },
["normal_mode"] = { {{}, "normal_mode"} },
["hi"] = { {{}, "greeting"} },
["hey"] = { {{}, "greeting"} },
["hello"] = { {{}, "greeting"} },
["bye"] = { {{}, "goodbye"} },
["help"] = {
{
{{"play"}}, "play_help"
},
{
{}, "help_general"
},
},
["tell"] = {
{
{{"bot"}}, "tell_bot"
},
},
["ask"] = {
{
{{"bot"}}, "ask_bot"
},
},
["how"] = {
{
{{"plant"},{"tree"}}, "plant_tree_help"
},
{
{{"play"}}, "play_help"
},
{
{{"get"},{"island"}}, "getting_island_help"
},
{
{{"get"},{"tree"}}, "plant_tree_help"
},
{
{{"get"},{"wood"}}, "getting_wood_help"
},
{
{{"get"},{"clay"}}, "getting_clay_help"
},
{
{{"get"},{"stone"}}, "getting_stone_help"
},
{
{{"get","make"},{"dirt"}}, "getting_dirt_help"
},
{
{{"get","find"}}, "getting_stuff_help"
},
{
{{"craft"}}, "craft_help"
},
{
{{"farm"}}, "farm_help"
},
{
{{"robot"}}, "robot_help"
},
{
{{"craft","make"}}, "craft_help"
},
},
["tpr"] = { {{}, "tpr"} },
["tpy"] = { {{}, "tpy"} },
}
bot_knowledge = {}; -- bot knows stuff players tell it
chat_data = {} -- various player data
--[[
[name] = {greet = true} -- already said hi
tpr = requester -- someone want to teleport to 'name'
--]]
responses = {
["greeting"] = function(name,imatch, iendmatch, words)
if imatch>2 then return end
if not chat_data[name] then chat_data[name] = {greet = true} elseif chat_data[name].greet then return end
chat_data[name].greet = true -- remember we said hi
local ipdata = _G.helloip.players[name]; local country = 'en'
if ipdata then country = ipdata.country end
local greetings = {
["ZZ"] = "hi ",
["EN"] = "hello and welcome ",
["DE"] = "hallo und willkommen ",
["FR"] = "bonjour et bienvenue ",
["PL"] = "czesc i witaj ",
["RU"] = "privet i dobro pozhalovat' ",
["NL"] = "hallo en welkom ",
}
talk((greetings[country] or greetings["EN"]) .. name )
end,
["goodbye"] = function(name,imatch,iendmatch, words)
if imatch>2 then return end
talk("see you later " .. name)
end,
["farm_help"] = function(name)
_G.basic_robot.gui["farming_help"].show(name)
end,
["robot_help"] = function(name)
_G.basic_robot.gui["robot_help"].show(name)
end,
["craft_help"] = function(name)
local text = "Build 3x3 normal wood table on the ground. Drop items in shape of crafting recipe on the table. Then use craft tool on table to craft item.\n\nIt is important to be looking in direction toward top of recipe.\n\nYou can craft more than 1 item - try dropping 5 of each items in recipe to craft 5 items ...\n\nUsing craft tool on bush will give you wood."
local form = "size[8,4.5] textarea[0,0.25;9,5.5;msg;CRAFT HELP;"..text .."]"
minetest.show_formspec(name, "basic_craft_help_text",form)
end,
["plant_tree_help"] = function(name) talk(name .. " you need to plant tree on composter. insert 10 leaves in composter first by putting leaves on top and punching composter. repeat this 10 times.") end,
["play_help"] = function(name) talk(name .. " open inventory and read crafting and farming help. Look at quests too and do them to progress. You can make island larger with leaves.") end,
["getting_island_help"] = function(name) talk(name .. " you need to complete level 1 to get your own island. for now your island only temporary.") end,
["help_general"] = function(name) talk("what you need help with " .. name .. " ?") end,
["getting_clay_help"] = function(name) talk("you get clay by grinding dirt in grinder ( basic_machines ). Either craft grinder from constructor or get it as level 3 1st quest reward") end,
["getting_wood_help"] = function(name) talk("you can get wood from bush stems or trees - use craft tool on bush stem.") end,
["getting_stone_help"] = function(name) talk("get pumice from lava and water. then search craft guide how to make cobble") end,
["getting_dirt_help"] = function(name) talk("place gravel on composter and punch composter.when composting done punch again to get out dirt.") end,
["tpr"] = function(name,imatch, iendmatch, words)
local target = words[2]; if not target then return end
if not minetest.get_player_by_name(target) then return end
local tdata = chat_data[target];
if not tdata then chat_data[target] = {}; tdata = chat_data[target] end
tdata.tpr = name
talk(name .. " wants to teleport to you - say tpy", target)
end,
["tpy"] = function(name,imatch, iendmatch, words)
local data = chat_data[name];
if not data then chat_data[name] = {}; data = chat_data[name] end
local requester = data.tpr; if not requester then return end
local rpl = minetest.get_player_by_name(requester)
if not rpl then return end
rpl:set_pos( minetest.get_player_by_name(name):get_pos() )
data.tpr = nil
end,
["calc"] = function(name,imatch,__,words) -- calculator
if imatch~=1 then return end
local expr = string.sub(table.concat(words," "),5)
if string.find(expr,"%a") then return end;
if string.find(expr,"{") then return end;
local exprfunc = _G.loadstring("return " .. expr);
local res = exprfunc and exprfunc() or say("error in expression: " .. expr);
if type(res) == "number" then talk(expr .. " = " .. res) end
end,
["ask_bot"] = function(name,imatch, iendmatch, words)
if imatch>1 then return end
local expr = string.sub(table.concat(words," "),9)
--i = string.find(expr," ")
--if i then expr = string.sub(expr,1,i-1) end
local answer = bot_knowledge[expr];
if not answer then
talk("i don't know about " .. expr)
else
talk(expr .. " " .. answer)
end
end,
["tell_bot"] = function(name,imatch, iendmatch, words)
if imatch>1 then return end
local expr = string.sub(table.concat(words," "),10)
local i = string.find(expr, " ")
if not i then
talk("what did you want to tell me about " .. expr .. " ?")
else
local dwords = {" is ", " are "}
local j;
for k=1,#dwords do j = string.find(expr,dwords[k]);if j then break end end
local topic, value
if j then
topic = string.sub(expr,1,j-1) value = string.sub(expr,j)
else
topic = string.sub(expr,1,i-1) value = string.sub(expr,i+1)
end
bot_knowledge[topic] = value
talk("i will remember what you told me about " .. topic, name)
end
end,
["day"] = function()
minetest.set_timeofday(0.25); talk("time set to day")
end,
["day4ever"] = function()
minetest.set_timeofday(0.25); talk("forever day")
minetest.settings:set("time_speed",0);
end,
["crazy_mode"] = function()
talk("crazy mode ON")
minetest.settings:set("time_speed",500000);
end,
["normal_mode"] = function()
talk("crazy mode OFF")
minetest.settings:set("time_speed",72);
end
}
talk = function(text,name)
if not name then
minetest.chat_send_all("<help bot> " .. text)
else
minetest.chat_send_player(name,"<help bot> " .. text)
end
end
check_msg = function(text)
local level = 0;
local pattern;
local words = {}
for word in string.gmatch(text,"[^%s,?:]+") do words[#words+1] = word end
local imatch
for i = 1,#words do
if patterns[words[i]] then pattern = patterns[words[i]] imatch = i end -- perhaps this will match?
end
if not imatch then return end
--say("possible match: " .. words[imatch])
-- check out all pattern possibilities
local jmatch
local iendmatch = imatch
for j = 1, #pattern do
if jmatch then break end -- already got it
local level = 1
local pat = pattern[j]
--say("pattern " .. j .. " length :" .. #pat[1]+1)
if #pat[1] == 0 then -- empty pattern, we have match
jmatch = j; break
end
for i = imatch+1, #words do -- only search from next word
if jmatch then break end
for k = 1, #pat[1][level] do
if words[i] == pat[1][level][k] then
level = level +1;
if #pat[1]+1 == level then jmatch = j iendmatch = i end
break
end
end
end
end
if jmatch then
local responseid = pattern[jmatch][2]
--say("match: " .. words[imatch] .. ", response " .. responseid)
return responses[responseid],imatch,iendmatch, words
end
end
self.listen(1);
--self.label("help bot - ask me questions")
self.label("")
end
speaker,msg = self.listen_msg()
if msg then
local response, imatch, iendmatch, words
response, imatch,iendmatch, words = check_msg(msg)
if response then response(speaker, imatch, iendmatch, words) end
end

View File

@ -1,32 +1,45 @@
-- return minetest object count for 5x5x5 blocks -- minetest object listen in radius 100 around robot
if not init then init = true if not init then init = false
local objs = minetest.get_objects_inside_radius(self.pos(), 30000); local objs = minetest.get_objects_inside_radius(self.pos(), 100);
local ret = {}; local ret = {};
local round = function(x) return math.floor(x/5)*5 end local round = function(x) return math.floor(x/5)*5 end
local ret = {}; local ret = {};
for i = 1, #objs do for i = 1, #objs do
local p = objs[i]:get_pos(); local p = objs[i]:get_pos();
local phash = round(p.x) .. " " .. round(p.y) .. " " .. round(p.z); local luaent = objs[i]:get_luaentity();
ret[phash] = (ret[phash] or 0) + 1 local entname = ""
end if luaent then
--entname = serialize(luaent)
local out = {}; entname = luaent.itemstring
for k,v in pairs(ret) do if entname == "robot" then entname = entname .. " " .. luaent.name end
out[#out+1] = {k,v} elseif objs[i]:is_player() then
end entname = "PLAYER " .. objs[i]:get_player_name()
end
table.sort(out, function(a,b) return a[2]>b[2] end)
local res = {}; local phash = round(p.x) .. " " .. round(p.y) .. " " .. round(p.z);
for i = 1, #out do if entname then ret[phash] = (ret[phash] or "") .. entname .. ", " end
res[#res+1] = out[i][1] .. "=" .. out[i][2] end
end
local out = {};
self.label("#objects " .. #objs .. "\n" .. table.concat(res, "\n")) for k,v in pairs(ret) do
out[#out+1] = {k,v}
end
--table.sort(out, function(a,b) return a[2]>b[2] end) -- additional stuff here - optional
local res = {};
for i = 1, #out do
res[#res+1] = out[i][1] .. " = " .. out[i][2]
end
self.label("#objects " .. #objs .. "\n" .. table.concat(res, "\n"))
--book.write(1,"",("#objects " .. #objs .. "\n" .. table.concat(res, "\n")))
--self.remove()
end end

View File

@ -0,0 +1,43 @@
-- text printer by rnd
-- instruction: go to position where text starts and look in desired direction
-- say: t TEXT\nTEXT...
if not init then init = true
names = {["rnd"]=true,["PrairieWind"] = true}
get_dir = function(view)
local dir
if math.abs(view.x)>math.abs(view.z) then
if view.x>0 then dir = {1,0} else dir = {-1,0} end
else
if view.z>0 then dir = {0,1} else dir = {0,-1} end
end
return dir
end
render_text = function(text,name)
local player = minetest.get_player_by_name(name)
local pos = player:get_pos()
local dir = get_dir(player:get_look_dir())
local i=0;
local x=0;local y=0
while i<string.len(text) do
i=i+1
local c = string.sub(text,i,i)
if c == "\\" and string.sub(text,i+1,i+1) == "n" then
x=0;y=y-1;i=i+2;c = string.sub(text,i,i)
end
cb = (string.byte(c) or 32)-97
minetest.set_node({x=pos.x+dir[1]*x,y=pos.y+y,z=pos.z+dir[2]*x},{name = "basic_robot:button_"..(97+cb)})
x=x+1
end
end
self.listen(1)
end
speaker,msg = self.listen_msg()
if names[speaker] and string.sub(msg,1,2) == "t " then
render_text(string.sub(msg,3),speaker)
end

View File

@ -0,0 +1,74 @@
--text formatting to specified width ( real font width appearance )
if not init then init = true
width = 13; -- desired width of formatted text
text = "bereits riskante situationen werden durch die britische variante noch riskanter"
dout = function(text) say(text,"_") end
cwidths = { -- how many chars fit into string lenght of 46 repeated a's
["a"] = 46, ["b"] = 46,["c"] = 57,["d"] = 46,["e"] = 46,
["f"] = 103,["g"] = 46,["h"] = 46,["i"] = 103,["j"] = 103,
["k"] = 52,["l"] = 103,["m"] = 32,["n"] = 46,["o"] = 46,
["p"] = 46,["q"] = 46,["r"] = 83,["s"] = 52,["t"] = 103,
["u"] = 46, ["v"] = 52,["w"] = 34.5,["x"] = 52,["y"] = 52,
["z"] = 52,
[" "] = 103,["."] = 103, [","] = 103,[":"]=103,[";"]=103,
["?"] = 46,
["0"] = 52,["1"] = 52,["2"] = 46,["3"] = 46,["4"] = 46,
["5"] = 46,["6"] = 46,["7"] = 46,["8"] = 46,["9"] = 46,
}
local ac = cwidths["a"];
for k,v in pairs(cwidths) do cwidths[k] = ac/v end -- compute lenghts in widths of 'a'
format_text = function(text,width,cwidths)
local ret = {};
local x = 0;
for word in string.gmatch(text,"%S+") do
local xw = x; -- remember where we were before word
local newline = false;
for i = 1, string.len(word) do
local c = string.sub(word,i,i)
local w = cwidths[c] or 1
if c~="\n" then
x=x+w;
if x>width then newline = true end
else
ret[#ret+1]=c;x=0; -- adding new line character
end
end
if newline then -- add word and space between words if not in newline
ret[#ret+1] = "\n"..word.. " ";
x=x+cwidths[" "]
x=x-xw; -- word width in new line
else
x=x+cwidths[" "]
ret[#ret+1] = word.." "
end
end
return table.concat(ret,"")
end
display = function(width)
local res = format_text(text, width,cwidths)
self.label(res)
end
display(width)
--self.show_form("_",
--"size[6,6] label[0,0;"..
--res .. "]" )
--self.remove()
self.listen(1)
end
speaker,msg = self.listen_msg()
if speaker == "_" then
display( tonumber(msg) or width)
end

View File

@ -0,0 +1,22 @@
from http.server import HTTPServer,SimpleHTTPRequestHandler
server_address = ('0.0.0.0', 80) # address 0.0.0.0 makes it listen to all requests from anywhere
class HTTPRequestHandler(SimpleHTTPRequestHandler):
def do_POST(self):
content_length = int(self.headers['Content-Length'])
body = self.rfile.read(content_length)
print(body.decode('utf-8')) #log display
self.send_response(200)
self.end_headers()
def do_GET(self):
print("D " + self.path)
SimpleHTTPRequestHandler.do_GET(self) #process using default do_GET
httpd = HTTPServer(server_address, HTTPRequestHandler)
httpd.serve_forever()

16
scripts/web/index.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<body>
<h2>rnd's web robot</h2>
<form action="" method = "">
<label for="cmd">command:</label><br>
<input type="text" id="cmd" name="cmd" value=""><br>
<input type="submit" value="Submit">
</form>
</body>
</html>

62
scripts/web/irc bot.lua Normal file
View File

@ -0,0 +1,62 @@
-- irc_bot, 05/02/2022 by rnd
-- adds irc bot commands with password login
if not init then init = true
_G.basic_robot.ircbot = {}; ircbot = _G.basic_robot.ircbot
ircbot.user_list = {} -- [user] = true, must login first
ircbot.auth_data = { -- [user] = {pass_hash,level,robot_name}
["r_n_d"] = {"5yfRRkrhJDbomacm2lsvEdg4GyY",3,"rnd1"},
["Noah"] = {"5yfRRkrhJDbomacm2lsvEdg4GyY",3,"noah1"}
}; --
robotname = self.name()
_G.irc.register_bot_command("c", {
params = "",
description = "",
func = function(usr,msg)
-- user not logged in yet?
local lvl = ircbot.user_list[usr.nick]
if not lvl then
if msg == "" then return false,"basic_robot: login first using: c $password" end
if not ircbot.auth_data[usr.nick] then return false, "basic_robot: you are not in user database. please contact server admin." end
if ircbot.auth_data[usr.nick][1] == minetest.get_password_hash("", msg) then
ircbot.user_list[usr.nick] = ircbot.auth_data[usr.nick][2]
local msg = "basic_robot: Logged in as " .. usr.nick ..", level " .. ircbot.user_list[usr.nick]
lvl = ircbot.auth_data[usr.nick][2]
local robotname = ircbot.auth_data[usr.nick][3]
if lvl>=3 then msg = msg .. ". you can use 'c !lua_cmd' to run lua_cmd in robot " .. robotname .. " sandbox" end
return false, msg
else
return false,"basic_robot: wrong password!"
end
end
-- action here : DEMO just displays message once logged in
local c = string.sub(msg,1,1)
if c~="!" or lvl<3 then _G.basic_robot.ircmsg = msg return end
local ScriptFunc, CompileError = _G.loadstring( string.sub(msg,2))
if CompileError then return false, CompileError end
local robotname = ircbot.auth_data[usr.nick][3]
_G.setfenv( ScriptFunc, _G.basic_robot.data[robotname].sandbox ) -- run code in robot sandbox
local Result, RuntimeError = _G.pcall( ScriptFunc );
if result then return false, _G.tostring(Result) end
if RuntimeError then return false,RuntimeError end
end
})
-- how to send msg to irc user
ircchat = minetest.registered_chatcommands["irc_msg"].func;
name = "r_n_d" -- client on irc you want to send msg too
-- ircchat("ROBOT", name .." " .. "hello irc world") -- chat will appear as coming from <ROBOT> on skyblock
end
self.label(_G.basic_robot.ircmsg or "")

View File

@ -0,0 +1 @@
D:\prog\programming\Python\Python37-32\python https_server.py

24
settings.lua Normal file
View File

@ -0,0 +1,24 @@
-- SETTINGS FOR BASIC_ROBOT
local b = basic_robot;
b.call_limit = {50,200,1500,10^9}; -- how many execution calls per script run allowed, for auth levels 0,1,2 (normal, robot, puzzle, admin)
b.count = {2,6,16,128} -- how many robots player can have
b.radius = 32; -- divide whole world into blocks of this size - used for managing events like keyboard punches
b.password = "raN___dOM_ p4S"; -- IMPORTANT: change it before running mod, password used for authentifications
b.admin_bot_pos = {x=0,y=1,z=0} -- position of admin robot spawner that will be run automatically on server start
b.maxoperations = 10; -- how many operations (dig, place,move,...,generate energy,..) available per run, 0 = unlimited
b.dig_require_energy = true; -- does robot require energy to dig?
b.bad_inventory_blocks = { -- disallow taking from these nodes inventories to prevent player abuses
["moreblocks:circular_saw"] = true,
["craft_guide:sign_wall"] = true,
["basic_machines:battery_0"] = true,
["basic_machines:battery_1"] = true,
["basic_machines:battery_2"] = true,
["basic_machines:generator"] = true,
}
b.http_api = minetest.request_http_api();

BIN
textures/robochars.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB