diff --git a/commands.lua b/commands.lua index 4d095fa..67b5352 100644 --- a/commands.lua +++ b/commands.lua @@ -595,6 +595,8 @@ local write_keyevent = function(data,pos, puncher,type) end +basic_robot.commands.write_keyevent = write_keyevent; + local button_punched = function(pos, node, player,type) local name = player:get_player_name(); if name==nil then return end local round = math.floor; @@ -609,8 +611,10 @@ local button_punched = function(pos, node, player,type) end end -local register_robot_button = function(R,G,B,type) - minetest.register_node("basic_robot:button"..R..G..B, +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", tiles = {"robot_button.png^[colorize:#"..R..G..B..":180"}, @@ -624,12 +628,14 @@ local register_robot_button = function(R,G,B,type) end 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, { description = "robot button", - tiles = {"robot_button".. number .. ".png"}, - inventory_image = "robot_button".. number .. ".png", - wield_image = "robot_button".. number .. ".png", + tiles = {texname}, + inventory_image = texname, + wield_image = texname, paramtype2 = "facedir", is_ground_content = false, @@ -640,12 +646,14 @@ end 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, - { + { description = "robot button", - tiles = {string.format("%03d",number).. ".png"}, - inventory_image = string.format("%03d",number).. ".png", - wield_image = string.format("%03d",number).. ".png", + tiles = {texname}, + inventory_image = texname, + wield_image = texname, is_ground_content = false, groups = {cracky=3,not_in_craft_guide = 1}, paramtype2 = "facedir", @@ -654,45 +662,79 @@ minetest.register_node("basic_robot:button_"..number, end local register_robot_button_custom = function(number,texture) -minetest.register_node("basic_robot:button_"..number, - { - description = "robot button", - tiles = {texture .. ".png"}, - inventory_image = texture .. ".png", - wield_image = texture .. ".png", - is_ground_content = false, - groups = {cracky=3,not_in_craft_guide = 1}, - on_punch = function(pos, node,player) button_punched(pos, node,player,type) end + local type = number + minetest.register_node("basic_robot:button_"..number, + { + description = "robot button", + tiles = {texture .. ".png"}, + inventory_image = texture .. ".png", + wield_image = texture .. ".png", + is_ground_content = false, + groups = {cracky=3,not_in_craft_guide = 1}, + on_punch = function(pos, node,player) button_punched(pos, node,player,type) 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); -register_robot_button("80","80","80",2); +--16-color palette from macintosh II, 1987 + +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("80","FF","80",4); register_robot_button("80","80","FF",5); register_robot_button("FF","FF","80",6); +--]] -for i = 0,9 do register_robot_button_number(i,i+7) end -for i = 0,255 do register_robot_button_char(i,i+17) 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+27) end -register_robot_button_custom(273,"puzzle_switch_off") -register_robot_button_custom(274,"puzzle_switch_on") -register_robot_button_custom(275,"puzzle_button_off") -register_robot_button_custom(276,"puzzle_button_on") +register_robot_button_custom(283,"puzzle_switch_off") +register_robot_button_custom(284,"puzzle_switch_on") +register_robot_button_custom(285,"puzzle_button_off") +register_robot_button_custom(286,"puzzle_button_on") -register_robot_button_custom(277,"puzzle_equalizer") -register_robot_button_custom(278,"puzzle_setter") -register_robot_button_custom(279,"puzzle_piston") +register_robot_button_custom(287,"puzzle_equalizer") +register_robot_button_custom(288,"puzzle_setter") +register_robot_button_custom(289,"puzzle_piston") -register_robot_button_custom(280,"puzzle_diode") -register_robot_button_custom(281,"puzzle_NOT") -register_robot_button_custom(282,"puzzle_delayer") -register_robot_button_custom(283,"puzzle_platform") +register_robot_button_custom(290,"puzzle_diode") +register_robot_button_custom(291,"puzzle_NOT") +register_robot_button_custom(292,"puzzle_delayer") +register_robot_button_custom(293,"puzzle_platform") -register_robot_button_custom(284,"puzzle_giver") -register_robot_button_custom(285,"puzzle_checker") +register_robot_button_custom(294,"puzzle_giver") +register_robot_button_custom(295,"puzzle_checker") @@ -730,22 +772,12 @@ basic_robot.commands.keyboard = { local nodename; if type == 0 then nodename = "air" - elseif type == 1 then - nodename = "basic_robot:buttonFFFFFF"; - elseif type == 2 then - nodename = "basic_robot:button808080"; - elseif type == 3 then - nodename = "basic_robot:buttonFF8080"; - elseif type == 4 then - nodename = "basic_robot:button80FF80"; - elseif type == 5 then - nodename = "basic_robot:button8080FF"; - elseif type == 6 then - nodename = "basic_robot:buttonFFFF80"; - elseif type>=7 and type <= 16 then - nodename = "basic_robot:button"..(type-7); + elseif type < 17 then + nodename = buttoncolors[type] + elseif type>=17 and type <= 26 then + nodename = "basic_robot:button"..(type-17); else - nodename = "basic_robot:button_"..(type-17); + nodename = "basic_robot:button_"..(type-27); end minetest.swap_node(pos, {name = nodename}) @@ -988,7 +1020,7 @@ basic_robot.commands.machine = { 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 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 @@ -1162,9 +1194,8 @@ basic_robot.commands.machine = { local energy = 0; -- can only do one step at a run time - 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 tdata.menergy = tdata.menergy + amount diff --git a/init.lua b/init.lua index cc8a395..74de83e 100644 --- a/init.lua +++ b/init.lua @@ -1,32 +1,11 @@ --- basic_robot by rnd, 2016-2021 - +-- basic_robot by rnd, 2016-2022 basic_robot = {}; ------- SETTINGS -------- -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.version = "2022/02/02b"; -basic_robot.radius = 32; -- divide whole world into blocks of this size - used for managing events like keyboard punches -basic_robot.password = "raN___dOM_ p4S"; -- IMPORTANT: change it before running mod, password used for authentifications +dofile(minetest.get_modpath("basic_robot").."/settings.lua") -- read configuration SETTINGS -basic_robot.admin_bot_pos = {x=0,y=1,z=0} -- position of admin robot spawner that will be run automatically on server start - -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 = "2021/06/28a"; +-- structures for storing robot data basic_robot.gui = {}; local robogui = basic_robot.gui -- gui management basic_robot.data = {}; -- stores all robot related data @@ -50,7 +29,6 @@ local check_code, preprocess_code,is_inside_string; -- SANDBOX for running lua code isolated and safely - function getSandboxEnv (name) local authlevel = basic_robot.data[name].authlevel or 0; @@ -61,7 +39,11 @@ function getSandboxEnv (name) 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 = { _Gerror = error, @@ -93,9 +75,11 @@ function getSandboxEnv (name) return commands.craft(item, mode, idx, amount, name) 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 - coroutine.yield() + if not amount or basic_robot.data[name].operations 32 then return nil end 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); if stack then inv:set_stack("library", i, stack) end end @@ -369,8 +355,8 @@ function getSandboxEnv (name) end, }, - rom = basic_robot.data[name].rom, - + rom = basic_robot.data[pname].rom, + string = { byte = string.byte, char = string.char, find = string.find, @@ -525,7 +511,7 @@ function getSandboxEnv (name) setfenv( ScriptFunc, basic_robot.data[name].sandbox ) - local Result, RuntimeError = pcall( ScriptFunc ); + local _, RuntimeError = pcall( ScriptFunc ); if RuntimeError then minetest.chat_send_player(name, "#code.run: run error " .. RuntimeError ) return false @@ -592,10 +578,13 @@ function getSandboxEnv (name) end -- code checker +-- bugfixes: +-- player Midskip found problem with code checking, fixed + check_code = function(code) --"while ", "for ", "do ","goto ", - local bad_code = {"repeat", "until", "_G", "while%(", "while{", "pcall","%.%.[^%.]"} --,"\\\"", "%[=*%[","--[["} + local bad_code = {"repeat", "until", "_G", "while%(", "while{", "pcall","[^%.]%.%.[^%.]","\\\"","\\\'","%[=*%["} for _, v in pairs(bad_code) do if string.find(code, v) then return v .. " is not allowed!"; @@ -603,8 +592,7 @@ check_code = function(code) end end - -local identify_strings = function(code) -- returns list of positions {start,end} of literal strings in lua code +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 mode = 0; -- 0: not in string, 1: in '...' string, 2: in "..." string, 3. in [==[ ... ]==] string @@ -622,8 +610,12 @@ local identify_strings = function(code) -- returns list of positions {start,end} for k=1,#modes do j = string.find(code,modes[k][1],i); if j and j0 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 diff --git a/scripts/games/blackbox_game.lua b/scripts/games/blackbox_game.lua index fe20678..7c3c675 100644 --- a/scripts/games/blackbox_game.lua +++ b/scripts/games/blackbox_game.lua @@ -1,210 +1,211 @@ ---black box by rnd, 03/18/2017 ---https://en.wikipedia.org/wiki/Black_Box_(game) - -if not data then --- novice: 8x8, 4 - m=8;n=8; - atoms = 8 - attempts = 1;turn = 0; - spawnpos = self.spawnpos(); spawnpos.x = spawnpos.x-m/2; spawnpos.y = spawnpos.y+2; spawnpos.z = spawnpos.z-n/2 - - local players = find_player(5,spawnpos); - if not player then self.remove() else pname = players[1] end - - self.listen_punch(self.pos()) -- attach punch listener - self.spam(1);t0 = _G.minetest.get_gametime(); - data = {}; - for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end - - for i=1,atoms do -- put in atoms randomly - data[math.random(m)][math.random(n)] = 1 - end - - atoms = 0 - for i = 1,m do for j = 1,n do if data[i][j]==1 then atoms = atoms + 1 end end end - - render_board = function(mode) -- mode 0 : render without solution, 1: render solution - for i = 1,m do for j = 1,n do -- render game - if mode == 0 or data[i][j] == 0 then - if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then - keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2) - end - else - keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},3) - end - end end - end - - get_dirl = function(dir) - local dirl; -- direction left - if dir[1] > 0.5 then dirl = {0,-1} - elseif dir[1] < -0.5 then dirl = {0,1} - elseif dir[2] > 0.5 then dirl = {-1,0} - elseif dir[2] < -0.5 then dirl = {1,0} - end - return dirl - end - - read_pos = function(x,z) - if x<1 or x>m or z<1 or z>n then return nil end - return data[x][z] - end - - newdir = function(x,z,dir) -- where will ray go next - local retdir = {dir[1],dir[2]}; - local xf = x+dir[1]; local zf = z+dir[2] -- forward - local dirl = get_dirl(dir) - - local nodef = read_pos(xf,zf) - local nodel = read_pos(xf + dirl[1],zf + dirl[2]) - local noder = read_pos(xf - dirl[1],zf - dirl[2]) - if nodef == 1 then - retdir = {0,0} -- ray hit something - elseif nodel == 1 and noder ~= 1 then - retdir = {-dirl[1],-dirl[2]} - elseif nodel ~= 1 and noder == 1 then - retdir = {dirl[1],dirl[2]} - elseif nodel == 1 and noder == 1 then - retdir = {-dir[1],-dir[2]} - end - return retdir - end - - shootray = function(x,z,dir) - --say("ray starts " .. x .. " " .. z .. " dir " .. dir[1] .. " " .. dir[2]) - local xp = x; local zp = z; - local dirp = {dir[1],dir[2]}; - local maxstep = m*n; - - for i = 1,maxstep do - dirp = newdir(xp,zp,dirp); - if dirp[1]==0 and dirp[2]==0 then return -i end -- hit - xp=xp+dirp[1];zp=zp+dirp[2]; - if xp<1 or xp>m or zp<1 or zp>n then return i,{xp,zp} end -- out - end - return 0 -- hit - end - - count = 0; -- how many letters were used up - border_start_ray = function(x,z) - local rdir - if x==0 then rdir = {1,0} - elseif x == m+1 then rdir = {-1,0} - elseif z == 0 then rdir = {0,1} - elseif z == n+1 then rdir = {0,-1} - end - if rdir then - local result,out = shootray(x,z,rdir); - if result >= 0 then - - if out 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); - else - if result<=1 then - keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},6); -- immediate bounce off - else - local nodename = "basic_robot:button_"..(65+count); - _G.minetest.set_node( - {x=spawnpos.x+out[1],y=spawnpos.y+1,z=spawnpos.z+out[2]}, - {name = nodename, param2 = 1}) - _G.minetest.set_node( - {x=spawnpos.x+x,y=spawnpos.y+1,z=spawnpos.z+z}, - {name = nodename, param2 = 1}) - count = count + 1; - keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4); - keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},4); - end - end - end - elseif result<0 then - keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3); -- hit - end - end - end - - -- initial border loop and marking - - --render blue border - 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 - - 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 - border_start_ray(x,z) - end - end - - x=m+1 -- right - for z = 1,n do - if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then - border_start_ray(x,z) - end - end - - z=n+1 -- top - for x = m,1,-1 do - if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then - border_start_ray(x,z) - end - end - - x=0 -- left - for z = n,1,-1 do - if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then - border_start_ray(x,z) - end - end - - check_solution = function() - for i = 1,m do - for j = 1,n do - if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}) == "basic_robot:buttonFF8080" then -- red - if data[i][j]~=1 then return false end - else - if data[i][j]~=0 then return false end - end - end - end - return true - end - - --render board - render_board(0) - keyboard.set({x=spawnpos.x,y=spawnpos.y,z=spawnpos.z-1},4) - keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z-1},5) - self.label("BLACKBOX with " .. atoms .. " atoms") - -end - -event = keyboard.get(); -if event then - local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z; - if x<1 or x>m or z<1 or z>n then - if event.type == 4 then - if check_solution() then - say("#BLACKBOX : CONGRATULATIONS! " .. event.puncher .. " found all atoms after " .. attempts .. " tries."); self.remove() - else - say("#BLACKBOX : " .. event.puncher .. " failed to detect atoms after " .. attempts .. " attempts.") - attempts = attempts+1 - end - elseif event.type == 5 then - say("#BLACKBOX : DISPLAYING SOLUTION",pname) - render_board(1) - self.remove() - end - else -- interior punch - nodetype = 2; - if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button808080" then - nodetype = 3 - end - keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},nodetype); - end - -end +--black box by rnd, 03/18/2017 +--https://en.wikipedia.org/wiki/Black_Box_(game) + +if not data then +-- novice: 8x8, 4 + m=9;n=9; + atoms = 16 + 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 + + self.listen_punch(self.pos()) -- attach punch listener + self.spam(1);t0 = _G.minetest.get_gametime(); + data = {}; + for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end + + for i=1,atoms do -- put in atoms randomly + data[math.random(m)][math.random(n)] = 1 + end + + atoms = 0 + for i = 1,m do for j = 1,n do if data[i][j]==1 then atoms = atoms + 1 end end end + + render_board = function(mode) -- mode 0 : render without solution, 1: render solution + for i = 1,m do for j = 1,n do -- render game + if mode == 0 or data[i][j] == 0 then + if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:buttongray" then + keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},15) -- 2 gray + end + else + keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},4) --on 3 + end + end end + end + + get_dirl = function(dir) + local dirl; -- direction left + if dir[1] > 0.5 then dirl = {0,-1} + elseif dir[1] < -0.5 then dirl = {0,1} + elseif dir[2] > 0.5 then dirl = {-1,0} + elseif dir[2] < -0.5 then dirl = {1,0} + end + return dirl + end + + read_pos = function(x,z) + if x<1 or x>m or z<1 or z>n then return nil end + return data[x][z] + end + + newdir = function(x,z,dir) -- where will ray go next + local retdir = {dir[1],dir[2]}; + local xf = x+dir[1]; local zf = z+dir[2] -- forward + local dirl = get_dirl(dir) + + local nodef = read_pos(xf,zf) + local nodel = read_pos(xf + dirl[1],zf + dirl[2]) + local noder = read_pos(xf - dirl[1],zf - dirl[2]) + if nodef == 1 then + retdir = {0,0} -- ray hit something + elseif nodel == 1 and noder ~= 1 then + retdir = {-dirl[1],-dirl[2]} + elseif nodel ~= 1 and noder == 1 then + retdir = {dirl[1],dirl[2]} + elseif nodel == 1 and noder == 1 then + retdir = {-dir[1],-dir[2]} + end + return retdir + end + + shootray = function(x,z,dir) + --say("ray starts " .. x .. " " .. z .. " dir " .. dir[1] .. " " .. dir[2]) + local xp = x; local zp = z; + local dirp = {dir[1],dir[2]}; + local maxstep = m*n; + + for i = 1,maxstep do + dirp = newdir(xp,zp,dirp); + if dirp[1]==0 and dirp[2]==0 then return -i end -- hit + xp=xp+dirp[1];zp=zp+dirp[2]; + if xp<1 or xp>m or zp<1 or zp>n then return i,{xp,zp} end -- out + end + return 0 -- hit + end + + count = 0; -- how many letters were used up + border_start_ray = function(x,z) + local rdir + if x==0 then rdir = {1,0} + elseif x == m+1 then rdir = {-1,0} + elseif z == 0 then rdir = {0,1} + elseif z == n+1 then rdir = {0,-1} + end + if rdir then + local result,out = shootray(x,z,rdir); + if result >= 0 then + + if out 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); + else + if result<=1 then + keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},6); -- immediate bounce off + else + local nodename = "basic_robot:button_"..(65+count); + _G.minetest.set_node( + {x=spawnpos.x+out[1],y=spawnpos.y+1,z=spawnpos.z+out[2]}, + {name = nodename, param2 = 1}) + _G.minetest.set_node( + {x=spawnpos.x+x,y=spawnpos.y+1,z=spawnpos.z+z}, + {name = nodename, param2 = 1}) + count = count + 1; + keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4); + keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},4); + end + end + end + elseif result<0 then + keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3); -- hit + end + end + end + + -- initial border loop and marking + + --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 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 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:buttonblue" then + border_start_ray(x,z) + end + end + + x=m+1 -- right + for z = 1,n do + if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:buttonblue" then + border_start_ray(x,z) + end + end + + z=n+1 -- top + for x = m,1,-1 do + if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:buttonblue" then + border_start_ray(x,z) + end + end + + x=0 -- left + for z = n,1,-1 do + if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:buttonblue" then + border_start_ray(x,z) + end + end + + check_solution = function() + for i = 1,m do + for j = 1,n do + if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}) == "basic_robot:buttonred" then -- red + if data[i][j]~=1 then return false end + else + if data[i][j]~=0 then return false end + end + end + end + return true + end + + --render board + render_board(0) + 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) + self.label("BLACKBOX with " .. atoms .. " atoms") + +end + +event = keyboard.get(); +if event then + local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z; + --self.label(serialize(event)) + if x<1 or x>m or z<1 or z>n then + if event.type == 9 then + if check_solution() then + say("#BLACKBOX : CONGRATULATIONS! " .. event.puncher .. " found all atoms after " .. attempts .. " tries."); self.remove() + else + say("#BLACKBOX : " .. event.puncher .. " failed to detect atoms after " .. attempts .. " attempts.") + attempts = attempts+1 + end + elseif event.type == 7 then + say("#BLACKBOX : DISPLAYING SOLUTION",pname) + render_board(1) + self.remove() + end + else -- interior punch + nodetype = 4; + if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:buttonred" then + nodetype = 15 + end + keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},nodetype); + end + +end ::END:: \ No newline at end of file diff --git a/scripts/games/connect4.lua b/scripts/games/connect4.lua index 415e7d8..5096246 100644 --- a/scripts/games/connect4.lua +++ b/scripts/games/connect4.lua @@ -1,67 +1,67 @@ --- CONNECT, coded in 20 minutes by rnd -if not data then - m=10;n=10;turn = 0; num = 5; --- m=3;n=3;turn = 0; num = 3; - self.spam(1);t0 = _G.minetest.get_gametime(); - spawnpos = self.spawnpos() -- place mines - self.listen_punch(self.pos()) -- attach punch listener - state = 0; -- 0 signup 1 game - players = {}; - data = {}; - for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end - for i = 1,m do for j = 1,n do -- render game - if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then - keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2) - end - end end - - get_count_in_dir = function(dir,x,y) - local r = num; -- num=4? in a row - local snode = data[x][y];local count = 1; - for j = 1,2 do - for i = 1,r-1 do - local x1 = x + dir[1]*i;local y1 = y + dir[2]*i; - if not data[x1] or not data[x1][y1] then break end; if data[x1][y1]~= snode then break end - count = count +1 - end - dir[1]=-dir[1];dir[2]=-dir[2]; - end - return count - end - - get_count = function(x,y) - 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) - if c2>c1 then c1 = c2 end; if c3>c1 then c1 = c3 end; if c4>c1 then c1 = c4 end - return c1 - end - - self.label("TRY TO GET FIRST " .. num .. " IN A ROW! : GREEN starts play. 2 players punch to join game.") -end - -event = keyboard.get(); -if event then - local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z; - if x<1 or x>m or z<1 or z>n then - elseif event.type == 2 then --if event.type == 2 then - if state == 0 then - if #players<2 then players[#players+1] = event.puncher -- 1 is green, turn 0 = green - else state = 1 end - if #players==2 then state = 1 end - end - if event.puncher == players[1] then -- green - if turn~=0 then return end -- ignore if not player turn - else - if turn~=1 then return end - end - keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4+turn); - data[x][z] = 4+turn; - 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 +-- CONNECT, coded in 20 minutes by rnd +if not data then + m=10;n=10;turn = 0; num = 4; + + self.spam(1);t0 = _G.minetest.get_gametime(); + spawnpos = self.spawnpos() -- place mines + self.listen_punch(self.pos()) -- attach punch listener + state = 0; -- 0 signup 1 game + players = {}; + data = {}; + for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end + for i = 1,m do for j = 1,n do -- render game + if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:buttonlight_gray" then + keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},13) + end + end end + + get_count_in_dir = function(dir,x,y) + local r = num; -- num=4? in a row + local snode = data[x][y];local count = 1; + for j = 1,2 do + for i = 1,r-1 do + local x1 = x + dir[1]*i;local y1 = y + dir[2]*i; + if not data[x1] or not data[x1][y1] then break end; if data[x1][y1]~= snode then break end + count = count +1 + end + dir[1]=-dir[1];dir[2]=-dir[2]; + end + return count + end + + get_count = function(x,y) + 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) + if c2>c1 then c1 = c2 end; if c3>c1 then c1 = c3 end; if c4>c1 then c1 = c4 end + return c1 + end + + self.label("TRY TO GET FIRST " .. num .. " IN A ROW! : GREEN starts play. 2 players punch to join game.") +end + +event = keyboard.get(); +if event then + local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z; + if x<1 or x>m or z<1 or z>n then + elseif event.type == 13 then --if event.type == 2 then + if state == 0 then + if #players<2 then players[#players+1] = event.puncher -- 1 is green, turn 0 = green + else state = 1 end + if #players==2 then state = 1 end + end + if event.puncher == players[1] then -- green + if turn~=0 then return end -- ignore if not player turn + else + if turn~=1 then return end + end + keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4+turn); + data[x][z] = 4+turn; + 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:: \ No newline at end of file diff --git a/scripts/games/hide_and_seek.lua b/scripts/games/hide_and_seek.lua index 43d4d9d..f8d79e5 100644 --- a/scripts/games/hide_and_seek.lua +++ b/scripts/games/hide_and_seek.lua @@ -1,106 +1,140 @@ ---HIDE AND SEEK game robot, by rnd -if not gamemaster then - timeout = 10; - gamemaster = "rnd" - player_list = {}; - _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) - self.listen(1); self.label(colorize("yellow","HIDE&SEEK")) -end - -speaker,msg = self.listen_msg(); - -if s==0 then - if msg =="#hide" then - player_list[speaker]={}; - _G.minetest.chat_send_all("# HIDE AND SEEK: " .. speaker .. " joined the game") - local player = _G.minetest.get_player_by_name(speaker); - if player then - player:setpos({x=0,y=5,z=0});player:set_properties({nametag_color = "0x0"}) - end - - end - if msg == "#start" and speaker == gamemaster then s = 0.5 _G.minetest.chat_send_all("# 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 - gamemaster = false; _G.minetest.chat_send_all("# HIDE AND SEEK only 1 player, aborting.") - else - _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.")) - 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),math.abs(pos.y),math.abs(pos.z)); - if dist>50 or (not _G.minetest.get_player_by_name(name)) then - _G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! went too far away " ) - player:set_properties({nametag_color = "white"}) - player_list[name] = nil; - end - if data.hp ~= player:get_hp() then - _G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! his health changed!" ) - player:set_properties({nametag_color = "white"}) - player_list[name] = nil; - 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>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 - - self.label(count) - - if count<=1 then - if count==1 then - for name,_ in pairs(player_list) do - player0=_G.minetest.get_player_by_name(name) - _G.minetest.chat_send_all(colorize("red","****** HIDE AND SEEK: ".. name .. " wins ******")) - player0:set_properties({nametag_color = "white"}) - gamemaster = false; - end - else - _G.minetest.chat_send_all("# HIDE AND SEEK: no players left") - gamemaster = false; - end - end - -end \ No newline at end of file +--HIDE AND SEEK game robot, by rnd +if not gamemaster then + timeout = 20; + fardist = 300 + gamemaster = "rnd" + + _G.minetest.forceload_block(self.pos(),true) + self.listen(1); + centerpos = {x=160,y= 606,z= 227}; + + init_game = function() + 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.")) + s=0;t=0; count = 0; + player_list = {} + end + + reset_name = function(name) + if name then + local player = minetest.get_player_by_name(name) + if not player then return end + player:set_properties({nametag_color = "white"}) + return + end + local players = _G.minetest.get_connected_players(); + for _,player in pairs(players) do + local name = player:get_player_name(); + local data = player_list[name]; + if data then + player:set_properties({nametag_color = "white"}) + end + end + end + + show_players = function() + local ret = {} + for k,v in pairs(player_list) do + ret[#ret+1]=k + end + self.label("hide and seek, players: " .. table.concat(ret,", ")) + end + init_game() +end + +speaker,msg = self.listen_msg(); + +if s==0 then + if msg =="#hide" then + player_list[speaker]={}; + _G.minetest.chat_send_all("# HIDE AND SEEK: " .. speaker .. " joined the game") + local player = _G.minetest.get_player_by_name(speaker); + if player then + player:setpos(centerpos);player:set_properties({nametag_color = "0x0"}) + end + + end + if msg == "#start" and speaker == gamemaster then s = 0.5 _G.minetest.chat_send_all("# 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 + gamemaster = false; _G.minetest.chat_send_all("# HIDE AND SEEK only 1 player, aborting.") + else + _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.")) + 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-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 + _G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! went too far away " ) + reset_name(name) + show_players() + player_list[name] = nil; + end + if data.hp < player:get_hp() then + _G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! his health changed!" ) + player:set_properties({nametag_color = "white"}) + player:setpos(centerpos) + player_list[name] = nil; + show_players() + 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>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 + diff --git a/scripts/games/lights_out.lua b/scripts/games/lights_out.lua new file mode 100644 index 0000000..2ce2c35 --- /dev/null +++ b/scripts/games/lights_out.lua @@ -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() \ No newline at end of file diff --git a/scripts/games/minesweeper_game.lua b/scripts/games/minesweeper_game.lua index 86f68f1..aa03b4d 100644 --- a/scripts/games/minesweeper_game.lua +++ b/scripts/games/minesweeper_game.lua @@ -8,21 +8,22 @@ if not data then self.spam(1) t0 = _G.minetest.get_gametime(); data = {}; spawnpos = self.spawnpos() -- place mines - self.listen_punch(self.pos()); -- attach punch listener + self.listen_punch(spawnpos); -- attach punch listener 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 data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0; + minescount = 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 - puzzle.set_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},{name = "basic_robot:button808080"}) + 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+i,y=spawnpos.y,z=spawnpos.z+j},{name = "basic_robot:buttonlight_grey"}) end end end - puzzle.set_node({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},{name = "basic_robot:button80FF80"}) - puzzle.set_node({x=spawnpos.x,y=spawnpos.y,z=spawnpos.z+1},{name = "basic_robot:button80FF80"}) + puzzle.set_node({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},{name = "basic_robot:buttondark_green"}) + puzzle.set_node({x=spawnpos.x+2,y=spawnpos.y,z=spawnpos.z},{name = "basic_robot:buttonblue"}) 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 @@ -34,7 +35,7 @@ if not data then chk_mines = function() local count = minescount; for i=1,m do for j=1,n do - 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 + 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 count=count-1 end end end @@ -47,9 +48,10 @@ end event = keyboard.get(); if event then + local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z; if x<1 or x>m or z<1 or z>n then - if x == 0 and z == 1 then + if x == 1 and z == 0 then local count = chk_mines(); if count>minescount/2 then say("fail, more than 50% of mines remaining ") @@ -69,20 +71,20 @@ if event then else --if event.type == 2 then local ppos = player.getpos(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 - if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:button808080" then - puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:button808080"}) + if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:buttonlight_grey" then + puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:buttonlight_grey"}) else - puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:buttonFF8080"}) + puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:buttonred"}) end else if data[x] and data[x][z]==1 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"}); + say("boom! "..event.puncher .. " is dead ");puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:buttonred"}); 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:button80FF80"}) + 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 diff --git a/scripts/games/nonogram.lua b/scripts/games/nonogram.lua index 688b00e..22c4a01 100644 --- a/scripts/games/nonogram.lua +++ b/scripts/games/nonogram.lua @@ -2,7 +2,7 @@ -- INIT if not grid then - n=6 + n=9 solved = false -- do we render solution or blank? -- _G.math.randomseed(3) @@ -21,7 +21,7 @@ if not grid then end _,scores_string = book.read(1); scores = minetest.deserialize(scores_string) - if not scores then scores = init_score(5,5,-999) end -- 5 levels, 5 top records + if not scores then scores = init_score(n-1,n-1,-999) end -- 5 levels, 5 top records t0 = _G.minetest.get_gametime() local intro ="numbers at beginning of each row (coloumn) tell how many\nred blocks are together in each row ( coloumn )." .. @@ -85,7 +85,7 @@ if not grid then grid[i]={}; for j = 1,n do local typ = keyboard.read({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+i}); - if typ == "basic_robot:button808080" then grid[i][j] = 0 else grid[i][j] = 1 end + if typ == "basic_robot:buttonlight_grey" then grid[i][j] = 0 else grid[i][j] = 1 end end end return grid @@ -131,7 +131,7 @@ if not grid then if #coldata[k]>0 then sum = sum + coldata[k][#coldata[k]] else sum = n end if sum == n then easy = easy + 1 end end - easy = 5-easy; + easy = n-1-easy; if easy < 0 then easy = 0 end return easy end @@ -142,8 +142,8 @@ if not grid then keyboard.set({x=spawnpos.x-n+j,y=spawnpos.y,z=spawnpos.z+i},0) -- clear keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+2*n-i+1},0) -- clear local typ; - if grid[j][i]==0 then typ = 2 else typ = 3 end - if not solved then typ = 2 end + if grid[j][i]==0 then typ = 13 else typ = 2 end + if not solved then typ = 13 end keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ) --board end end @@ -152,18 +152,18 @@ if not grid then for i=1,n do length = #rowdata[i] for k = 1,length do - keyboard.set({x=spawnpos.x-length+k,y=spawnpos.y,z=spawnpos.z+i},rowdata[i][k]+7) + keyboard.set({x=spawnpos.x-length+k,y=spawnpos.y,z=spawnpos.z+i},rowdata[i][k]+17) end end --render counts coloumns for j=1,n do length = #coldata[j] for k = 1,length do - keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+k+n},coldata[j][k]+7) + keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+k+n},coldata[j][k]+17) end end - 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 + keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},9) -- game check button + keyboard.set({x=spawnpos.x+2,y=spawnpos.y,z=spawnpos.z},7) -- game check button local players = find_player(6,spawnpos) if not players then error("nonogram: no players near") end @@ -183,6 +183,9 @@ if not grid then elseif difficulty == 2 then limit = 80 reward = 7 elseif difficulty <= 1 then limit = 70 reward = 6 end + if not scores[difficulty] then scores[difficulty] = {{"-",-999}} end + if not scores[difficulty][1] then scores[difficulty][1] = {"-",-999} end + minetest.chat_send_player(pname, "nonogram difficulty " .. difficulty .. ". you will get " .. reward .. " gold if you solve it in faster than " .. limit .."s" .. ". Current record " .. -scores[difficulty][1][2] .. " by " .. scores[difficulty][1][1]) @@ -233,7 +236,7 @@ if event then for i=1,n do for j =1,n do local typ; - if grid[j][i]==0 then typ = 2 else typ = 3 end + 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) end end @@ -244,7 +247,7 @@ if event then if i>0 and i<=n and j>0 and j<=n then 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 + if typ ~= "basic_robot:buttonlight_grey" then newtyp = 13 else newtyp = 2 end keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},newtyp); diff --git a/scripts/games/paint_game.lua b/scripts/games/paint_game.lua index 0b7075e..4976336 100644 --- a/scripts/games/paint_game.lua +++ b/scripts/games/paint_game.lua @@ -1,112 +1,112 @@ --- paint canvas by rnd, 2018 -if not init then - colors = { - "black","blue","brown","cyan","dark_green","dark_grey","green","grey", - "magenta","orange","pink","red","violet","white","yellow" - } - invcolors = {}; for i = 1,#colors do invcolors[colors[i]] = i end - - color = 1; - size = 16; - - init = true - - local ent = _G.basic_robot.data[self.name()].obj:get_luaentity(); - ent.timestep = 0.5 - - players = find_player(5); if not players then self.remove() end - player = _G.minetest.get_player_by_name(players[1]) - self.label("-> " .. players[1]) - - spos = self.spawnpos(); spos.y=spos.y+1; - - canvasn = "wool:white" - reset_canvas = function() - for i = 1, size do - for j = 1, size do - minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn}) - end - end - end - reset_canvas() - - save_image = function() - local ret = {}; - for i = 1, size do - for j = 1, size do - local nname = string.sub(minetest.get_node({x=spos.x +i , y = spos.y + j, z = spos.z }).name,6) - local pcolor = invcolors[nname] or 1; - ret[#ret+1]= string.char(96+pcolor) - end - end - return table.concat(ret,"") - end - - load_image = function(image) - if not image then return end - local ret = {}; local k = 0; - for i = 1, size do - for j = 1, size do - k=k+1; - local pcolor = colors[string.byte(image,k)-96] or "black"; - minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = "wool:"..pcolor}) - end - end - end - - - --draw buttons - for i = 1,#colors do - minetest.set_node({x=spos.x +i , y = spos.y , z = spos.z },{name = "wool:"..colors[i]}) - end - - minetest.set_node({x=spos.x +1 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_83"}) - minetest.set_node({x=spos.x +2 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_76"}) - - - vn = {x=0,y=0,z=1}; - T0 = {x=spos.x+0.5,y=spos.y+0.5,z=spos.z-0.5*vn.z}; - - 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 b = vn.x*v.x + vn.y*v.y + vn.z*v.z - if b<=0 then return nil end - if a<=0 then return nil end - local t = a / b - return {x = p.x+v.x*t, y= p.y+v.y*t, z = p.z+v.z*t} - end - -end - -if player:get_player_control().LMB then -- player interacts with 'virtual canvas gui' - local v = player:get_look_dir(); - local p = player:get_pos(); p.y = p.y + 1.5 - local c = get_intersect(vn,T0,p,v); - if c then - - local x = c.x - T0.x; local y = c.y - T0.y - if x>0 and x-2 and y0 then -- above: painting - c.z = c.z+0.5 - minetest.set_node(c, {name = "wool:" .. colors[color]}) - elseif y>-1 then -- color selection - x = 1+math.floor(x) - if colors[x] then - color = x; - self.label(colors[x]) - end - else -- save,load button row - x = 1+math.floor(x) - if x==1 then - self.label("SAVED.") - book.write(1,"ROBOT_IMAGE",save_image()) - elseif x==2 then - local _,image = book.read(1) - load_image(image); - self.label("LOADED.") - end - end - end - - end +-- paint canvas by rnd, 2018 +if not init then + colors = { + "white","yellow","orange","red","magenta","purple","blue","cyan", + "green","dark_green","brown","tan","light_grey","medium_grey","dark_grey","black" + } + invcolors = {}; for i = 1,#colors do invcolors[colors[i]] = i end + + color = invcolors["black"]; + size = 16; + + init = true + + local ent = _G.basic_robot.data[self.name()].obj:get_luaentity(); + ent.timestep = 0.125 + + players = find_player(5); if not players then self.remove() end + player = _G.minetest.get_player_by_name(players[1]) + self.label("-> " .. players[1]) + + spos = self.spawnpos(); spos.y=spos.y+1; + canvasn = "basic_robot:buttonwhite" + reset_canvas = function() + for i = 1, size do + for j = 1, size do + minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn}) + end + end + end + reset_canvas() + + save_image = function() + local ret = {}; + for i = 1, size do + for j = 1, size do + local nname = string.sub(minetest.get_node({x=spos.x +i , y = spos.y + j, z = spos.z }).name,19) + local pcolor = invcolors[nname] or 1; + ret[#ret+1]= string.char(96+pcolor) + end + end + + return table.concat(ret,"") + end + + load_image = function(image) + if not image then return end + local ret = {}; local k = 0; + for i = 1, size do + for j = 1, size do + k=k+1; + local pcolor = colors[string.byte(image,k)-96] or "black"; + minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = "basic_robot:button"..pcolor}) + end + end + end + + + --draw buttons + for i = 1,#colors do + minetest.set_node({x=spos.x +i , y = spos.y , z = spos.z },{name = "basic_robot:button"..colors[i]}) + end + + minetest.set_node({x=spos.x +1 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_83"}) + minetest.set_node({x=spos.x +2 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_76"}) + + + vn = {x=0,y=0,z=1}; + T0 = {x=spos.x+0.5,y=spos.y+0.5,z=spos.z-0.5*vn.z}; + + 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 b = vn.x*v.x + vn.y*v.y + vn.z*v.z + if b<=0 then return nil end + if a<=0 then return nil end + local t = a / b + return {x = p.x+v.x*t, y= p.y+v.y*t, z = p.z+v.z*t} + end + +end + +if player:get_player_control().LMB then -- player interacts with 'virtual canvas gui' + local v = player:get_look_dir(); + local p = player:get_pos(); p.y = p.y + 1.5 + local c = get_intersect(vn,T0,p,v); + if c then + + local x = c.x - T0.x; local y = c.y - T0.y + if x>0 and x-2 and y0 then -- above: painting + c.z = c.z+0.5 + minetest.set_node(c, {name = "basic_robot:button" .. colors[color]}) + elseif y>-1 then -- color selection + x = 1+math.floor(x) + if colors[x] then + color = x; + self.label(colors[x]) + end + else -- save,load button row + x = 1+math.floor(x) + if x==1 then + self.label("SAVED.") + book.write(1,"ROBOT_IMAGE",save_image()) + elseif x==2 then + local _,image = book.read(1) + load_image(image); + self.label("LOADED.") + end + end + end + + end end \ No newline at end of file diff --git a/scripts/games/rubik.lua b/scripts/games/rubik.lua new file mode 100644 index 0000000..13acc20 --- /dev/null +++ b/scripts/games/rubik.lua @@ -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)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 +-- \ No newline at end of file diff --git a/scripts/games/simple_box_push.lua b/scripts/games/simple_box_push.lua index 4a737ec..fb371f5 100644 --- a/scripts/games/simple_box_push.lua +++ b/scripts/games/simple_box_push.lua @@ -1,56 +1,59 @@ --- simple box pushing game, rnd - -if not init then - spos = self.spawnpos(); spos.x = spos.x +5; spos.z = spos.z +5; - - for i = 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"}) - end - end - - init = true - players = find_player(5); - if not players then say("no players nearby") self.remove() end - say("BOX PUSH demo. punch the white box to move it around ") - - pushables = {[1] = true} -- button types - canpushnodes = {["air"] = 1, ["basic_robot:button8080FF"] = 2} -- 1 push node, 2 absorb node -end - -event = keyboard.get() -if event then - local boxtype = event.type - if pushables[boxtype] then - player = puzzle.get_player(event.puncher) - local pos = player:getpos(); - local boxpos = {x = event.x, y = event.y, z = event.z}; - local diff = { pos.x-boxpos.x, pos.z-boxpos.z}; - - local newx,newz - if math.abs(diff[1])>math.abs(diff[2]) then -- punch in x-direction - newx = boxpos.x - (diff[1]>0 and 1 or -1) - newz = boxpos.z - else - newx = boxpos.x - newz = boxpos.z - (diff[2]>0 and 1 or -1) - end - - local newnode = puzzle.get_node({x=newx, y= boxpos.y, z= newz}).name - - - local canpush = canpushnodes[newnode] - if canpush then - local oldnode = puzzle.get_node(boxpos).name - puzzle.set_node(boxpos,{name= "air"}) -- remove node - if canpush == 1 then -- simply move the box - newnode = oldnode - elseif canpush == 2 then -- absorb the box - newnode = newnode - end - - puzzle.set_node({x=newx, y= boxpos.y, z= newz}, {name = newnode}) - end - end - --say(serialize(event)) +-- simple box pushing game, rnd + +if not init then + spos = self.spawnpos(); spos.x = spos.x +5; spos.z = spos.z +5; + + for i = 1, 2 do -- set up 4 boxes + for j = 1,2 do + puzzle.set_node({x=spos.x+i,y=spos.y,z=spos.z+j}, {name = "basic_robot:buttonwhite"}) + end + end + + init = true + players = find_player(5); + if not players then say("no players nearby") self.remove() end + 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 + canpushnodes = {["air"] = 1, ["basic_robot:buttonblue"] = 2} -- 1 push node, 2 absorb node + + self.listen_punch(self.pos()) -- robot will now read punch button events in 32x32 box area +end + +event = keyboard.get() + +if event then -- there was punch event + local boxtype = event.type + if pushables[boxtype] then + player = puzzle.get_player(event.puncher) + local pos = player:getpos(); + local boxpos = {x = event.x, y = event.y, z = event.z}; + local diff = { pos.x-boxpos.x, pos.z-boxpos.z}; + + local newx,newz + if math.abs(diff[1])>math.abs(diff[2]) then -- punch in x-direction + newx = boxpos.x - (diff[1]>0 and 1 or -1) + newz = boxpos.z + else + newx = boxpos.x + newz = boxpos.z - (diff[2]>0 and 1 or -1) + end + + local newnode = puzzle.get_node({x=newx, y= boxpos.y, z= newz}).name + + + local canpush = canpushnodes[newnode] + if canpush then + local oldnode = puzzle.get_node(boxpos).name + puzzle.set_node(boxpos,{name= "air"}) -- remove node + if canpush == 1 then -- simply move the box + newnode = oldnode + elseif canpush == 2 then -- absorb the box + newnode = newnode + end + + puzzle.set_node({x=newx, y= boxpos.y, z= newz}, {name = newnode}) + end + end + --say(serialize(event)) end \ No newline at end of file diff --git a/scripts/games/sliding_puzzle_game.lua b/scripts/games/sliding_puzzle_game.lua index fd5142b..f833a08 100644 --- a/scripts/games/sliding_puzzle_game.lua +++ b/scripts/games/sliding_puzzle_game.lua @@ -19,7 +19,7 @@ if not init then board[i]={}; for j = 1,n do 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 board[math.random(n)][math.random(n)] = 0 diff --git a/scripts/games/sokoban_game.lua b/scripts/games/sokoban_game.lua index c176f28..1fa54c3 100644 --- a/scripts/games/sokoban_game.lua +++ b/scripts/games/sokoban_game.lua @@ -34,7 +34,7 @@ self.spam(1) - self.listen_punch(self.pos()); -- attach punch listener + self.listen_punch(self.pos()); -- attach punch listener sokoban.push_time = 0 sokoban.blocks = 0;sokoban.level = 0; sokoban.moves=0; imax = 0; jmax = 0 @@ -43,7 +43,7 @@ SOKOBAN_WALL = "moreblocks:cactus_brick" SOKOBAN_FLOOR = "default:silver_sandstone" SOKOBAN_GOAL = "default:aspen_tree" - SOKOBAN_BOX = "basic_robot:buttonFFFFFF" + SOKOBAN_BOX = "basic_robot:buttonwhite" load_level = function(lvl) diff --git a/scripts/games/wordle.lua b/scripts/games/wordle.lua new file mode 100644 index 0000000..4e30685 --- /dev/null +++ b/scripts/games/wordle.lua @@ -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 #responses1 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 + + diff --git a/scripts/misc/flower_gunnew 6.lua b/scripts/misc/flower_gunnew 6.lua new file mode 100644 index 0000000..2aaa172 --- /dev/null +++ b/scripts/misc/flower_gunnew 6.lua @@ -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 diff --git a/scripts/simulators/redstone_emulator.lua b/scripts/simulators/redstone_emulator.lua index 7fc4447..1e51967 100644 --- a/scripts/simulators/redstone_emulator.lua +++ b/scripts/simulators/redstone_emulator.lua @@ -1,507 +1,514 @@ --- REDSTONE EMULATOR & EDITOR ---v 06/28/2018a - -if not init then - local players = find_player(5); - if not players then - name = ""; - else - name = players[1] - player_ = puzzle.get_player(name) - local inv = player_:get_inventory(); - inv:set_stack("main", 8, puzzle.ItemStack("basic_robot:control 1 0 \"@\"")) -- add controller in players inventory - --add items for building - inv:set_stack("main", 1, puzzle.ItemStack("default:pick_diamond")) - inv:set_stack("main", 2, puzzle.ItemStack("basic_robot:button_273 999")) -- switch 9 = 273/274 - inv:set_stack("main", 3, puzzle.ItemStack("basic_robot:button_275 999")) -- button 7 = 275/276 - inv:set_stack("main", 4, puzzle.ItemStack("basic_robot:button_277 999")) -- equalizer 61 = 277 - inv:set_stack("main", 5, puzzle.ItemStack("basic_robot:button_278 999")) -- setter 15 = 278 - inv:set_stack("main", 6, puzzle.ItemStack("basic_robot:button_279 999")) -- piston 171 = 279 - inv:set_stack("main", 7, puzzle.ItemStack("basic_robot:button_282 999")) -- delayer 232 = 282 - inv:set_stack("main", 9, puzzle.ItemStack("basic_robot:button_281 999")) -- NOT 33 = 281 - inv:set_stack("main", 10, puzzle.ItemStack("basic_robot:button_280 999")) -- diode 175 = 280 - inv:set_stack("main", 11, puzzle.ItemStack("basic_robot:button_283 999")) -- platform 22 = 283 - inv:set_stack("main", 12, puzzle.ItemStack("basic_robot:button_284 999")) -- giver 23 150/284 - inv:set_stack("main", 13, puzzle.ItemStack("basic_robot:button_285 999")) -- checker 24 151/285 - - - local round = math.floor; protector_position = function(pos) local r = 32;local ry = 2*r; return {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry,z=round(pos.z/r+0.5)*r}; end - local spawnblock = protector_position(self.spawnpos()) - - local meta = puzzle.get_meta(spawnblock); - meta:set_string("shares", name) -- add player to protection! - puzzle.chat_send_player(name,colorize("yellow","#EDITOR: if you need any blocks get them by using 'give me' in craft guide. you can now use controller to make links from pointed at blocks. In addition hold SHIFT to display infos. Reset block links by selecting block 2x")) - end - - init = true - self.spam(1) - self.label(colorize("orange","REDSTONE EMULATOR/EDITOR")) - - - - - -- 1. EMULATOR CODE - - - TTL = 16 -- signal propagates so many steps before dissipate - --self.label(colorize("red","REDSTONE")..colorize("yellow","EMULATOR")) - opcount = 0; - - -- DEFINITIONS OF BLOCKS THAT CAN BE ACTIVATED - toggle_button_action = function(mode,pos,ttl) -- SIMPLE TOGGLE BUTTONS - SWITCH - if not ttl or ttl <=0 then return end - if mode == 1 then -- turn on - puzzle.set_node(pos,{name = "basic_robot:button_274"}) - local meta = puzzle.get_meta(pos); - if not meta then return end - local n = meta:get_int("n"); - for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end - else -- turn off - puzzle.set_node(pos,{name = "basic_robot:button_273"}) - local meta = puzzle.get_meta(pos); - if not meta then return end - local n = meta:get_int("n"); - for i = 1,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end - end - end - - - button_action = function(mode,pos,ttl) -- SIMPLE ON BUTTON, TOGGLES BACK OFF after 1s - if not ttl or ttl <=0 then return end - if mode == 0 then return end - puzzle.set_node(pos,{name = "basic_robot:button_276"}) - local meta = puzzle.get_meta(pos); - if not meta then return end - local n = meta:get_int("n"); - - minetest.after(1, function() - puzzle.set_node(pos,{name = "basic_robot:button_275"}) - for i = 1,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end - end) - for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end - end - - giver_action = function(mode,pos,ttl) -- GIVER: give block below it to player and activate targets - local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name; - if nodename == "air" then return end - local objects = minetest.get_objects_inside_radius(pos, 5);local player1; - for _,obj in pairs(objects) do if obj:is_player() then player1 = obj; break end end - if player1 then - player1:get_inventory():add_item("main", puzzle.ItemStack(nodename)) - local meta = puzzle.get_meta(pos); - if not meta then return end - local n = meta:get_int("n"); - for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end - end - end - - checker_action = function(mode,pos,ttl) -- CHECKER: check if player has block below it, then remove block from player and activate targets - local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name; - if nodename == air then return end - local objects = minetest.get_objects_inside_radius(pos, 5);local player1; - for _,obj in pairs(objects) do if obj:is_player() then player1 = obj; break end end - if player1 then - local inv = player1:get_inventory(); - if inv:contains_item("main", puzzle.ItemStack(nodename)) then - inv:remove_item("main",puzzle.ItemStack(nodename)) - local meta = puzzle.get_meta(pos); - if not meta then return end - local n = meta:get_int("n"); - for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end - end - end - end - - equalizer_action = function(mode,pos,ttl) -- CHECK NODES AT TARGET1,TARGET2. IF EQUAL ACTIVATE TARGET3,TARGET4,... - if not ttl or ttl <=0 then return end - if mode == 0 then return end - - local meta = puzzle.get_meta(pos); - if not meta then return end - local n = meta:get_int("n"); - local node1 = puzzle.get_node({x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}).name - local node2 = puzzle.get_node({x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z}).name - - - if node1==node2 then - for i = 3,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end - else - for i = 3,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end - end - end - - delayer_action = function(mode,pos,ttl) -- DELAY FORWARD SIGNAL, delay determined by distance of target1 from delayer ( in seconds) - if not ttl or ttl <=0 then return end - local meta = puzzle.get_meta(pos); - if not meta then return end - - local n = meta:get_int("n"); - local pos1 = {x=meta:get_int("x1"),y=meta:get_int("y1"),z=meta:get_int("z1")} - local delay = math.sqrt(pos1.x^2+pos1.y^2+pos1.z^2); - - if delay > 0 then - minetest.after(delay, function() - if mode == 1 then - for i = 2,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end - else - for i = 2,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end - end - end) - end - end - - diode_action = function(mode,pos,ttl) -- ONLY pass through ON signal - if not ttl or ttl <=0 then return end - if mode ~= 1 then return end - local meta = puzzle.get_meta(pos); - if not meta then return end - local n = meta:get_int("n"); - for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end - end - - not_action = function(mode,pos,ttl) -- negate signal: 0 <-> 1 - if not ttl or ttl <=0 then return end - local meta = puzzle.get_meta(pos); - if not meta then return end - local n = meta:get_int("n"); - for i = 1,n do activate(1-mode,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end - end - - setter_action = function(mode,pos,ttl) -- SETS NODES IN TARGET AREA TO PRESELECTED NODE - if not ttl or ttl <=0 then return end - if mode == 0 then return end - - local meta = puzzle.get_meta(pos); - if not meta then return end - local n = meta:get_int("n"); - if n ~= 3 then say("#setter: error, needs to be set with 3 links"); return end - local node1 = puzzle.get_node({x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}) - local pos1 = {x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z} - local pos2 = {x=meta:get_int("x3")+pos.x,y=meta:get_int("y3")+pos.y,z=meta:get_int("z3")+pos.z} - - if pos1.x>pos2.x then pos1.x,pos2.x = pos2.x,pos1.x end - if pos1.y>pos2.y then pos1.y,pos2.y = pos2.y,pos1.y end - if pos1.z>pos2.z then pos1.z,pos2.z = pos2.z,pos1.z end - - local size = (pos2.x-pos1.x+1)*(pos2.y-pos1.y+1)*(pos2.z-pos1.z+1) - if size > 27 then say("#setter: target area too large, more than 27 blocks!"); return end - for x = pos1.x,pos2.x do - for y = pos1.y,pos2.y do - for z = pos1.z,pos2.z do - puzzle.set_node({x=x,y=y,z=z},node1) - end - end - end - end - - local piston_displaceable_nodes = {["air"] = 1,["default:water_flowing"] = 1} - - piston_action = function(mode,pos,ttl) -- PUSH NODE AT TARGET1 AWAY FROM PISTON - if not ttl or ttl <=0 then return end - --if mode == 0 then return end - - local meta = puzzle.get_meta(pos); - if not meta then return end - local n = meta:get_int("n"); - if n < 1 or n>2 then say("#piston: error, needs to be set with at least link and most two"); return end - local pos1 = {x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z} - - -- determine direction - local dir = {x=pos1.x-pos.x, y= pos1.y-pos.y, z= pos1.z-pos.z}; - - local dirabs = {x=math.abs(dir.x), y= math.abs(dir.y), z= math.abs(dir.z)}; - local dirmax = math.max(dirabs.x,dirabs.y,dirabs.z); - - if dirabs.x == dirmax then dir = { x = dir.x>0 and 1 or -1, y = 0,z = 0 } - elseif dirabs.y == dirmax then dir = { x = 0, y = dir.y>0 and 1 or -1, z=0} - else dir = {x = 0, y = 0, z = dir.z>0 and 1 or -1} - end - - local pos2 = {x=pos1.x+dir.x,y=pos1.y+dir.y,z=pos1.z+dir.z}; - - if mode == 0 then pos1,pos2 = pos2,pos1 end - - local node1 = puzzle.get_node(pos1) - if node1.name == "air" then return end - - - if piston_displaceable_nodes[puzzle.get_node(pos2).name] then - puzzle.set_node(pos2, node1) - puzzle.set_node(pos1, {name = "air"}) - minetest.check_for_falling(pos2) - self.sound("doors_door_open",1,pos) - end - end - - platform_action = function(mode,pos,ttl) -- SPAWN MOVING PLATFORM - - if mode~=1 then return end - local meta = puzzle.get_meta(pos); - if not meta then return end - local n = meta:get_int("n"); - if n ~= 2 then say("#platform: error, needs to be set with 2 targets"); return end - local pos1 = {x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z} - local pos2 = {x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z} - - -- determine direction - local dir = {x=pos2.x-pos1.x, y= pos2.y-pos1.y, z= pos2.z-pos1.z}; - - local obj = minetest.add_entity(pos1, "basic_robot:projectile"); - - if not obj then return end - obj:setvelocity(dir); - --obj:setacceleration({x=0,y=-gravity,z=0}); - local luaent = obj:get_luaentity(); - luaent.name = name; - luaent.spawnpos = pos1; - - local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name; - local tiles = minetest.registered_nodes[nodename].tiles; tiles = tiles or {}; - local texture = tiles[1] or "default_stone"; - obj:set_properties({visual = "cube",textures = {texture,texture,texture,texture,texture,texture},visual_size = {x=1,y=1}, - collisionbox={-0.5,-0.5,-0.5,0.5,0.5,0.5}}) - end - - - -- HOW TO ACTIVATE TARGET ELEMENT - adds mesecons/basic machines compatibility - activate = function(mode, pos, ttl) - if not ttl or ttl <=0 then return end - local nodename = puzzle.get_node(pos).name; - local active_element = active_elements[nodename]; - opcount = opcount + 1 - if opcount > 64 then say("#puzzle error: opcount 64 exceeded. too many active connections."); error("#puzzle: abort") end - - if active_element then - active_element(mode,pos,ttl-1) - else -- try mesecons activate - local nodename = puzzle.get_node(pos).name - local table = minetest.registered_nodes[nodename]; - if table and table.mesecons then else return end - - local effector=table.mesecons.effector; - - if mode == 1 then - if effector.action_on then - effector.action_on(pos,node,ttl) - end - else - if effector.action_off then - effector.action_off(pos,node,ttl) - end - end - end - end - - -- THESE REACT WHEN PUNCHED - interactive_elements = { - [275] = {button_action,1}, -- BUTTON, 1 means it activates(ON) on punch - [273] = {toggle_button_action,1}, -- TOGGLE BUTTON_OFF - [274] = {toggle_button_action,0}, -- TOGGLE BUTTON_ON, 0 means it deactivates - [284] = {giver_action,0}, -- GIVER: give player item below it when activated and activate targets after that - [285] = {checker_action,0}, -- CHECKER: check if player has block below it in inventory, remove it and activate targets after that - } - - -- THESE CAN BE ACTIVATED WITH SIGNAL - active_elements = { - ["basic_robot:button_275"] = button_action, -- BUTTON, what action to do on activate - ["basic_robot:button_273"] = toggle_button_action, -- TOGGLE BUTTON_OFF - ["basic_robot:button_274"] = toggle_button_action, -- TOGGLE BUTTON_ON - ["basic_robot:button_278"] = setter_action, -- SETTER - ["basic_robot:button_277"] = equalizer_action, -- EQUALIZER - ["basic_robot:button_279"] = piston_action, -- PISTON - ["basic_robot:button_283"] = platform_action, -- PLATFORM - ["basic_robot:button_282"] = delayer_action, -- DELAYER - ["basic_robot:button_280"] = diode_action, -- DIODE - ["basic_robot:button_281"] = not_action, -- NOT - } - - - -- EDITOR CODE -- - - edit = {}; - edit.state = 1; -- tool state - edit.source = {}; edit.sourcenode = ""; -- selected source - - -- blocks that can be activated - edit.active_elements = { - ["basic_robot:button_275"] = "button: now select one or more targets", -- button - ["basic_robot:button_273"] = "switch: now select one or more targets", -- switch OFF - ["basic_robot:button_274"] = "switch: now select one or more targets", -- switch ON - ["basic_robot:button_278"] = "setter: target1 defines what material wall will use, target2/3 defines where wall will be", -- setter - ["basic_robot:button_277"] = "equalizer: target1 and target2 are for comparison, other targets are activated", -- equalizer - ["basic_robot:button_279"] = "piston: push block at target1 in direction away from piston", -- equalizer - ["basic_robot:button_283"] = "platform: select target1 to set origin, target2 for direction", -- PLATFORM - ["basic_robot:button_282"] = "delayer: distance from delayer to target1 determines delay", -- delayer - ["basic_robot:button_280"] = "diode: only pass through ON signal", -- DIODE - ["basic_robot:button_281"] = "NOT gate: negates the signal", -- NOT - - ["basic_robot:button_284"] = "GIVER: give player item below it when activated and activate targets after that", - ["basic_robot:button_285"] = "CHECKER: check if player has block below it in inventory, remove it and activate targets after that", - } - - linker_use = function(pos) - if not pos then return end - - --say(serialize(player_:get_player_control())) - if edit.state < 0 then -- link edit mode! - local meta = puzzle.get_meta(edit.source); - local i = -edit.state; - meta:set_int("x" ..i, pos.x-edit.source.x); meta:set_int("y" ..i, pos.y-edit.source.y); meta:set_int("z" ..i, pos.z-edit.source.z) - puzzle.chat_send_player(name, colorize("red", "EDIT ".. " target " .. i .. " changed")) - edit.state = 1 - goto display_particle - end - - if player_:get_player_control().sneak then -- SHOW LINKS - local meta = puzzle.get_meta(pos); - local n = meta:get_int("n"); - local nodename = puzzle.get_node(pos).name; - local active_element = edit.active_elements[nodename] - if active_element and edit.source.x == pos.x and edit.source.y == pos.y and edit.source.z == pos.z then -- gui with more info - local form = "size[4,"..(0.75*n).."] label[0,-0.25; "..active_element .."]" ; - for i = 1,n do -- add targets as lines - form = form .. - "button[0,".. (0.75*i-0.5) .. ";1,1;".."S"..i..";" .. "show " .. i .. "]".. - "button_exit[1,".. (0.75*i-0.5) .. ";1,1;".."E"..i..";" .. "edit " .. "]" .. - "button_exit[2,".. (0.75*i-0.5) .. ";1,1;".."D"..i..";" .. "delete " .. "]".. - "label[3,".. (0.75*i-0.25) .. "; " .. meta:get_int("x"..i) .. " " .. meta:get_int("y"..i) .. " " .. meta:get_int("z"..i) .."]" - end - self.show_form(name,form); - edit.state = 3; - return - end - edit.source = {x=pos.x,y=pos.y, z=pos.z}; - edit.state = 1 - if not active_element then return end - local i = string.find(active_element,":"); - if not i then return end - puzzle.chat_send_player(name,colorize("red","#INFO ".. string.sub(active_element,1,i-1) ..":") .." has " .. n .. " targets. Select again for more info.") - meta:set_string("infotext",string.sub(active_element,1,i-1)) -- write name of element on it! - - for i = 1, n do - minetest.add_particle( - { - pos = {x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z}, - expirationtime = 5, - velocity = {x=0, y=0,z=0}, - size = 18, - texture = "010.png", - acceleration = {x=0,y=0,z=0}, - collisiondetection = true, - collision_removal = true, - } - ) - end - return - end - - if edit.state == 1 then -- SET SOURCE - local nodename = puzzle.get_node(pos).name; - local active_element = edit.active_elements[nodename] - if not active_element then puzzle.chat_send_player(name,colorize("red","#ERROR linker:").. " source must be valid element like switch"); return end - edit.source = {x=pos.x,y=pos.y, z=pos.z}; - sourcenode = nodename; - puzzle.chat_send_player(name, colorize("yellow","SETUP " ..edit.state .. ": ").. active_element) - edit.state = 2 - else -- SET TARGET - local meta = puzzle.get_meta(edit.source); - local n = meta:get_int("n"); - - if edit.state == 2 and pos.x == edit.source.x and pos.y == edit.source.y and pos.z == edit.source.z then -- RESET LINK FOR SOURCE - local meta = puzzle.get_meta(pos);meta:set_int("n",0) -- reset links - puzzle.chat_send_player(name, colorize("red", "SETUP " .. edit.state .. ":") .. " resetted links for selected source.") - edit.state = 1;return - else - n=n+1; - meta:set_int("x"..n, pos.x-edit.source.x);meta:set_int("y"..n, pos.y-edit.source.y);meta:set_int("z"..n, pos.z-edit.source.z) -- relative to source! - meta:set_int("n",n) - puzzle.chat_send_player(name, colorize("red", "SETUP "..edit.state .. ":") .. " added target #" .. n) - edit.state = 1 - end - end - - -- display - ::display_particle:: - - minetest.add_particle( - { - pos = pos, - expirationtime = 5, - velocity = {x=0, y=0,z=0}, - size = 18, - texture = "009.png", - acceleration = {x=0,y=0,z=0}, - collisiondetection = true, - collision_removal = true, - } - ) - end - - tools = { - ["basic_robot:control"] = linker_use - } - - ------ END OF EDIT PROGRAM - -end - -opcount = 0 -event = keyboard.get() -- handle keyboard -if event then - if event.type == 0 then -- EDITING - if event.puncher == name then -- players in protection can edit -- not minetest.is_protected({x=event.x,y=event.y,z=event.z},event.puncher) - local wield_item = player_:get_wielded_item():get_name() - local tool = tools[wield_item] - if tool then tool({x=event.x,y=event.y,z=event.z}) end - end - else -- EMULATOR - local typ = event.type; - local interactive_element = interactive_elements[typ] - if interactive_element then - interactive_element[1](interactive_element[2],{x=event.x,y=event.y,z=event.z},TTL) - self.sound("doors_glass_door_open",1,{x=event.x,y=event.y,z=event.z}) - end - end -end - - -sender,fields = self.read_form() -- handle gui for editing -if sender then - edit.state = 1 - for k,_ in pairs(fields) do - local c = string.sub(k,1,1); - local i = tonumber(string.sub(k,2)) or 1; - if c == "S" then - - local meta = puzzle.get_meta(edit.source); - minetest.add_particle( - { - pos = {x=meta:get_int("x"..i)+edit.source.x,y=meta:get_int("y"..i)+edit.source.y,z=meta:get_int("z"..i)+edit.source.z}, - expirationtime = 5, - velocity = {x=0, y=0,z=0}, - size = 18, - texture = "010.png", - acceleration = {x=0,y=0,z=0}, - collisiondetection = true, - collision_removal = true, - } - ) - elseif c == "E" then - edit.state = -i; - puzzle.chat_send_player(name, colorize("yellow", "#EDIT: select target " .. i)); - elseif c == "D" then - local meta = puzzle.get_meta(edit.source); - local n = meta:get_int("n") - if n > 0 then - for j = i,n-1 do - meta:set_int("x"..j, meta:get_int("x"..(j+1))) - meta:set_int("y"..j, meta:get_int("y"..(j+1))) - meta:set_int("z"..j, meta:get_int("z"..(j+1))) - end - meta:set_int("n",n-1) - end - puzzle.chat_send_player(name, colorize("red", "#EDIT: target " .. i .. " deleted")); - end - --say(serialize(fields)) - end +-- REDSTONE EMULATOR & EDITOR +--v 28/06/2018a, fixed 28/08/2022a + +if not init then + dout = function(text) say(text,"rnd") end + seri = function(tab) + local out = {} for k,v in pairs(tab) do if type(v) ~= "function" then out[#out+1] = k .. " = " .. v end end + return "{"..table.concat(out,",") .."}" + end + local players = find_player(5); + if not players then + name = ""; + else + name = players[1] + player_ = puzzle.get_player(name) + local inv = player_:get_inventory(); + inv:set_stack("main", 8, puzzle.ItemStack("basic_robot:control 1 0 \"@\"")) -- add controller in players inventory + --add items for building + inv:set_stack("main", 1, puzzle.ItemStack("default:pick_diamond")) + inv:set_stack("main", 2, puzzle.ItemStack("basic_robot:button_283 999")) -- switch 9 = 283/284 + inv:set_stack("main", 3, puzzle.ItemStack("basic_robot:button_285 999")) -- button 7 = 285/286 + inv:set_stack("main", 4, puzzle.ItemStack("basic_robot:button_287 999")) -- equalizer 61 = 287 + inv:set_stack("main", 5, puzzle.ItemStack("basic_robot:button_288 999")) -- setter 15 = 288 + inv:set_stack("main", 6, puzzle.ItemStack("basic_robot:button_289 999")) -- piston 171 = 289 + inv:set_stack("main", 7, puzzle.ItemStack("basic_robot:button_292 999")) -- delayer 232 = 292 + inv:set_stack("main", 9, puzzle.ItemStack("basic_robot:button_291 999")) -- NOT 33 = 291 + inv:set_stack("main", 10, puzzle.ItemStack("basic_robot:button_290 999")) -- diode 175 = 290 + inv:set_stack("main", 11, puzzle.ItemStack("basic_robot:button_293 999")) -- platform 22 = 293 + inv:set_stack("main", 12, puzzle.ItemStack("basic_robot:button_294 999")) -- giver 23 150/294 + inv:set_stack("main", 13, puzzle.ItemStack("basic_robot:button_295 999")) -- checker 24 151/295 + + + local round = math.floor; protector_position = function(pos) local r = 32;local ry = 2*r; return {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry,z=round(pos.z/r+0.5)*r}; end + local spawnblock = protector_position(self.spawnpos()) + + local meta = puzzle.get_meta(spawnblock); + meta:set_string("shares", name) -- add player to protection! + puzzle.chat_send_player(name,colorize("yellow","#EDITOR: if you need any blocks get them by using 'give me' in craft guide. you can now use controller to make links from pointed at blocks. In addition hold SHIFT to display infos. Reset block links by selecting block 2x")) + end + + init = true + self.listen_punch(self.pos()); -- attach punch listener + self.spam(1) + self.label(colorize("orange","REDSTONE EMULATOR/EDITOR")) + + + + + -- 1. EMULATOR CODE + + + TTL = 16 -- signal propagates so many steps before dissipate + --self.label(colorize("red","REDSTONE")..colorize("yellow","EMULATOR")) + opcount = 0; + + -- DEFINITIONS OF BLOCKS THAT CAN BE ACTIVATED + toggle_button_action = function(mode,pos,ttl) -- SIMPLE TOGGLE BUTTONS - SWITCH + if not ttl or ttl <=0 then return end + if mode == 1 then -- turn on + puzzle.set_node(pos,{name = "basic_robot:button_284"}) + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); + for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + else -- turn off + puzzle.set_node(pos,{name = "basic_robot:button_283"}) + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); + for i = 1,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + end + + + button_action = function(mode,pos,ttl) -- SIMPLE ON BUTTON, TOGGLES BACK OFF after 1s + if not ttl or ttl <=0 then return end + if mode == 0 then return end + puzzle.set_node(pos,{name = "basic_robot:button_286"}) + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); + + minetest.after(1, function() + puzzle.set_node(pos,{name = "basic_robot:button_285"}) + for i = 1,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end) + for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + + giver_action = function(mode,pos,ttl) -- GIVER: give block below it to player and activate targets + local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name; + if nodename == "air" then return end + local objects = minetest.get_objects_inside_radius(pos, 5);local player1; + for _,obj in pairs(objects) do if obj:is_player() then player1 = obj; break end end + if player1 then + player1:get_inventory():add_item("main", puzzle.ItemStack(nodename)) + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); + for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + end + + checker_action = function(mode,pos,ttl) -- CHECKER: check if player has block below it, then remove block from player and activate targets + local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name; + if nodename == air then return end + local objects = minetest.get_objects_inside_radius(pos, 5);local player1; + for _,obj in pairs(objects) do if obj:is_player() then player1 = obj; break end end + if player1 then + local inv = player1:get_inventory(); + if inv:contains_item("main", puzzle.ItemStack(nodename)) then + inv:remove_item("main",puzzle.ItemStack(nodename)) + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); + for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + end + end + + equalizer_action = function(mode,pos,ttl) -- CHECK NODES AT TARGET1,TARGET2. IF EQUAL ACTIVATE TARGET3,TARGET4,... + if not ttl or ttl <=0 then return end + if mode == 0 then return end + + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); + local node1 = puzzle.get_node({x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}).name + local node2 = puzzle.get_node({x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z}).name + + + if node1==node2 then + for i = 3,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + else + for i = 3,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + end + + delayer_action = function(mode,pos,ttl) -- DELAY FORWARD SIGNAL, delay determined by distance of target1 from delayer ( in seconds) + if not ttl or ttl <=0 then return end + local meta = puzzle.get_meta(pos); + if not meta then return end + + local n = meta:get_int("n"); + local pos1 = {x=meta:get_int("x1"),y=meta:get_int("y1"),z=meta:get_int("z1")} + local delay = math.sqrt(pos1.x^2+pos1.y^2+pos1.z^2); + + if delay > 0 then + minetest.after(delay, function() + if mode == 1 then + for i = 2,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + else + for i = 2,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + end) + end + end + + diode_action = function(mode,pos,ttl) -- ONLY pass through ON signal + if not ttl or ttl <=0 then return end + if mode ~= 1 then return end + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); + for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + + not_action = function(mode,pos,ttl) -- negate signal: 0 <-> 1 + if not ttl or ttl <=0 then return end + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); + for i = 1,n do activate(1-mode,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + + setter_action = function(mode,pos,ttl) -- SETS NODES IN TARGET AREA TO PRESELECTED NODE + if not ttl or ttl <=0 then return end + if mode == 0 then return end + + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); + if n ~= 3 then say("#setter: error, needs to be set with 3 links"); return end + local node1 = puzzle.get_node({x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}) + local pos1 = {x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z} + local pos2 = {x=meta:get_int("x3")+pos.x,y=meta:get_int("y3")+pos.y,z=meta:get_int("z3")+pos.z} + + if pos1.x>pos2.x then pos1.x,pos2.x = pos2.x,pos1.x end + if pos1.y>pos2.y then pos1.y,pos2.y = pos2.y,pos1.y end + if pos1.z>pos2.z then pos1.z,pos2.z = pos2.z,pos1.z end + + local size = (pos2.x-pos1.x+1)*(pos2.y-pos1.y+1)*(pos2.z-pos1.z+1) + if size > 27 then say("#setter: target area too large, more than 27 blocks!"); return end + for x = pos1.x,pos2.x do + for y = pos1.y,pos2.y do + for z = pos1.z,pos2.z do + puzzle.set_node({x=x,y=y,z=z},node1) + end + end + end + end + + local piston_displaceable_nodes = {["air"] = 1,["default:water_flowing"] = 1} + + piston_action = function(mode,pos,ttl) -- PUSH NODE AT TARGET1 AWAY FROM PISTON + if not ttl or ttl <=0 then return end + --if mode == 0 then return end + + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); + if n < 1 or n>2 then say("#piston: error, needs to be set with at least link and most two"); return end + local pos1 = {x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z} + + -- determine direction + local dir = {x=pos1.x-pos.x, y= pos1.y-pos.y, z= pos1.z-pos.z}; + + local dirabs = {x=math.abs(dir.x), y= math.abs(dir.y), z= math.abs(dir.z)}; + local dirmax = math.max(dirabs.x,dirabs.y,dirabs.z); + + if dirabs.x == dirmax then dir = { x = dir.x>0 and 1 or -1, y = 0,z = 0 } + elseif dirabs.y == dirmax then dir = { x = 0, y = dir.y>0 and 1 or -1, z=0} + else dir = {x = 0, y = 0, z = dir.z>0 and 1 or -1} + end + + local pos2 = {x=pos1.x+dir.x,y=pos1.y+dir.y,z=pos1.z+dir.z}; + + if mode == 0 then pos1,pos2 = pos2,pos1 end + + local node1 = puzzle.get_node(pos1) + if node1.name == "air" then return end + + + if piston_displaceable_nodes[puzzle.get_node(pos2).name] then + puzzle.set_node(pos2, node1) + puzzle.set_node(pos1, {name = "air"}) + minetest.check_for_falling(pos2) + self.sound("doors_door_open",1,pos) + end + end + + platform_action = function(mode,pos,ttl) -- SPAWN MOVING PLATFORM + + if mode~=1 then return end + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); + if n ~= 2 then say("#platform: error, needs to be set with 2 targets"); return end + local pos1 = {x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z} + local pos2 = {x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z} + + -- determine direction + local dir = {x=pos2.x-pos1.x, y= pos2.y-pos1.y, z= pos2.z-pos1.z}; + + local obj = minetest.add_entity(pos1, "basic_robot:projectile"); + + if not obj then return end + obj:setvelocity(dir); + --obj:setacceleration({x=0,y=-gravity,z=0}); + local luaent = obj:get_luaentity(); + luaent.name = name; + luaent.spawnpos = pos1; + + local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name; + local tiles = minetest.registered_nodes[nodename].tiles; tiles = tiles or {}; + local texture = tiles[1] or "default_stone"; + obj:set_properties({visual = "cube",textures = {texture,texture,texture,texture,texture,texture},visual_size = {x=1,y=1}, + collisionbox={-0.5,-0.5,-0.5,0.5,0.5,0.5}}) + end + + + -- HOW TO ACTIVATE TARGET ELEMENT - adds mesecons/basic machines compatibility + activate = function(mode, pos, ttl) + if not ttl or ttl <=0 then return end + local nodename = puzzle.get_node(pos).name; + local active_element = active_elements[nodename]; + opcount = opcount + 1 + if opcount > 64 then say("#puzzle error: opcount 64 exceeded. too many active connections."); error("#puzzle: abort") end + + if active_element then + active_element(mode,pos,ttl-1) + else -- try mesecons activate + local nodename = puzzle.get_node(pos).name + local table = minetest.registered_nodes[nodename]; + if table and table.mesecons then else return end + + local effector=table.mesecons.effector; + + if mode == 1 then + if effector.action_on then + effector.action_on(pos,node,ttl) + end + else + if effector.action_off then + effector.action_off(pos,node,ttl) + end + end + end + end + + -- THESE REACT WHEN PUNCHED + interactive_elements = { + [285] = {button_action,1}, -- BUTTON, 1 means it activates(ON) on punch + [283] = {toggle_button_action,1}, -- TOGGLE BUTTON_OFF + [284] = {toggle_button_action,0}, -- TOGGLE BUTTON_ON, 0 means it deactivates + [294] = {giver_action,0}, -- GIVER: give player item below it when activated and activate targets after that + [295] = {checker_action,0}, -- CHECKER: check if player has block below it in inventory, remove it and activate targets after that + } + + -- THESE CAN BE ACTIVATED WITH SIGNAL + active_elements = { + ["basic_robot:button_285"] = button_action, -- BUTTON, what action to do on activate + ["basic_robot:button_283"] = toggle_button_action, -- TOGGLE BUTTON_OFF + ["basic_robot:button_284"] = toggle_button_action, -- TOGGLE BUTTON_ON + ["basic_robot:button_288"] = setter_action, -- SETTER + ["basic_robot:button_287"] = equalizer_action, -- EQUALIZER + ["basic_robot:button_289"] = piston_action, -- PISTON + ["basic_robot:button_293"] = platform_action, -- PLATFORM + ["basic_robot:button_292"] = delayer_action, -- DELAYER + ["basic_robot:button_290"] = diode_action, -- DIODE + ["basic_robot:button_291"] = not_action, -- NOT + } + + + -- EDITOR CODE -- + + edit = {}; + edit.state = 1; -- tool state + edit.source = {}; edit.sourcenode = ""; -- selected source + + -- blocks that can be activated + edit.active_elements = { + ["basic_robot:button_285"] = "button: now select one or more targets", -- button + ["basic_robot:button_283"] = "switch: now select one or more targets", -- switch OFF + ["basic_robot:button_284"] = "switch: now select one or more targets", -- switch ON + ["basic_robot:button_288"] = "setter: target1 defines what material wall will use, target2/3 defines where wall will be", -- setter + ["basic_robot:button_287"] = "equalizer: target1 and target2 are for comparison, other targets are activated", -- equalizer + ["basic_robot:button_289"] = "piston: push block at target1 in direction away from piston", -- equalizer + ["basic_robot:button_293"] = "platform: select target1 to set origin, target2 for direction", -- PLATFORM + ["basic_robot:button_292"] = "delayer: distance from delayer to target1 determines delay", -- delayer + ["basic_robot:button_290"] = "diode: only pass through ON signal", -- DIODE + ["basic_robot:button_291"] = "NOT gate: negates the signal", -- NOT + + ["basic_robot:button_294"] = "GIVER: give player item below it when activated and activate targets after that", + ["basic_robot:button_295"] = "CHECKER: check if player has block below it in inventory, remove it and activate targets after that", + } + + linker_use = function(pos) + if not pos then return end + + --say(serialize(player_:get_player_control())) + if edit.state < 0 then -- link edit mode! + local meta = puzzle.get_meta(edit.source); + local i = -edit.state; + meta:set_int("x" ..i, pos.x-edit.source.x); meta:set_int("y" ..i, pos.y-edit.source.y); meta:set_int("z" ..i, pos.z-edit.source.z) + puzzle.chat_send_player(name, colorize("red", "EDIT ".. " target " .. i .. " changed")) + edit.state = 1 + goto display_particle + end + + if player_:get_player_control().sneak then -- SHOW LINKS + local meta = puzzle.get_meta(pos); + local n = meta:get_int("n"); + local nodename = puzzle.get_node(pos).name; + local active_element = edit.active_elements[nodename] + if active_element and edit.source.x == pos.x and edit.source.y == pos.y and edit.source.z == pos.z then -- gui with more info + local form = "size[5,"..(0.75*n).."] label[0,-0.25; "..active_element .."]" ; + for i = 1,n do -- add targets as lines + form = form .. + "button[0,".. (0.75*i-0.5) .. ";1.25,1;".."S"..i..";" .. "show " .. i .. "]".. + "button_exit[1,".. (0.75*i-0.5) .. ";1,1;".."E"..i..";" .. "edit " .. "]" .. + "button_exit[2,".. (0.75*i-0.5) .. ";1.25,1;".."D"..i..";" .. "delete " .. "]".. + "label[3,".. (0.75*i-0.25) .. "; " .. meta:get_int("x"..i) .. " " .. meta:get_int("y"..i) .. " " .. meta:get_int("z"..i) .."]" + end + self.show_form(name,form); + edit.state = 3; + return + end + edit.source = {x=pos.x,y=pos.y, z=pos.z}; + edit.state = 1 + if not active_element then return end + local i = string.find(active_element,":"); + if not i then return end + puzzle.chat_send_player(name,colorize("red","#INFO ".. string.sub(active_element,1,i-1) ..":") .." has " .. n .. " targets. Select again for more info.") + meta:set_string("infotext",string.sub(active_element,1,i-1)) -- write name of element on it! + + for i = 1, n do + minetest.add_particle( + { + pos = {x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z}, + expirationtime = 5, + velocity = {x=0, y=0,z=0}, + size = 18, + texture = "puzzle_button_on.png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + } + ) + end + return + end + + if edit.state == 1 then -- SET SOURCE + local nodename = puzzle.get_node(pos).name; + local active_element = edit.active_elements[nodename] + if not active_element then puzzle.chat_send_player(name,colorize("red","#ERROR linker:").. " source must be valid element like switch"); return end + edit.source = {x=pos.x,y=pos.y, z=pos.z}; + sourcenode = nodename; + puzzle.chat_send_player(name, colorize("yellow","SETUP " ..edit.state .. ": ").. active_element) + edit.state = 2 + else -- SET TARGET + local meta = puzzle.get_meta(edit.source); + local n = meta:get_int("n"); + + if edit.state == 2 and pos.x == edit.source.x and pos.y == edit.source.y and pos.z == edit.source.z then -- RESET LINK FOR SOURCE + local meta = puzzle.get_meta(pos);meta:set_int("n",0) -- reset links + puzzle.chat_send_player(name, colorize("red", "SETUP " .. edit.state .. ":") .. " resetted links for selected source.") + edit.state = 1;return + else + n=n+1; + meta:set_int("x"..n, pos.x-edit.source.x);meta:set_int("y"..n, pos.y-edit.source.y);meta:set_int("z"..n, pos.z-edit.source.z) -- relative to source! + meta:set_int("n",n) + puzzle.chat_send_player(name, colorize("red", "SETUP "..edit.state .. ":") .. " added target #" .. n) + edit.state = 1 + end + end + + -- display + ::display_particle:: + + minetest.add_particle( + { + pos = pos, + expirationtime = 5, + velocity = {x=0, y=0,z=0}, + size = 18, + texture = "puzzle_button_off.png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + } + ) + end + + tools = { + ["basic_robot:control"] = linker_use + } + + ------ END OF EDIT PROGRAM + +end + +opcount = 0 +event = keyboard.get() -- handle keyboard + +if event and not player_:get_player_control().sneak then + if event.type == 0 then -- EDITING + if event.puncher == name then -- players in protection can edit -- not minetest.is_protected({x=event.x,y=event.y,z=event.z},event.puncher) + local wield_item = player_:get_wielded_item():get_name() + local tool = tools[wield_item] + if tool then tool({x=event.x,y=event.y,z=event.z}) end + end + else -- EMULATOR + local typ = event.type; + local interactive_element = interactive_elements[typ] + if interactive_element then + interactive_element[1](interactive_element[2],{x=event.x,y=event.y,z=event.z},TTL) + self.sound("doors_glass_door_open",1,{x=event.x,y=event.y,z=event.z}) + end + end +end + + +sender,fields = self.read_form() -- handle gui for editing +if sender then + edit.state = 1 + for k,_ in pairs(fields) do + local c = string.sub(k,1,1); + local i = tonumber(string.sub(k,2)) or 1; + if c == "S" then + + local meta = puzzle.get_meta(edit.source); + minetest.add_particle( + { + pos = {x=meta:get_int("x"..i)+edit.source.x,y=meta:get_int("y"..i)+edit.source.y,z=meta:get_int("z"..i)+edit.source.z}, + expirationtime = 5, + velocity = {x=0, y=0,z=0}, + size = 18, + texture = "puzzle_button_on.png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + } + ) + elseif c == "E" then + edit.state = -i; + puzzle.chat_send_player(name, colorize("yellow", "#EDIT: select target " .. i)); + elseif c == "D" then + local meta = puzzle.get_meta(edit.source); + local n = meta:get_int("n") + if n > 0 then + for j = i,n-1 do + meta:set_int("x"..j, meta:get_int("x"..(j+1))) + meta:set_int("y"..j, meta:get_int("y"..(j+1))) + meta:set_int("z"..j, meta:get_int("z"..(j+1))) + end + meta:set_int("n",n-1) + end + puzzle.chat_send_player(name, colorize("red", "#EDIT: target " .. i .. " deleted")); + end + --say(serialize(fields)) + end end \ No newline at end of file diff --git a/scripts/utils/copy_paste.lua b/scripts/utils/copy_paste.lua new file mode 100644 index 0000000..1987b0e --- /dev/null +++ b/scripts/utils/copy_paste.lua @@ -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(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 \ No newline at end of file diff --git a/scripts/utils/object_lister.lua b/scripts/utils/object_lister.lua index 40b6e90..2615cef 100644 --- a/scripts/utils/object_lister.lua +++ b/scripts/utils/object_lister.lua @@ -1,41 +1,45 @@ --- minetest object listen in radius 100 around robot - -if not init then init = false - -local objs = minetest.get_objects_inside_radius(self.pos(), 100); -local ret = {}; - -local round = function(x) return math.floor(x/5)*5 end -local ret = {}; - -for i = 1, #objs do - local p = objs[i]:get_pos(); - local luaent = objs[i]:get_luaentity(); - local entname = "" - if luaent then - entname = luaent.itemstring - if entname == "robot" then entname = entname .. " " .. luaent.name end - elseif objs[i]:is_player() then - entname = "PLAYER " .. objs[i]:get_player_name() - end - - local phash = round(p.x) .. " " .. round(p.y) .. " " .. round(p.z); - ret[phash] = (ret[phash] or "") .. entname .. ", " -end - -local out = {}; -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")) - - - +-- minetest object listen in radius 100 around robot + +if not init then init = false + +local objs = minetest.get_objects_inside_radius(self.pos(), 100); +local ret = {}; + +local round = function(x) return math.floor(x/5)*5 end +local ret = {}; + +for i = 1, #objs do + local p = objs[i]:get_pos(); + local luaent = objs[i]:get_luaentity(); + local entname = "" + if luaent then + --entname = serialize(luaent) + entname = luaent.itemstring + if entname == "robot" then entname = entname .. " " .. luaent.name end + elseif objs[i]:is_player() then + entname = "PLAYER " .. objs[i]:get_player_name() + end + + local phash = round(p.x) .. " " .. round(p.y) .. " " .. round(p.z); + if entname then ret[phash] = (ret[phash] or "") .. entname .. ", " end +end + +local out = {}; +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 \ No newline at end of file diff --git a/scripts/utils/text_printer.lua b/scripts/utils/text_printer.lua index 9f2d8ae..3a93b4f 100644 --- a/scripts/utils/text_printer.lua +++ b/scripts/utils/text_printer.lua @@ -3,7 +3,7 @@ -- say: t TEXT\nTEXT... if not init then init = true - name = "_" + names = {["rnd"]=true,["PrairieWind"] = true} get_dir = function(view) local dir @@ -15,7 +15,7 @@ if not init then init = true return dir end - render_text = function(text) + 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()) @@ -37,7 +37,7 @@ if not init then init = true end speaker,msg = self.listen_msg() -if speaker == name and string.sub(msg,1,2) == "t " then +if names[speaker] and string.sub(msg,1,2) == "t " then - render_text(string.sub(msg,3)) + render_text(string.sub(msg,3),speaker) end \ No newline at end of file diff --git a/scripts/web/https_server.py b/scripts/web/https_server.py new file mode 100644 index 0000000..32c99a6 --- /dev/null +++ b/scripts/web/https_server.py @@ -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() \ No newline at end of file diff --git a/scripts/web/index.html b/scripts/web/index.html new file mode 100644 index 0000000..7f75794 --- /dev/null +++ b/scripts/web/index.html @@ -0,0 +1,16 @@ + + + + +

rnd's web robot

+ +
+
+
+ +
+ + + + + \ No newline at end of file diff --git a/scripts/web/irc bot.lua b/scripts/web/irc bot.lua new file mode 100644 index 0000000..bb60c7e --- /dev/null +++ b/scripts/web/irc bot.lua @@ -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 on skyblock +end + + +self.label(_G.basic_robot.ircmsg or "") \ No newline at end of file diff --git a/scripts/web/run_server.bat b/scripts/web/run_server.bat new file mode 100644 index 0000000..ab13fc9 --- /dev/null +++ b/scripts/web/run_server.bat @@ -0,0 +1 @@ +D:\prog\programming\Python\Python37-32\python https_server.py \ No newline at end of file diff --git a/settings.lua b/settings.lua new file mode 100644 index 0000000..9061f2e --- /dev/null +++ b/settings.lua @@ -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(); \ No newline at end of file diff --git a/textures/robochars.png b/textures/robochars.png new file mode 100644 index 0000000..4d75d3e Binary files /dev/null and b/textures/robochars.png differ