From 79bbe6c2affedb76e8d7752b50881eee6bd82bde Mon Sep 17 00:00:00 2001 From: ac-minetest Date: Thu, 7 Jan 2021 11:30:01 +0100 Subject: [PATCH] fixes and new scripts --- commands.lua | 94 +- init.lua | 80 +- robogui.lua | 49 +- scripts/copy_paste.lua | 115 +- scripts/games/battle_minesweeper_game.lua | 2 + scripts/games/checkers.lua | 117 ++ scripts/games/connect4.lua | 12 +- scripts/games/fallout_hacking.lua | 46 +- scripts/games/go.lua | 94 ++ scripts/games/hide_and_seek_new.lua | 165 ++ scripts/games/high_scores_for_games | 23 + scripts/games/mensch_argere_dich_nicht.lua | 217 +-- scripts/games/minesweeper_game.lua | 169 +- scripts/games/nonogram.lua | 509 +++--- scripts/games/simple_layout_gen.lua | 24 + scripts/games/sokoban.txt | 1398 +++++++++++++++++ scripts/games/sokoban_game.lua | 420 ++--- scripts/music/music_playback.lua | 121 ++ .../programming/hash_table_implementation.lua | 108 ++ scripts/programming/perm2cycles.lua | 83 + scripts/programming/radix_sort.lua | 104 ++ scripts/programming/room_finder.lua | 54 + scripts/utils/helper_chat_bot.lua | 92 +- scripts/utils/helper_chat_bot2.lua | 300 ++++ scripts/utils/text_printer.lua | 43 + scripts/utils/text_width_formatter.lua | 74 + 26 files changed, 3777 insertions(+), 736 deletions(-) create mode 100644 scripts/games/checkers.lua create mode 100644 scripts/games/go.lua create mode 100644 scripts/games/hide_and_seek_new.lua create mode 100644 scripts/games/high_scores_for_games create mode 100644 scripts/games/simple_layout_gen.lua create mode 100644 scripts/games/sokoban.txt create mode 100644 scripts/music/music_playback.lua create mode 100644 scripts/programming/hash_table_implementation.lua create mode 100644 scripts/programming/perm2cycles.lua create mode 100644 scripts/programming/radix_sort.lua create mode 100644 scripts/programming/room_finder.lua create mode 100644 scripts/utils/helper_chat_bot2.lua create mode 100644 scripts/utils/text_printer.lua create mode 100644 scripts/utils/text_width_formatter.lua diff --git a/commands.lua b/commands.lua index 14c2c22..92c9296 100644 --- a/commands.lua +++ b/commands.lua @@ -19,7 +19,7 @@ local pi = math.pi; local function pos_in_dir(obj, dir) -- position after we move in specified direction local yaw = obj:getyaw(); - local pos = obj:getpos(); + local pos = obj:get_pos(); if dir == 1 then -- left yaw = yaw + pi/2; @@ -88,7 +88,7 @@ basic_robot.commands.move = function(name,dir) return false end - obj:moveto(pos, true) + obj:move_to(pos, true) -- sit and stand up for model - doesnt work for overwriten obj export -- if dir == 5 then-- up @@ -113,7 +113,8 @@ end basic_robot.digcosts = { -- 1 energy = 1 coal ["default:stone"] = 1/25, - ["default:cloud"] = 10^8, + ["gloopblocks:pumice_cooled"] = 1/25, + ["default:cloud"] = 10^9, } @@ -273,7 +274,7 @@ basic_robot.commands.pickup = function(r,name) if r>8 then return false end check_operations(name,4,true) - local pos = basic_robot.data[name].obj:getpos(); + local pos = basic_robot.data[name].obj:get_pos(); local spos = basic_robot.data[name].spawnpos; -- position of spawner block local meta = minetest.get_meta(spos); local inv = minetest.get_meta(spos):get_inventory(); @@ -382,8 +383,8 @@ basic_robot.commands.attack = function(name, target) -- attack range 4, damage 5 local tplayer = minetest.get_player_by_name(target); if not tplayer then return false end local obj = basic_robot.data[name].obj; - local pos = obj:getpos(); - local tpos = tplayer:getpos(); + local pos = obj:get_pos(); + local tpos = tplayer:get_pos(); if math.abs(pos.x-tpos.x)> reach or math.abs(pos.y-tpos.y)> reach or math.abs(pos.z-tpos.z)> reach then return false @@ -400,8 +401,8 @@ basic_robot.commands.grab = function(name,target) local tplayer = minetest.get_player_by_name(target); if not tplayer then return false end local obj = basic_robot.data[name].obj; - local pos = obj:getpos(); - local tpos = tplayer:getpos(); + local pos = obj:get_pos(); + local tpos = tplayer:get_pos(); if math.abs(pos.x-tpos.x)> reach or math.abs(pos.y-tpos.y)> reach or math.abs(pos.z-tpos.z)> reach then return false @@ -444,8 +445,9 @@ basic_robot.commands.write_book = function(name,title,text) -- returns itemstack local data = {} if title == "" or not title then title = "program book "..minetest.get_gametime() end - data.title = title or "" - data.text = text or "" + if text == "" or not text then text = "empty" end + data.text = text + data.title = title data.text_len = #data.text data.page = 1 data.description = title or "" @@ -568,6 +570,31 @@ basic_robot.commands.activate = function(name,mode, dir) return true end +local write_keyevent = function(data,pos, puncher,type) + local keyevent = data.keyevent; + if not keyevent then -- init + data.keyevent = {5,1,1,{}} -- how many events buffer holds, input idx, output idx, buffer data + keyevent = data.keyevent; + end + + local iidx = keyevent[2]; -- input idx + -- write event at input idx + keyevent[4][iidx] = {x=pos.x,y=pos.y,z=pos.z, puncher = puncher, type = type} + + local oidx = keyevent[3]; -- output idx + + iidx=iidx+1; if iidx>keyevent[1] then iidx = 1 end -- advance idx for next input write + keyevent[2] = iidx + + if iidx == oidx then -- old event was overwritten, lets increase output idx + oidx = oidx+1 + if oidx>keyevent[1] then oidx = 1 end + keyevent[3] = oidx + end + + +end + local register_robot_button = function(R,G,B,type) minetest.register_node("basic_robot:button"..R..G..B, @@ -587,7 +614,9 @@ local register_robot_button = function(R,G,B,type) local meta = minetest.get_meta(ppos); local name = meta:get_string("name"); local data = basic_robot.data[name]; - if data then data.keyboard = {x=pos.x,y=pos.y,z=pos.z, puncher = player:get_player_name(), type = type} end + if data then + write_keyevent(data,pos, player:get_player_name(),type) + end end }) @@ -600,6 +629,7 @@ minetest.register_node("basic_robot:button"..number, tiles = {"robot_button".. number .. ".png"}, inventory_image = "robot_button".. number .. ".png", wield_image = "robot_button".. number .. ".png", + paramtype2 = "facedir", is_ground_content = false, groups = {cracky=3}, @@ -611,7 +641,9 @@ minetest.register_node("basic_robot:button"..number, local meta = minetest.get_meta(ppos); local name = meta:get_string("name"); local data = basic_robot.data[name]; - if data then data.keyboard = {x=pos.x,y=pos.y,z=pos.z, puncher = player:get_player_name(), type = type} end + if data then + write_keyevent(data,pos, player:get_player_name(),type) + end end }) end @@ -626,6 +658,7 @@ minetest.register_node("basic_robot:button_"..number, wield_image = string.format("%03d",number).. ".png", is_ground_content = false, groups = {cracky=3}, + paramtype2 = "facedir", on_punch = function(pos, node, player) local name = player:get_player_name(); if name==nil then return end local round = math.floor; @@ -634,7 +667,10 @@ minetest.register_node("basic_robot:button_"..number, local meta = minetest.get_meta(ppos); local name = meta:get_string("name"); local data = basic_robot.data[name]; - if data then data.keyboard = {x=pos.x,y=pos.y,z=pos.z, puncher = player:get_player_name(), type = type} end + if data then + write_keyevent(data,pos, player:get_player_name(),type) + --data.keyboard = {x=pos.x,y=pos.y,z=pos.z, puncher = player:get_player_name(), type = type} + end end }) end @@ -656,7 +692,10 @@ minetest.register_node("basic_robot:button_"..number, local meta = minetest.get_meta(ppos); local name = meta:get_string("name"); local data = basic_robot.data[name]; - if data then data.keyboard = {x=pos.x,y=pos.y,z=pos.z, puncher = player:get_player_name(), type = number} end + if data then + write_keyevent(data,pos, player:get_player_name(),type) + --data.keyboard = {x=pos.x,y=pos.y,z=pos.z, puncher = player:get_player_name(), type = number} + end end }) end @@ -693,14 +732,21 @@ register_robot_button_custom(285,"puzzle_checker") -- interactive button for robot: place robot on top of protector to intercept events +local dout = minetest.chat_send_all + basic_robot.commands.keyboard = { - get = function(name) + get = function(name) -- read keyboard event local data = basic_robot.data[name]; - if data.keyboard then - local keyboard = data.keyboard; - local event = {x=keyboard.x,y=keyboard.y,z=keyboard.z, puncher = keyboard.puncher, type = keyboard.type}; - data.keyboard = nil; + if data.keyevent then + local keyevent = data.keyevent; + local oidx = keyevent[3]; + local iidx = keyevent[2]; + if oidx == iidx then return nil end --just read past last written event, nothing to read anymore + local event = keyevent[4][oidx]; + -- move oidx (read position ) to newer event + oidx = oidx+1; if oidx>keyevent[1] then oidx = 1 end + keyevent[3] = oidx return event else return nil @@ -829,7 +875,7 @@ end --pathfinding: find_path, walk_path basic_robot.commands.find_path = function(name,pos2) local obj = basic_robot.data[name].obj; - local pos1 = obj:getpos(); + local pos1 = obj:get_pos(); if (pos1.x-pos2.x)^2+(pos1.y-pos2.y)^2+(pos1.z-pos2.z)^2> 50^2 then return nil,"2: distance too large" @@ -869,7 +915,7 @@ basic_robot.commands.walk_path = function(name) local pos2 = path[idx] local obj = basic_robot.data[name].obj; - local pos1 = obj:getpos(); + local pos1 = obj:get_pos(); local ndist = (pos1.x-pos2.x)^2+(pos1.y-pos2.y)^2+(pos1.z-pos2.z)^2 if ndist> 4 then return -ndist end -- too far away from next node @@ -1304,7 +1350,7 @@ local cmd_get_player = function(data,pname) -- return player for further manipul local player = minetest.get_player_by_name(pname) if not player then error("player does not exist"); return end local spos = data.spawnpos; - local ppos = player:getpos(); + local ppos = player:get_pos(); if not is_same_block(ppos,spos) then error("can not get player in another protection zone") return end return player end @@ -1313,7 +1359,7 @@ local cmd_get_player_inv = function(data,pname) local player = minetest.get_player_by_name(pname) if not player then return end local spos = data.spawnpos; - local ppos = player:getpos(); + local ppos = player:get_pos(); if not is_same_block(ppos,spos) then error("can not get player in another protection zone") return end return player:get_inventory(); end @@ -1472,7 +1518,7 @@ function Vplayer:new(name) -- constructor end -- functions - function Vplayer:getpos() return self.obj:getpos() end + function Vplayer:get_pos() return self.obj:get_pos() end function Vplayer:remove() end function Vplayer:setpos() end function Vplayer:move_to() end diff --git a/init.lua b/init.lua index 2b5b0b8..867479f 100644 --- a/init.lua +++ b/init.lua @@ -26,7 +26,7 @@ basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes invento basic_robot.http_api = minetest.request_http_api(); -basic_robot.version = "2020/10/14a"; +basic_robot.version = "2020/11/27a"; basic_robot.gui = {}; local robogui = basic_robot.gui -- gui management basic_robot.data = {}; -- stores all robot related data @@ -69,12 +69,12 @@ function getSandboxEnv (name) boost = function(v) if math.abs(v)>2 then v = 0 end; local obj = basic_robot.data[name].obj; if v == 0 then - local pos = obj:getpos(); pos.x = math.floor(pos.x+0.5);pos.y = math.floor(pos.y+0.5); pos.z = math.floor(pos.z+0.5); + local pos = obj:get_pos(); pos.x = math.floor(pos.x+0.5);pos.y = math.floor(pos.y+0.5); pos.z = math.floor(pos.z+0.5); obj:setpos(pos); obj:set_velocity({x=0,y=0,z=0}); return end local yaw = obj:get_yaw(); - obj:set_velocity({x=v*math.cos(yaw),y=0,z=v*math.sin(yaw)}); + obj:set_velocity({x=-v*math.sin(yaw),y=0,z=v*math.cos(yaw)}); end, turn = { @@ -97,7 +97,7 @@ function getSandboxEnv (name) end, self = { - pos = function() return basic_robot.data[name].obj:getpos() end, + pos = function() return basic_robot.data[name].obj:get_pos() end, spawnpos = function() local pos = basic_robot.data[name].spawnpos; return {x=pos.x,y=pos.y,z=pos.z} end, name = function() return name end, operations = function() return basic_robot.data[name].operations end, @@ -171,7 +171,7 @@ function getSandboxEnv (name) fire = function(speed, pitch,gravity, texture, is_entity) -- experimental: fires an projectile local obj = basic_robot.data[name].obj; - local pos = obj:getpos(); + local pos = obj:get_pos(); local yaw = obj:getyaw()+ math.pi/2; pitch = pitch*math.pi/180 local velocity = {x=speed*math.cos(yaw)*math.cos(pitch), y=speed*math.sin(pitch),z=speed*math.sin(yaw)*math.cos(pitch)}; @@ -253,16 +253,16 @@ function getSandboxEnv (name) find_nodes = function(nodename,r) if r>8 then return false end - local q = minetest.find_node_near(basic_robot.data[name].obj:getpos(), r, nodename); + local q = minetest.find_node_near(basic_robot.data[name].obj:get_pos(), r, nodename); if q==nil then return false end - local p = basic_robot.data[name].obj:getpos() + local p = basic_robot.data[name].obj:get_pos() return math.sqrt((p.x-q.x)^2+(p.y-q.y)^2+(p.z-q.z)^2) end, -- in radius around position find_player = function(r,pos) - pos = pos or basic_robot.data[name].obj:getpos(); - if r>10 then return false end + pos = pos or basic_robot.data[name].obj:get_pos(); + if r<0 or r>10 then return false end local objects = minetest.get_objects_inside_radius(pos, r); local plist = {}; for _,obj in pairs(objects) do @@ -277,7 +277,7 @@ function getSandboxEnv (name) player = { getpos = function(name) local player = minetest.get_player_by_name(name); - if player then return player:getpos() else return nil end + if player then return player:get_pos() else return nil end end, connected = function() @@ -536,7 +536,7 @@ function getSandboxEnv (name) set_triggers = function(triggers) commands.puzzle.set_triggers(pdata,triggers) end, -- FIX THIS! check_triggers = function(pname) local player = minetest.get_player_by_name(pname); if not player then return end - commands.puzzle.checkpos(pdata,player:getpos(),pname) + commands.puzzle.checkpos(pdata,player:get_pos(),pname) end, add_particle = function(def) minetest.add_particle(def) end, count_objects = function(pos,radius) return #minetest.get_objects_inside_radius(pos, math.min(radius,5)) end, @@ -832,7 +832,7 @@ local robot_spawner_update_form = function (pos, mode) "size[9.5,8]" .. -- width, height "textarea[1.25,-0.25;8.75,9.8;code;;".. code.."]".. "button[-0.25,7.5;1.25,1;EDIT;EDIT]".. - "button_exit[-0.25,-0.25;1.25,1;OK;SAVE]".. + "button[-0.25,-0.25;1.25,1;OK;SAVE]".. "button_exit[-0.25, 0.75;1.25,1;spawn;START]".. "button[-0.25, 1.75;1.25,1;despawn;STOP]".. "field[0.25,3.;1.,1;id;id;"..id.."]".. @@ -882,10 +882,10 @@ code_edit_form = function(pos,name) local list = ""; for _,line in pairs(lines) do list = list .. minetest.formspec_escape(line) .. "," end local form = "size[12,9.25]" .. "textlist[0,0;12,8;listname;" .. list .. ";"..selection..";false]" .. - "button[10,8;2,1;INSERT;INSERT LINE]" .. - "button[10,8.75;2,1;DELETE;DELETE LINE]" .. - "button_exit[2,8.75;2,1;SAVE;SAVE CODE]" .. - "button[0,8.75;2,1;UPDATE;UPDATE LINE]".. + "button[10,8;2.25,1;INSERT;INSERT LINE]" .. + "button[10,8.75;2.25,1;DELETE;DELETE LINE]" .. + "button_exit[2.25,8.75;2.25,1;SAVE;SAVE CODE]" .. + "button[0,8.75;2.25,1;UPDATE;UPDATE LINE] label[4.25,9; LINE " .. selection .."]".. "textarea[0.25,8;10,1;input;;".. input .. "]" return form end @@ -949,6 +949,11 @@ minetest.register_entity("basic_robot:robot",{ return; end + if data.object and data.object~=self.object then -- is there another one already? + self.object:remove(); + return; + end + self.owner = data.owner; self.authlevel = data.authlevel; @@ -1099,7 +1104,7 @@ local spawn_robot = function(pos,node,ttl) if not data.obj then --create virtual robot that reports position and other properties local obj = {}; - function obj:getpos() return {x=pos.x,y=pos.y,z=pos.z} end + function obj:get_pos() return {x=pos.x,y=pos.y,z=pos.z} end function obj:getyaw() return 0 end function obj:get_luaentity() local luaent = {}; @@ -1280,6 +1285,8 @@ local on_receive_robot_form = function(pos, formname, fields, sender) return end end + + minetest.chat_send_player(name,"#ROBOT: code saved " .. os.date("%H:%M:%S")) meta:set_string("code", code); meta:set_int("codechange",1) end @@ -1663,8 +1670,11 @@ minetest.register_node("basic_robot:spawner", { param1=1, walkable = true, alpha = 150, + after_place_node = function(pos, placer) - local meta = minetest.env:get_meta(pos) + + local meta = minetest.get_meta(pos) + --local meta = minetest.env:get_meta(pos) -- THIS WORKS, DOES CHANGING THIS CAUSE META PROBLEM? local owner = placer:get_player_name(); meta:set_string("owner", owner); @@ -1681,11 +1691,38 @@ minetest.register_node("basic_robot:spawner", { meta:set_string("infotext", "robot spawner (owned by ".. placer:get_player_name() .. ")") meta:set_string("libpos",pos.x .. " " .. pos.y .. " " .. pos.z) + local inv = meta:get_inventory(); -- spawner inventory + inv:set_size("main",32); + inv:set_size("library",16); --4*4 + robot_spawner_update_form(pos); + --[[ + local meta = minetest:get_meta(pos) + local owner = placer:get_player_name(); + meta:set_string("owner", owner); + + local authlevel = get_authlevel(placer:get_player_name()); + + meta:set_int("authlevel",authlevel) + local sec_hash = minetest.get_password_hash("",authlevel .. owner .. basic_robot.password) -- 'digitally sign' authlevel using password + meta:set_string("sec_hash", sec_hash); + + meta:set_string("code",""); + meta:set_int("id",1); -- initial robot id + meta:set_string("name", placer:get_player_name().."1") + + meta:set_string("infotext", "robot spawner (owned by ".. placer:get_player_name() .. ")") + + --WTF: here it reads correct meta but later there is none!! + minetest.chat_send_all("D meta info " .. meta:get_string("infotext")) + + meta:set_string("libpos",pos.x .. " " .. pos.y .. " " .. pos.z) local inv = meta:get_inventory(); -- spawner inventory inv:set_size("main",32); inv:set_size("library",16); --4*4 + robot_spawner_update_form(pos); + --]] end, mesecons = {effector = { @@ -1718,6 +1755,7 @@ minetest.register_node("basic_robot:spawner", { end, can_dig = function(pos, player) + if not player then return end if minetest.is_protected(pos, player:get_player_name()) then return false end local meta = minetest.get_meta(pos); if not meta:get_inventory():is_empty("main") or not meta:get_inventory():is_empty("library") then return false end @@ -1831,9 +1869,9 @@ minetest.register_craftitem("basic_robot:control", { minetest.chat_send_player(owner, "#remote control: compile error " .. CompileError ) return end + script = preprocess_code(script,basic_robot.call_limit[data.authlevel+1]); setfenv( ScriptFunc, basic_robot.data[name].sandbox ) - local Result, RuntimeError = pcall( ScriptFunc ); if RuntimeError then minetest.chat_send_player(owner, "#remote control: run error " .. RuntimeError ) @@ -1873,7 +1911,7 @@ minetest.register_entity( if (self.oldvel.x~=0 and vel.x==0) or (self.oldvel.y~=0 and vel.y==0) or (self.oldvel.z~=0 and vel.z==0) then -- hit local data = basic_robot.data[self.name]; if data then - data.fire_pos = self.object:getpos(); + data.fire_pos = self.object:get_pos(); end self.object:remove() return @@ -1888,7 +1926,7 @@ minetest.register_entity( if not self.state then return nil end local data = basic_robot.data[self.name]; if data then - data.fire_pos = self.object:getpos(); + data.fire_pos = self.object:get_pos(); end self.object:remove(); return nil diff --git a/robogui.lua b/robogui.lua index 6c236b4..b448d87 100644 --- a/robogui.lua +++ b/robogui.lua @@ -105,7 +105,7 @@ minetest.register_on_player_receive_fields( -- }) -- end -local help_address = {}; -- array containing current page name for player +local help_address = {}; -- table containing current page name for player [name] = {address = ..., sel = ..} local help_pages = { ["main"] = { " === ROBOT HELP - MAIN SCREEN === ","", @@ -201,7 +201,7 @@ local help_pages = { " attack(target) attempts to attack target player if nearby", " grab(target) attempt to grab target player if nearby and returns", " true if succesful", - " player.getpos(name) return position of player, player.connected()", + " player.get_pos(name) return position of player, player.connected()", " returns list of connected players names", }, @@ -328,15 +328,18 @@ end local robot_show_help = function(pname) --formname: robot_help - local address = help_address[pname] or "main"; + local data = help_address[pname]; + if not data then help_address[pname] = {address = "main", sel = 1} data = help_address[pname] end + local address = data.address; --minetest.chat_send_all("D DISPLAY HELP for ".. address ) local pages = help_pages[address]; - + if not pages then return end + local content = table.concat(pages,",") - local size = 9; local vsize = 8.75; + local size = 9.5; local vsize = 8.75; - local form = "size[" .. size .. "," .. size .. "] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]"; + local form = "size[" .. size .. "," .. size .. "] button[-0.25,".. size-0.15 ..";1,0.5;go;go] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+0.25) .. ";wiki;".. content .. ";1]"; --minetest.chat_send_all("D " .. form) minetest.show_formspec(pname, "robot_help", form) return @@ -346,19 +349,31 @@ end robogui["robot_help"] = { response = function(player,formname,fields) local name = player:get_player_name() - local fsel = fields.wiki; - if fsel and string.sub(fsel,1,3) == "DCL" then - local sel = tonumber(string.sub(fsel,5)) or 1; -- selected line - local address = help_address[name] or "main"; - local pages = help_pages[address]; - - local link = string.match(pages[sel] or "", "\\%[([%w%s]+)\\%]") - if help_pages[link] then - help_address[name] = link; - robot_show_help(name) - end + --minetest.chat_send_all("D " .. minetest.serialize(fields)) + + + local go = false; + if fsel then + help_address[name].sel = tonumber(string.sub(fsel or "",5)) or 1 + if string.sub(fsel,1,3) == "CHG" then return end -- just select topic, dont go there + go = true -- we want to go to selected page end + + if fields.go then go = true end + if not go then return end + + local sel = help_address[name].sel; -- selected line + local address = help_address[name].address; + local pages = help_pages[address]; + + local link = string.match(pages[sel] or "", "\\%[([%w%s]+)\\%]") + if help_pages[link] then + help_address[name].address = link + help_address[name].sel = 1 + robot_show_help(name) + end + end, getForm = function(player_name) diff --git a/scripts/copy_paste.lua b/scripts/copy_paste.lua index 471a509..4059f22 100644 --- a/scripts/copy_paste.lua +++ b/scripts/copy_paste.lua @@ -1,4 +1,11 @@ --- COPY PASTE ROBOT by rnd: c1 c2 r = markers, c = copy p = paste +-- COPY PASTE ROBOT by rnd: c1 c2 r = markers, c = copy p = paste, rotz = rotate 90 deg cw z-axis, s = set +-- command parameters : +-- s nodename node_step +-- copy: +-- c1,c2 set markers, r set reference, c = copy, +-- p = paste at player position (reference is put there) +-- rotate: +-- c1,c2 set area, rotz = rotate 90 deg around z-axis if not paste then _G.minetest.forceload_block(self.pos(),true) @@ -18,36 +25,38 @@ if not paste then pos = pos, expirationtime = 10, velocity = {x=0, y=0,z=0}, - size = 18, + size = 9, texture = label..".png", acceleration = {x=0,y=0,z=0}, - collisiondetection = true, - collision_removal = true, }) end self.listen(1) - self.label("COPY-PASTE MASTER v1.2 gold edition. commands: c1 c2 r c p") +self.label("") +-- self.label("WorldEdit Bot\ncommands: c1 c2 r c p s rotz") end speaker, msg = self.listen_msg() -if speaker == "rnd" then +if speaker == "_" or speaker == "test" then + local args = {} + for word in string.gmatch(msg,"%S+") do args[#args+1]=word end + local player = _G.minetest.get_player_by_name(speaker); local 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 msg == "c1" then - paste.src1 = {x=p.x,y=p.y,z=p.z};say("marker 1 set at " .. p.x .. " " .. p.y .. " " .. p.z) + 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,"099") -- c - elseif msg == "c2" then - paste.src2 = {x=p.x,y=p.y,z=p.z};say("marker 2 set at " .. p.x .. " " .. p.y .. " " .. p.z) + 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,"099") -- c - elseif msg == "r" then - paste.ref = {x=p.x,y=p.y,z=p.z};say("reference set at " .. p.x .. " " .. p.y .. " " .. p.z) + 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,"114") -- r - elseif msg == "c" then -- copy + elseif args[1] == "c" then -- copy local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z); local 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 = {}; @@ -65,7 +74,7 @@ if speaker == "rnd" then end end say(count .. " nodes copied "); - elseif msg == "p" then -- paste + elseif args[1] == "p" then -- paste local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z); local 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; @@ -85,6 +94,82 @@ if speaker == "rnd" then end end end - say(count .. " nodes pasted "); + say(count .. " nodes pasted ",speaker); + elseif args[1] == "s" then -- set node + local nodename = args[2] or "air" + local step = args[3] or 1; + local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z); + local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z); + local count = 0; + for i = x1,x2,step do + for j = y1,y2,step do + for k = z1,z2,step do + minetest.set_node({x=i,y=j,z=k}, {name = nodename}); + end + end + end + say((x2-x1+1)*(y2-y1+1)*(z2-z1+1) .. " nodes set to " .. nodename,speaker) + elseif args[1] == "rotz" then -- rotate around z axis, center of selection + local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z); + local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z); + + + local d = x2-x1; if z2-z1(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/games/battle_minesweeper_game.lua b/scripts/games/battle_minesweeper_game.lua index 5eae232..3066e29 100644 --- a/scripts/games/battle_minesweeper_game.lua +++ b/scripts/games/battle_minesweeper_game.lua @@ -1,3 +1,5 @@ +-- battle minesweeper, 2 player + if not data then m=10;n=10; players = {}; diff --git a/scripts/games/checkers.lua b/scripts/games/checkers.lua new file mode 100644 index 0000000..4125441 --- /dev/null +++ b/scripts/games/checkers.lua @@ -0,0 +1,117 @@ +--checkers by rnd, 1.5 hr +if not init then init=true + spos = self.spawnpos() + sizex = 8; sizez= 8 + + gamepieces = { + { -- player1: regular piece, queen + "basic_robot:buttonFFFF80","basic_robot:buttonFF8080" + }, + { -- player2 + "basic_robot:button80FF80","basic_robot:button8080FF" + } + } + show_mark = function(pos) + minetest.add_particle( + { + pos = pos, + expirationtime = 5, + velocity = {x=0, y=0,z=0}, + size = 18, + texture = "default_apple.png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + } + ) + end + + build_game = function() + for i = 1,sizez do + for j = 1,2 do + minetest.swap_node({x=spos.x+j,y=spos.y,z=spos.z+i},{name = "basic_robot:button_131"}) + minetest.swap_node({x=spos.x+j,y=spos.y+1,z=spos.z+i},{name = "air"}) + end + minetest.swap_node({x=spos.x+3,y=spos.y,z=spos.z+i},{name = "basic_robot:button_".. (48+i), param2 = 3}) + end + for i = 1,sizex do + minetest.swap_node({x=spos.x+3+i,y=spos.y,z=spos.z},{name = "basic_robot:button_".. (64+i)}) + minetest.swap_node({x=spos.x+3+i,y=spos.y,z=spos.z+sizez+1},{name = "basic_robot:button_".. (64+i), param2 = 2}) + end + local white = true; + for i = 1,sizex do + for j = 1,sizez do + if white then + minetest.swap_node({x=spos.x+3+i,y=spos.y,z=spos.z+j},{name = "basic_robot:button_0"}) + else + minetest.swap_node({x=spos.x+3+i,y=spos.y,z=spos.z+j},{name = "basic_robot:button808080"}) + end + white = not white + minetest.swap_node({x=spos.x+3+i,y=spos.y+1,z=spos.z+j},{name = "air"}) + end + white = not white + end + minetest.swap_node({x=spos.x+1,y=spos.y+1,z=spos.z+1},{name = gamepieces[1][1]}) + minetest.swap_node({x=spos.x+1,y=spos.y+2,z=spos.z+1},{name = gamepieces[1][2]}) + + minetest.swap_node({x=spos.x+1,y=spos.y+1,z=spos.z+sizez},{name = gamepieces[2][1]}) + minetest.swap_node({x=spos.x+1,y=spos.y+2,z=spos.z+sizez},{name = gamepieces[2][2]}) + + for i=1,sizex,2 do + minetest.swap_node({x=spos.x+4+i,y=spos.y+1,z=spos.z+1},{name = gamepieces[1][1]}) + minetest.swap_node({x=spos.x+3+i,y=spos.y+1,z=spos.z+2},{name = gamepieces[1][1]}) + + minetest.swap_node({x=spos.x+4+i,y=spos.y+1,z=spos.z+sizez-1},{name = gamepieces[2][1]}) + minetest.swap_node({x=spos.x+3+i,y=spos.y+1,z=spos.z+sizez},{name = gamepieces[2][1]}) + end + end + + msgs = {1} --{idx, msg1, msg2,msg3, msg4, msg5}; -- up to 5 ingame messages displayed + add_msg = function(text) + local idx = msgs[1] or 1; + msgs[idx+1] = text;idx = idx+1; if idx>5 then idx = 1 end msgs[1] = idx + end + show_msgs = function() -- last message on top + local out = {}; local idx = msgs[1] or 1; + for i = idx,2,-1 do out[#out+1] = msgs[i] or "" end + for i = 6, idx+1,-1 do out[#out+1] = msgs[i] or "" end + self.label(table.concat(out,"\n")) + end + + build_game() + punchpos = nil; -- pos of last punched piece + step = 0; + self.label("checkers\npunch piece then punch board to move") +end + +event = keyboard.get() + + +if event then + --self.label(serialize(event)) + if event.y==spos.y+1 then -- piece was punched to be moved + punchpos = {x=event.x,y=event.y,z=event.z} + elseif event.y==spos.y and event.x~=spos.x+3 then -- board was punched + if not punchpos then return end + + step = step+1 + local x1 = punchpos.x-spos.x-3; local z1 = punchpos.z-spos.z + local x2 = event.x-spos.x-3; local z2 = event.z-spos.z + add_msg(minetest.colorize("red","MOVE " .. step) .. ": " .. event.puncher .. " " .. string.char(64+x1) .. z1 .. " to " .. string.char(64+x2) .. z2); show_msgs() + + local nodename = minetest.get_node(punchpos).name + minetest.swap_node(punchpos,{name = "air"}) + show_mark(punchpos) + + -- promotion of pieces to queen when reaching the opposite side + if nodename == gamepieces[1][1] and event.z==spos.z+sizez and event.x>spos.x+3 then + nodename = gamepieces[1][2] + elseif nodename == gamepieces[2][1] and event.z==spos.z+1 and event.x>spos.x+3 then + nodename = gamepieces[2][2] + end + + minetest.swap_node({x=event.x,y=event.y+1,z=event.z},{name = nodename}) + show_mark({x=event.x,y=event.y+2,z=event.z}) + punchpos = nil + end +end \ No newline at end of file diff --git a/scripts/games/connect4.lua b/scripts/games/connect4.lua index fd3ebd6..ae4c22a 100644 --- a/scripts/games/connect4.lua +++ b/scripts/games/connect4.lua @@ -1,6 +1,7 @@ -- CONNECT, coded in 20 minutes by rnd if not data then - m=8;n=8;turn = 0; num = 4; + 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 state = 0; -- 0 signup 1 game @@ -34,7 +35,7 @@ if not data then return c1 end - self.label("CONNECT 4 : GREEN starts play. 2 players punch to join game.") + self.label("TRY TO GET FIRST " .. num .. " IN A ROW! : GREEN starts play. 2 players punch to join game.") end event = keyboard.get(); @@ -43,10 +44,15 @@ if event then 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 + 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 diff --git a/scripts/games/fallout_hacking.lua b/scripts/games/fallout_hacking.lua index 54f1c37..e3fbc7e 100644 --- a/scripts/games/fallout_hacking.lua +++ b/scripts/games/fallout_hacking.lua @@ -1,5 +1,13 @@ +-- 'hacking' game from Fallout, by rnd if not init then init = true + + max_guesses = 4 -- how many guesses player gets + n = 40; -- how many options + pass_length=5 + charset_size=10 -- a,b,c,d,e,...? + cols = 4; + rows = math.ceil(n/cols); generate_random_string = function(n,m) local ret = {}; @@ -18,21 +26,18 @@ if not init then end get_form = function() - local n = #passlist; local frm = "label[0,0;" .. intro_msg .. "] " .. "label[0,8.5;" .. msg .. "] " - - for i = 1,10 do - frm = frm .. "button[0,".. (i)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] " - end - - for i = 11,n do - frm = frm .. "button[2,".. (i-11+1)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] " - end - - local form = "size[6," .. 9 .. "]" .. frm; - return form + local k = 0 + for i=0,cols-1 do + for j = 1,rows do + k=k+1; if k>n then break end + frm = frm .. "button[" .. 2*i.. ",".. j*0.75 ..";2,1;" .. k .. ";".. passlist[k] .. "] " + end + end + local form = "size[" .. 2*cols .. "," .. 9 .. "]" .. frm; + return form end _G.math.randomseed(os.time()) @@ -40,15 +45,17 @@ if not init then msg = "" --TEST\nTEST\nTEST"; passlist = {}; passdict = {} - n = 20; -- how many options + + count = 0; while count< n do - local pass = generate_random_string(5,5); -- password length, charset size + local pass = generate_random_string(pass_length,charset_size); -- password length, charset size if not passdict[pass] then passlist[#passlist+1] = pass; passdict[pass] = true; count = count + 1 end end correct = math.random(n) +-- say(passlist[correct]) guesses = 0 - max_guesses = 4 + rom.data = {}; if not rom.data then rom.data = {} end @@ -57,7 +64,7 @@ if not init then local players = find_player(4); if not players then say("#fallout hacking game: no players") self.remove() end pname = players[1]; - say("#fallout hacking game, player " .. pname) + minetest.chat_send_player(pname,"#fallout hacking game, player " .. pname) --if rom.data[pname] then say("password is locked out!") self.remove() end @@ -76,18 +83,19 @@ sender,fields = self.read_form() if selected>0 then guesses = guesses + 1 if selected == correct then - say("password " .. passlist[correct] .. " is correct! " .. guesses .. " guesses.") - self.show_form(pname, "size[1,1] label[0,0.5;" .. minetest.colorize("lawngreen", "ACCESS GRANTED") .. "]") + minetest.chat_send_all("#FALLOUT HACKING: " .. pname .. " guessed the password " .. passlist[correct] .. " after " .. guesses .. " guesses.") + self.show_form(pname, "size[3,1] label[0,0.5;" .. minetest.colorize("lawngreen", "ACCESS GRANTED") .. "]") self.remove() --correct: do something with player else + if guesses == 3 then msg = msg .. "\n" end msg = msg .. " " .. minetest.colorize("yellow",guesses .. ". " .. passlist[selected]) .. " (" .. get_similiarity(passlist[correct], passlist[selected]) .. " match)" self.show_form(pname, get_form()) end if guesses>=max_guesses then msg = minetest.colorize("red","A C C E S S D E N I E D!") self.show_form(pname, get_form()) - say("too many false guesses. password locked out!") rom.data[pname] = 1; self.remove() + minetest.chat_send_player(pname,"too many false guesses. password locked out!") rom.data[pname] = 1; self.remove() end end if fields.quit then self.remove() end diff --git a/scripts/games/go.lua b/scripts/games/go.lua new file mode 100644 index 0000000..9381a83 --- /dev/null +++ b/scripts/games/go.lua @@ -0,0 +1,94 @@ +--go by rnd +if not init then init=true + spos = self.spawnpos() + sizex = 9; sizez = 9 + + gamepieces = { + { -- player black + "basic_robot:buttonFF8080" + }, + { -- player white - blue + "basic_robot:button8080FF" + } + } + show_mark = function(pos) + minetest.add_particle( + { + pos = pos, + expirationtime = 10, + velocity = {x=0, y=0,z=0}, + size = 5, + texture = "default_apple.png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + } + ) + end + + build_game = function() + for i = 1,sizex do + minetest.swap_node({x=spos.x+i,y=spos.y,z=spos.z+sizez+1},{name = "basic_robot:button_"..(64+i), param2=2}) + minetest.swap_node({x=spos.x+i,y=spos.y,z=spos.z},{name = "basic_robot:button_"..(64+i)}) + for j = 1,sizez do + minetest.swap_node({x=spos.x+i,y=spos.y,z=spos.z+j},{name = "basic_robot:button808080"}) + minetest.swap_node({x=spos.x+i,y=spos.y+1,z=spos.z+j},{name = "air"}) + end + end + for j = 1,sizez do + minetest.swap_node({x=spos.x,y=spos.y,z=spos.z+j},{name = "basic_robot:button_".. (48+j), param2=3}) + end + + end + + msgs = {1} --{idx, msg1, msg2,msg3, msg4, msg5}; -- up to 5 ingame messages displayed + add_msg = function(text) + local idx = msgs[1] or 1; + msgs[idx+1] = text;idx = idx+1; if idx>5 then idx = 1 end msgs[1] = idx + end + show_msgs = function() -- last message on top + local out = {}; local idx = msgs[1] or 1; + for i = idx,2,-1 do out[#out+1] = msgs[i] or "" end + for i = 6, idx+1,-1 do out[#out+1] = msgs[i] or "" end + self.label(table.concat(out,"\n")) + end + + build_game() + punchnode = nil; -- name of piece to place + step = 0; + turn = 0; + self.label("go\n\n" .. + "RULES:\n\n".. + "1. liberty of block is free position next to block (not diagonally). block\n".. + "is alive if it has liberties left. group of connected (not diagonally) blocks\n".. + "is alive if at least one block in group is alive.\n".. + "2. dead blocks are immediately removed from game.\n".. + "3.to get score count how much territory your blocks occupy, including blocks\n".. + "themselves. If not clear which blocks are dead, continue game until it is clear\n\n".. + "punch board to place your red/blue piece. blue starts first.") +end + +event = keyboard.get() + + +if event then + --self.label(serialize(event)) + local x = event.x-spos.x; local z = event.z-spos.z; + if event.y~= spos.y then return end + if x<1 or x>sizex or z<1 or z>sizez then return end + + turn = 1- turn + step = step+1 + local x2 = event.x-spos.x; local z2 = event.z-spos.z + if turn == 0 then + add_msg(minetest.colorize("red",step) .. ": " .. event.puncher .. " " .. string.char(64+x2) .. z2); + else + add_msg(minetest.colorize("blue",step) .. ": " .. event.puncher .. " " .. string.char(64+x2) .. z2); + end + + show_msgs() + + minetest.swap_node({x=event.x,y=event.y+1,z=event.z},{name = gamepieces[turn+1][1]}) + punchnode = nil + show_mark({x=event.x,y=event.y+2,z=event.z}) +end \ No newline at end of file diff --git a/scripts/games/hide_and_seek_new.lua b/scripts/games/hide_and_seek_new.lua new file mode 100644 index 0000000..4776177 --- /dev/null +++ b/scripts/games/hide_and_seek_new.lua @@ -0,0 +1,165 @@ +--HIDE AND SEEK game robot +if not init then init = true + _G.minetest.forceload_block(self.pos(),true) + timeout = 30; + + gamepos = {x=0,y=11,z=0} -- game of hide and seek is played around here + maxgamedist = 100; -- how far around gamepos player can go before kicked out of game + gameoutpos = {x=0,y=20,z=0} -- position for players that go out of game + + player_list = {}; + s=0;t=0; count = 0; + prize = "" + + announce = function(text) -- show message to all players in game only + for name,_ in pairs(player_list) do + _G.minetest.chat_send_player(name, text) + end + end + + + get_players = function() + local msg = ""; + for name,_ in pairs(player_list) do + msg = msg .. " " .. name + end + return msg + end + + init_game = function() + + local msg = get_players(); + announce(colorize("red","# HIDE AND SEEK : hide from everyone else who is playing. Winner gets DIAMONDS\nsay join to join play. say #start to start game.".. + " players: " .. msg)) + s=0;t=0; + end + + --show initial message for all players + --minetest.chat_send_all(colorize("red","# HIDE AND SEEK : hide from everyone else who is playing. Winner gets DIAMONDS\nsay join to join play. say #start to start game.") + + init_game() + _G.minetest.forceload_block(self.pos(),true) + self.listen(1); self.label(colorize("yellow","HIDE&SEEK")) +end + +speaker,msg = self.listen_msg(); + +if s==0 then + + t = t +1 + if t%30 == 0 then + init_game(); + end + + if msg =="join" then + player_list[speaker]={}; + announce(colorize("red","# HIDE AND SEEK: " .. speaker .. " joined the game")); + announce("players: " .. get_players()) + + local player = _G.minetest.get_player_by_name(speaker); + count = count + 1 + if player then + player:setpos(gamepos);player:set_properties({nametag_color = "0x0"}) + player:set_hp(20) + local inv = player:get_inventory();inv:set_list("main",{}) + end + + end + if msg == "#start" and count>1 then s = 0.5 announce(colorize("red","# HIDE AND SEEK STARTS in " .. timeout .. " SECONDS!")) end + +elseif s==0.5 then + t=t+1 + if t==timeout then + t=0;s = 1; count = 0; + for pname,_ in pairs(player_list) do + local player = _G.minetest.get_player_by_name(pname); + if player then + player_list[pname].hp = player:get_hp(); + player_list[pname].pos = player:getpos() + player_list[pname].t = 0; + count = count+1 + end + end + if count == 1 then + init = false; announce("# HIDE AND SEEK only 1 player, aborting.") + else + prize = "default:diamond " .. (count-1); + announce(colorize("red","# HIDE AND SEEK STARTS NOW WITH " .. count .. " PLAYERS.".. + "You are out if: 1.your health changes, 2. leave game area. If stay in same area for too long or you will be exposed.")) + announce(colorize("red","# WINNER WILL GET " .. prize)) + + end + + end +elseif s==1 then + players = _G.minetest.get_connected_players(); + count = 0; + for _,player in pairs(players) do + local name = player:get_player_name(); + local data = player_list[name]; + if data then + count=count+1 + local pos = player:getpos(); + local dist = math.max(math.abs(pos.x-gamepos.x),math.abs(pos.y-gamepos.y),math.abs(pos.z-gamepos.z)); + if dist>maxgamedist or (not _G.minetest.get_player_by_name(name)) then + announce("# HIDE AND SEEK: ".. name .. " is OUT! went too far away " ) + player:set_properties({nametag_color = "white"}) + player_list[name] = nil; + announce("remaining players: " .. get_players()) + end + if data.hp ~= player:get_hp() then + announce("# HIDE AND SEEK: ".. name .. " is OUT! his health changed!" ) + player:set_properties({nametag_color = "white"}) + player_list[name] = nil; + announce("remaining players: " .. get_players()) + player:setpos(gameoutpos) + end + + --expose campers + local p = data.pos; + dist = math.max(math.abs(pos.x-p.x),math.abs(pos.y-p.y),math.abs(pos.z-p.z)); + --say( name .. " dist " .. dist .. " t " .. data.t) + if dist<8 then + data.t = data.t+1; + if not data.camp then + if data.t>25 and not data.camp then + _G.minetest.chat_send_player(name, "# HIDE AND SEEK: move in 5s or be exposed") + data.camp = true + end + elseif data.t>=30 then + pos.x=math.ceil(pos.x);pos.y=math.ceil(pos.y);pos.z=math.ceil(pos.z); + announce("# HIDE AND SEEK: " .. name .. " is camping at " .. pos.x .. " " .. pos.z) + data.camp = false; data.t = 0 + end + else + data.t = 0; data.pos = player:getpos(); data.camp = false + end + + end + end + + self.label(count) + + if count<=1 then + if count==1 then + for name,_ in pairs(player_list) do + local player0=_G.minetest.get_player_by_name(name) + if player0 then + announce(colorize("red","****** HIDE AND SEEK: ".. name .. " wins ******")) + local inv = player0:get_inventory(); + inv:add_item("main",_G.ItemStack(prize)) + player0:set_properties({nametag_color = "white"}) + player0:setpos(gameoutpos) + end + s=2 + end + else + announce("# HIDE AND SEEK: no players left") + s=2 + end + end + +elseif s==2 then + player_list = {} + init_game() +end \ No newline at end of file diff --git a/scripts/games/high_scores_for_games b/scripts/games/high_scores_for_games new file mode 100644 index 0000000..6ad5732 --- /dev/null +++ b/scripts/games/high_scores_for_games @@ -0,0 +1,23 @@ +-- high scores for multiple levels of game +if not init then init = true + init_score = function(levels, tops, default_value) -- [level] = {{name,score}, ...} + local data = {} for i = 1, levels do data[i] = {} for j = 1,tops do data[i][j] = {"-",default_value} end end return data + end + + add_score = function(data,name,score,level) + local datal = data[level]; local tops = #datal; + local j;for i = 1,tops do if score>datal[i][2] then j = i break end end + if not j then return end; for i=tops,j+1,-1 do datal[i][1] = datal[i-1][1];datal[i][2] = datal[i-1][2] end + datal[j] = {name,score} + end + + _,text = book.read(1); data = minetest.deserialize(text) + if not data then data = init_score(1,5,-999) end + + add_score(data,"pl1",-50,1) + add_score(data,"pl2",-40,1) + + book.write(1,"score", minetest.serialize(data)) + + self.label(serialize(data)) +end \ No newline at end of file diff --git a/scripts/games/mensch_argere_dich_nicht.lua b/scripts/games/mensch_argere_dich_nicht.lua index 2c3a6cb..d665c58 100644 --- a/scripts/games/mensch_argere_dich_nicht.lua +++ b/scripts/games/mensch_argere_dich_nicht.lua @@ -1,89 +1,130 @@ - -if not init then - - msg = - "'Mensch argere Dich nicht' is a German board game (but not a German-style board game), developed by Josef Friedrich Schmidt in 1907/1908.\n".. - " The players throw a die in turn and can advance any of their pieces in the game by the thrown number of dots on the dice.\n" .. - "Throwing a six means bringing a piece into the game (by placing one from the 'out' area onto the 'start' field) and throwing the dice again. If\n" .. - "a piece is on the 'start' field and there are still pieces in the 'out' area, it must be moved as soon as possible. If a piece cannot be\n".. "brought into the game then any other piece in the game must be moved by the thrown number, if that is possible. Pay attention that throwing\n".." dice continuously without moving is forbidden and by each dice throw you have to make a move.\n" .. - "Pieces can jump over other pieces, and throw out pieces from other players (into that player's 'out' area) if they land on them. A player\n".. "cannot throw out his own pieces though, he can advance further than the last field in the 'home' row. A player can be thrown out if he is on\n".. - "his 'start' field.\n" .. - "Variation which is played by most players: A player who has no pieces in the game has 3 tries to throw a six" - self.label(msg) - - init = true; - state = 1; -- game on - step = 0; - punchstate = 1; -- first punch - punchpos = {} - pos = self.spawnpos() - dice = 0 - spawns = {{2,2,"basic_robot:buttonFF8080"},{2,11,"basic_robot:button8080FF"},{11,11,"basic_robot:button80FF80"},{11,2,"basic_robot:buttonFFFF80"}} - - for i = 1,12 do for j = 1,12 do - minetest.swap_node({x=pos.x+i,y=pos.y+1,z=pos.z+j},{name = "air"}) - end end - - for k = 1,#spawns do - for i = 0,1 do for j = 0,1 do - minetest.swap_node({x=pos.x+i+spawns[k][1],y=pos.y+1,z=pos.z+j+spawns[k][2]},{name = spawns[k][3]}) - end end - end - - keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7) - -end - -if state == 0 then -elseif state == 1 then - event = keyboard.get(); - if event then - x = event.x-pos.x; y = event.y-pos.y; z = event.z-pos.z - --say("type " .. event.type .. " pos " .. x .. " " .. y .. " " .. z) - if x == 7 and y == 1 and z == 7 then - _G.math.randomseed(os.time()) - dice = math.random(6); - keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7+dice) - step = step + 1; - msg = colorize("red","") .. " STEP " .. step .. ": " ..event.puncher .. " threw dice = " .. dice; - minetest.chat_send_all(msg) - self.label(msg) - punchstate = 1 - elseif punchstate == 1 then - if y == 1 and event.type ~= 2 and event.type<7 then - punchpos = 2; minetest.chat_send_player(event.puncher,colorize("red","") .. " punch place on board where to move ") - punchpos = {x=event.x,y=event.y,z=event.z} - punchstate = 2 - end - elseif punchstate == 2 then - if y == 0 and event.type ~= 2 then - if x<2 or x>12 or z<2 or z>12 then - else - local nodename = minetest.get_node(punchpos).name; - minetest.swap_node({x=event.x, y = event.y+1, z=event.z},{name = nodename}) - minetest.swap_node(punchpos,{name = "air"}) - punchstate = 1; dice = 0 - minetest.add_particle( - { - pos = punchpos, - expirationtime = 15, - velocity = {x=0, y=0,z=0}, - size = 18, - texture = "default_apple.png", - acceleration = {x=0,y=0,z=0}, - collisiondetection = true, - collision_removal = true, - } - ) - msg = colorize("red","") .. " " .. event.puncher .. " moved."; - minetest.chat_send_all(msg) - self.label(msg) - end - end - - - end - end - - +-- 'Mensch argere Dich nicht'. modified by rnd +if not init then + msg = + "Sorry!/Mensch argere Dich nicht, modified by rnd\n\n" .. + "1. goal of game is put all 4 of your pieces in your home area by moving clockwise around on white path\n".. + "2. start of game is player trying to move his piece out to start spot next to home by trying 3x to get 6 from dice.\n".. + "3. after that player uses dice at start of turn. he moves his playing pieces ( those\n".. + "on board) so many positions as dice said. negative number means moving back.\n".. + "4. if player can move his piece he must. if his piece goes backward all the way to start\n".. + "he must put it back out.If piece lands on opponent piece then opponent piece is put out to initial start.\n".. + "5. if dice shows 6 he can move 6 or put another piece out if possible. player can move\n".. + "pieces within home area if possible.\n".. + "6. if dice shows 0 you play as one of your opponents for a turn." + self.label(msg) + + init = true; + state = 1; -- game on + step = 0; + punchstate = 1; -- first punch + punchpos = {} + pos = self.spawnpos() + dice = 0 + spawns = { + {2,2,1,2,2,"basic_robot:buttonFF8080"}, -- xstart,zstart,ystart, dimx, dimz, nodename + {2,11,1,2,2,"basic_robot:button8080FF"}, + {11,11,1,2,2,"basic_robot:button80FF80"}, + {11,2,1,2,2,"basic_robot:buttonFFFF80"}, --4 bases + + {7,3,0,1,4,"basic_robot:buttonFF8080"}, -- red final + {3,7,0,4,1,"basic_robot:button8080FF"}, -- blue final + {7,8,0,1,4,"basic_robot:button80FF80"}, -- green final + {8,7,0,4,1,"basic_robot:buttonFFFF80"}, -- yellow final + + {1,1,0,5,5,"basic_robot:button808080"},{6,1,0,3,1,"basic_robot:button808080"}, + {9,1,0,5,5,"basic_robot:button808080"},{13,6,0,1,3,"basic_robot:button808080"}, + {1,9,0,5,5,"basic_robot:button808080"},{1,6,0,1,3,"basic_robot:button808080"}, + {9,9,0,5,5,"basic_robot:button808080"},{6,13,0,3,1,"basic_robot:button808080"}, + + {2,2,0,2,2,"basic_robot:buttonFFFFFF"}, + {2,11,0,2,2,"basic_robot:buttonFFFFFF"}, + {11,11,0,2,2,"basic_robot:buttonFFFFFF"}, + {11,2,0,2,2,"basic_robot:buttonFFFFFF"}, + } + + -- build board + for i = 1,12 do for j = 1,12 do + minetest.swap_node({x=pos.x+i,y=pos.y,z=pos.z+j},{name = "basic_robot:buttonFFFFFF"}) + minetest.swap_node({x=pos.x+i,y=pos.y+1,z=pos.z+j},{name = "air"}) + end end + + for k = 1,#spawns do + for i = 0,spawns[k][4]-1 do for j = 0,spawns[k][5]-1 do + minetest.swap_node({x=pos.x+i+spawns[k][1],y=pos.y+spawns[k][3],z=pos.z+j+spawns[k][2]},{name = spawns[k][6]}) + end end + end + + keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7) + + msgs = {1} --{idx, msg1, msg2,msg3, msg4, msg5}; -- up to 5 ingame messages displayed + add_msg = function(text) + local idx = msgs[1] or 1; + msgs[idx+1] = text;idx = idx+1; if idx>5 then idx = 1 end msgs[1] = idx + end + show_msgs = function() -- last message on top + local out = {}; local idx = msgs[1] or 1; + for i = idx,2,-1 do out[#out+1] = msgs[i] or "" end + for i = 6, idx+1,-1 do out[#out+1] = msgs[i] or "" end + self.label(table.concat(out,"\n")) + end + +end + +if state == 0 then +elseif state == 1 then + event = keyboard.get(); + if event then + x = event.x-pos.x; y = event.y-pos.y; z = event.z-pos.z + --say("type " .. event.type .. " pos " .. x .. " " .. y .. " " .. z) + if x == 7 and y == 1 and z == 7 then + _G.math.randomseed(os.time()) + dice = -3+math.random(9); + keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7+math.abs(dice)) + if dice<0 then + keyboard.set({x=pos.x+7,y=pos.y+2,z=pos.z+7},7+11) + else + keyboard.set({x=pos.x+7,y=pos.y+2,z=pos.z+7},0) + end + step = step + 1; + msg = minetest.colorize("red","STEP ".. step) .. ": " ..event.puncher .. " threw dice = " .. dice; + add_msg(msg); show_msgs() + + + punchstate = 1 + elseif punchstate == 1 then + if y == 1 and event.type ~= 2 and event.type<7 then + punchpos = 2; minetest.chat_send_player(event.puncher,colorize("red","") .. " punch place on board where to move ") + punchpos = {x=event.x,y=event.y,z=event.z} + punchstate = 2 + end + elseif punchstate == 2 then + if y == 0 and event.type ~= 2 then + if x<2 or x>12 or z<2 or z>12 then + else + local nodename = minetest.get_node(punchpos).name; + minetest.swap_node({x=event.x, y = event.y+1, z=event.z},{name = nodename}) + minetest.swap_node(punchpos,{name = "air"}) + punchstate = 1; dice = 0 + minetest.add_particle( + { + pos = punchpos, + expirationtime = 15, + velocity = {x=0, y=0,z=0}, + size = 18, + texture = "default_apple.png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + } + ) + msg = event.puncher .. " moved."; + add_msg(msg); show_msgs() + end + end + + + end + end + + end \ No newline at end of file diff --git a/scripts/games/minesweeper_game.lua b/scripts/games/minesweeper_game.lua index 7f72bbe..adcd27c 100644 --- a/scripts/games/minesweeper_game.lua +++ b/scripts/games/minesweeper_game.lua @@ -1,83 +1,88 @@ --- minesweeper -if not data then - m=24;n=22; minescount = m*n/5; - reward = 30; - - if not find_player(4) then error("minesweeper: no players near") end - - self.spam(1) - t0 = _G.minetest.get_gametime(); - data = {}; spawnpos = self.spawnpos() -- place mines - for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end - 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"}) - end - end end - puzzle.set_node({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},{name = "basic_robot:button80FF80"}) - - get_mine_count = function(i,j) - if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0 - for k = -1,1 do for l = -1,1 do - if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end - end end - return count - end - 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 - count=count-1 - end - end end - return count - end - say("minesweeper " .. m .. "x" ..n .. " with " .. minescount .. " mines ") - self.label("find all hidden mines! mark mine by standing on top of block and punch,\notherwise it will uncover the block (and possibly explode).") - -end - -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 - local count = chk_mines(); - if count == 0 then - t0 = _G.minetest.get_gametime() - t0; - say("congratulations! " .. event.puncher .. " discovered all mines in " .. t0 .. " s") - _G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward - else - reward = reward*(1-(count/minescount))^(1.5); reward = math.floor(reward); - say("FAIL! " .. count .. " mines remaining. You get " .. reward .. " gold for found mines") - _G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward - end - self.remove() - end - 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"}) - else - puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:buttonFF8080"}) - 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"}); - 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"}) - else puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:button"..count}) end - end - end - end +-- minesweeper +if not data then + m=24;n=22; minescount = m*n/5; + reward = 30; + + if not find_player(6) then error("minesweeper: no players near") end + + self.spam(1) + t0 = _G.minetest.get_gametime(); + data = {}; spawnpos = self.spawnpos() -- place mines + for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end + 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"}) + 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"}) + + get_mine_count = function(i,j) + if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0 + for k = -1,1 do for l = -1,1 do + if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end + end end + return count + end + 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 + count=count-1 + end + end end + return count + end + say("minesweeper " .. m .. "x" ..n .. " with " .. minescount .. " mines ") + self.label("find all hidden mines! mark mine by standing on top of block and punch,\notherwise it will uncover the block (and possibly explode).") + +end + +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 + local count = chk_mines(); + if count>minescount/2 then + say("fail, more than 50% of mines remaining ") + return + end + if count == 0 then + t0 = _G.minetest.get_gametime() - t0; + say("congratulations! " .. event.puncher .. " discovered all mines in " .. t0 .. " s") + _G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward + else + reward = reward*(1-(count/minescount))^(1.5); reward = math.floor(reward); + say("FAIL! " .. count .. " mines remaining. You get " .. reward .. " gold for found mines") + _G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward + end + self.remove() + end + 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"}) + else + puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:buttonFF8080"}) + 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"}); + 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"}) + else puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:button"..count}) end + end + end + end end \ No newline at end of file diff --git a/scripts/games/nonogram.lua b/scripts/games/nonogram.lua index 111766f..8605f81 100644 --- a/scripts/games/nonogram.lua +++ b/scripts/games/nonogram.lua @@ -1,264 +1,247 @@ --- nonogram game, created in 1hr 40 min by rnd - --- INIT -if not grid then - n=6 - solved = false -- do we render solution or blank? --- _G.math.randomseed(3) - - self.spam(1) - function get_score_from_string(score) - --say(score) - local scores = {}; - local j=1; --j k l - for i=0,5 do -- 0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999 - j = string.find(score," ", j+1); - local k = string.find(score," ", j+1); - local l = string.find(score," ", k+1); - if i==5 then l = string.len(score)+1 end - scores[i] = {string.sub(score,j+1,k-1),tonumber(string.sub(score,k+1,l-1))}; - j=l - end - return scores - end - - if not rom.score then _,rom.score = book.read(1) end - if not rom.score then rom.score = "0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999" end - highscore = get_score_from_string(rom.score) - --self.label(string.gsub(_G.dump(highscore), "\n","")) - - function get_score_string(scores) - local out = "" - for i = 0,5 do - out = out .. i .. " " .. - scores[i][1] .. " " .. - scores[i][2] .. " " - end - return out - end - - 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 )." .. - "\npunch gray blocks to toggle them and reveal hidden red blocks.\npunch green to check solution. If you give up punch blue."; - self.label(intro) - - grid = {} - spawnpos = self.spawnpos(); - offsetx = 10 - math.ceil(n/2); offsetz = math.floor(n/2); - spawnpos.x = spawnpos.x - offsetx; spawnpos.z = spawnpos.z - offsetz; - spawnpos.y = spawnpos.y+3 - - for i=1,n do - grid[i]={}; - for j=1,n do - grid[i][j]=math.random(2)-1 - end - end - - getcounts = function(grid) - local rowdata = {}; - for i=1,n do - rowdata[i]={}; local data = rowdata[i]; - local s=0;local c=0; - for j = 1, n do - if s == 0 and grid[i][j]==1 then s=1;c=0 end - if s == 1 then - if grid[i][j]==1 then - c=c+1 - if j == n then data[#data+1]=c end - else - data[#data+1]=c; s=0 - end - - end - end - end - local coldata = {}; - for j=1,n do - coldata[j]={}; local data = coldata[j]; - local s=0;local c=0; - for i = 1, n do - if s == 0 and grid[i][j]==1 then s=1;c=0 end - if s == 1 then - if grid[i][j]==1 then - c=c+1 - if i == n then data[#data+1]=c end - else - data[#data+1]=c; s=0 - end - - end - end - end - return rowdata,coldata - end - - read_field = function() - local grid = {}; - for i = 1, n do - 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 - end - end - return grid - end - - rowdata,coldata = getcounts(grid) - - check_solution = function() - local rdata,cdata; - rdata,cdata = getcounts(read_field()) - for i = 1,#rdata do - if #rdata[i]~=#rowdata[i] then return false end - for j = 1, #rdata[i] do - if rdata[i][j]~=rowdata[i][j] then return false end - end - end - - for i = 1,#cdata do - if #cdata[i]~=#coldata[i] then return false end - for j = 1, #rdata[i] do - if cdata[i][j]~=coldata[i][j] then return false end - end - end - return true - end - - get_difficulty = function() - local easy = 0; - for k = 1, n do - local sum=0 - for i = 1,#rowdata[k]-1 do - sum = sum + rowdata[k][i]+1; - end - if #rowdata[k]>0 then sum = sum + rowdata[k][#rowdata[k]] else sum = n end - if sum == n then easy = easy + 1 end - end - - for k = 1, n do - local sum=0 - for i = 1,#coldata[k]-1 do - sum = sum + coldata[k][i]+1; - end - 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; - if easy < 0 then easy = 0 end - return easy - end - - -- render game - for i=1,n do - for j =1,n do - 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 - keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ) --board - end - end - - --render counts rows - 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) - 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) - 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 - - local players = find_player(4,spawnpos) - if not players then error("minesweeper: no players near") end - local pname = players[1]; - - - --self.label() - - --self.label(string.gsub(_G.dump(read_field()),"\n","") ) - difficulty = get_difficulty() - reward = 0; limit = 0; - - if difficulty == 5 then limit = 120 reward = 10 - elseif difficulty == 4 then limit = 115 reward = 9 -- 60s - elseif difficulty == 3 then limit = 100 reward = 8 - elseif difficulty == 2 then limit = 80 reward = 7 - elseif difficulty <= 1 then limit = 70 reward = 6 - 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 " .. highscore[difficulty][2] .. " by " .. highscore[difficulty][1]) - -end - -event = keyboard.get() -if event then - if event.y == spawnpos.y and event.z == spawnpos.z then - if event.x == spawnpos.x+1 then -- check solution - if check_solution() then - t = _G.minetest.get_gametime(); t = t- t0; - local msg = ""; - keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},2) - msg = n .. "x" .. n .. " nonogram (difficuly " .. difficulty .. ") solved by " .. event.puncher .. " in " .. t .. " seconds. " - - if t < limit then - msg = msg .. " He gets " .. reward .. " gold for quick solve."; - else - reward = reward*2*(1-2*(t-limit)/limit)/2; if reward<0 then reward = 0 end - reward = math.floor(reward); - msg = msg .. " Your time was more than " .. limit .. ", you get " .. reward .. " gold "; - end - - -- highscore - if t0 then - local player = _G.minetest.get_player_by_name(event.puncher); - if player then - local inv = player:get_inventory(); - inv:add_item("main",_G.ItemStack("default:gold_ingot " .. reward)) - end - end - minetest.chat_send_player(event.puncher,msg) - - self.remove() - - else self.label("FAIL") end - elseif event.x == spawnpos.x+2 then -- solve - minetest.chat_send_player(event.puncher,"you gave up on game, displaying solution") - for i=1,n do - for j =1,n do - local typ; - if grid[j][i]==0 then typ = 2 else typ = 3 end - keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ) - end - end - self.remove() - end - else - local i = event.x-spawnpos.x;local j = event.z-spawnpos.z; - 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 - else newtyp = 2 - end - keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},newtyp); - end - end +-- nonogram game, created in 1hr 40 min by rnd + +-- INIT +if not grid then + n=6 -- size + solved = false -- do we render solution or blank? +-- _G.math.randomseed(3) + self.spam(1) + + + init_score = function(levels, tops, default_value) -- [level] = {{name,score}, ...} + local data = {} for i = 1, levels do data[i] = {} for j = 1,tops do data[i][j] = {"-",default_value} end end return data + end + + add_score = function(data,name,score,level) + local datal = data[level]; local tops = #datal; local j;for i = 1,tops do + if score>datal[i][2] then j = i break end end + if not j then return false end; for i=tops,j+1,-1 do datal[i][1] = datal[i-1][1];datal[i][2] = datal[i-1][2] end + datal[j] = {name,score} return true + 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, smaller time is better (thats why - in top 5, there largest value counts) + + 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 )." .. + "\npunch gray blocks to toggle them and reveal hidden red blocks.\npunch green to check solution. If you give up punch blue."; + self.label(intro) + + grid = {} + spawnpos = self.spawnpos(); + offsetx = 10 - math.ceil(n/2); offsetz = math.floor(n/2); + spawnpos.x = spawnpos.x - offsetx; spawnpos.z = spawnpos.z - offsetz; + spawnpos.y = spawnpos.y+3 + + for i=1,n do + grid[i]={}; + for j=1,n do + grid[i][j]=math.random(2)-1 + end + end + + getcounts = function(grid) + local rowdata = {}; + for i=1,n do + rowdata[i]={}; local data = rowdata[i]; + local s=0;local c=0; + for j = 1, n do + if s == 0 and grid[i][j]==1 then s=1;c=0 end + if s == 1 then + if grid[i][j]==1 then + c=c+1 + if j == n then data[#data+1]=c end + else + data[#data+1]=c; s=0 + end + + end + end + end + local coldata = {}; + for j=1,n do + coldata[j]={}; local data = coldata[j]; + local s=0;local c=0; + for i = 1, n do + if s == 0 and grid[i][j]==1 then s=1;c=0 end + if s == 1 then + if grid[i][j]==1 then + c=c+1 + if i == n then data[#data+1]=c end + else + data[#data+1]=c; s=0 + end + + end + end + end + return rowdata,coldata + end + + read_field = function() + local grid = {}; + for i = 1, n do + 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 + end + end + return grid + end + + rowdata,coldata = getcounts(grid) + + check_solution = function() + local rdata,cdata; + rdata,cdata = getcounts(read_field()) + for i = 1,#rdata do + if #rdata[i]~=#rowdata[i] then return false end + for j = 1, #rdata[i] do + if rdata[i][j]~=rowdata[i][j] then return false end + end + end + + for i = 1,#cdata do + if #cdata[i]~=#coldata[i] then return false end + for j = 1, #rdata[i] do + if cdata[i][j]~=coldata[i][j] then return false end + end + end + return true + end + + get_difficulty = function() + local easy = 0; + for k = 1, n do + local sum=0 + for i = 1,#rowdata[k]-1 do + sum = sum + rowdata[k][i]+1; + end + if #rowdata[k]>0 then sum = sum + rowdata[k][#rowdata[k]] else sum = n end + if sum == n then easy = easy + 1 end + end + + for k = 1, n do + local sum=0 + for i = 1,#coldata[k]-1 do + sum = sum + coldata[k][i]+1; + end + 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; + if easy < 0 then easy = 0 end + return easy + end + + -- render game + for i=1,n do + for j =1,n do + 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 + keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ) --board + end + end + + --render counts rows + 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) + 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) + 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 + + local players = find_player(6,spawnpos) + if not players then error("nonogram: no players near") end + local pname = players[1]; + + difficulty = get_difficulty() + reward = 0; limit = 0; + + if difficulty == 5 then limit = 120 reward = 10 + elseif difficulty == 4 then limit = 115 reward = 9 -- 60s + elseif difficulty == 3 then limit = 100 reward = 8 + elseif difficulty == 2 then limit = 80 reward = 7 + elseif difficulty <= 1 then limit = 70 reward = 6 + 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]) + + +end + +event = keyboard.get() +if event then + if event.y == spawnpos.y and event.z == spawnpos.z then + if event.x == spawnpos.x+1 then -- check solution + if check_solution() then + t = _G.minetest.get_gametime(); t = t- t0; + local msg = ""; + keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},2) + msg = n .. "x" .. n .. " nonogram (difficuly " .. difficulty .. ") solved by " .. event.puncher .. " in " .. t .. " seconds. " + + if t < limit then + msg = msg .. " He gets " .. reward .. " gold for quick solve."; + else + reward = reward*2*(1-2*(t-limit)/limit)/2; if reward<0 then reward = 0 end + reward = math.floor(reward); + msg = msg .. " Your time was more than " .. limit .. ", you get " .. reward .. " gold "; + end + + -- highscore + if add_score(scores,event.puncher,-t,difficulty) then + say("nonogram difficulty " .. difficulty .. ": new record " .. t .. " s !") + local sdata = scores[difficulty]; + local out = {"TOP 5 PLAYERS/TIMES: "}; for i=1,#sdata do out[#out+1] = sdata[i][1] .. " " .. -sdata[i][2].."," end say(table.concat(out," ")) + book.write(1,"scores", minetest.serialize(scores)) + end + + + if reward>0 then + local player = _G.minetest.get_player_by_name(event.puncher); + if player then + local inv = player:get_inventory(); + inv:add_item("main",_G.ItemStack("default:gold_ingot " .. reward)) + end + end + minetest.chat_send_player(event.puncher,msg) + + self.remove() + + else self.label("FAIL") end + elseif event.x == spawnpos.x+2 then -- solve + minetest.chat_send_player(event.puncher,"you gave up on game, displaying solution") + for i=1,n do + for j =1,n do + local typ; + if grid[j][i]==0 then typ = 2 else typ = 3 end + keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ) + end + end + self.remove() + end + else + local i = event.x-spawnpos.x;local j = event.z-spawnpos.z; + 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 + else newtyp = 2 + end + keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},newtyp); + end + end end \ No newline at end of file diff --git a/scripts/games/simple_layout_gen.lua b/scripts/games/simple_layout_gen.lua new file mode 100644 index 0000000..802e3a3 --- /dev/null +++ b/scripts/games/simple_layout_gen.lua @@ -0,0 +1,24 @@ +if not data then + m=50;n=50; minescount = m*n/10; + + t0 = _G.minetest.get_gametime(); + data = {}; spawnpos = self.spawnpos(); + for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end + + get_mine_count = function(i,j) + if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0 + for k = -1,1 do for l = -1,1 do + if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end + end end + return count + end + + for i = 1,m do for j = 1,n do + if get_mine_count(i,j) > 0 or (data[i] and data[i][j] == 1) then + _G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "basic_robot:buttonFFFFFF"}) + else + _G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "default:dirt"}) + end + end end +end +self.remove() \ No newline at end of file diff --git a/scripts/games/sokoban.txt b/scripts/games/sokoban.txt new file mode 100644 index 0000000..f6c2351 --- /dev/null +++ b/scripts/games/sokoban.txt @@ -0,0 +1,1398 @@ +; 0 + ##### + # # + #$ # + ### $## + # $ $ # +### # ## # ###### +# # ## ##### ..# +# $ $ ..# +##### ### #@## ..# + # ######### + ####### +; 1 + +############ +#.. @# ### +#.. # $ $ # +#.. #$#### # +#.. ## # +#.. # # $ ## +###### ##$ $ # + # $ $ $ $ # + # # # + ############ +; 2 + + ######## + # @# + # $#$ ## + # $ $# + ##$ $ # +######### $ # ### +#.... ## $ $ # +##... $ $ # +#.... ########## +######## +; 3 + + ######## + # ....# +############ ....# +# # $ $ ....# +# $$$#$ $ # ....# +# $ $ # ....# +# $$ #$ $ $######## +# $ # # +## ######### +# # ## +# $ ## +# $$#$$ @# +# # ## +########### +; 4 + + ##### + # ##### + # #$## # + # $ # +######### ### # +#.... ## $ $### +#.... $ $$ ## +#.... ##$ $ @# +######### $ ## + # $ $ # + ### ## # + # # + ###### +; 5 + +###### ### +#.. # ##@## +#.. ### # +#.. $$ # +#.. # # $ # +#..### # $ # +#### $ #$ # + # $# $ # + # $ $ # + # ## # + ######### +; 6 + + ##### + ####### ## +## # @## $$ # +# $ # +# $ ### # +### #####$### +# $ ### ..# +# $ $ $ ...# +# ###...# +# $$ # #...# +# ### ##### +#### +; 7 + + #### + # ########### + # $ $ $ # + # $# $ # $ # + # $ $ # # +### $# # #### # +#@#$ $ $ ## # +# $ #$# # # +# $ $ $ $ # +##### ######### + # # + # # + #......# + #......# + #......# + ######## +; 8 + + ####### + # ...# + ##### ...# + # . .# + # ## ...# + ## ## ...# + ### ######## + # $$$ ## + ##### $ $ ##### +## #$ $ # # +#@ $ $ $ $ # +###### $$ $ ##### + # # + ######## +; 9 + + ### ############# +##@#### # # +# $$ $$ $ $ ...# +# $$$# $ #...# +# $ # $$ $$ #...# +### # $ #...# +# # $ $ $ #...# +# ###### ###...# +## # # $ $ #...# +# ## # $$ $ $##..# +# ..# # $ #.# +# ..# # $$$ $$$ #.# +##### # # #.# + # ######### #.# + # #.# + ############### +; 10 + + #### + #### # # + ### @###$ # + ## $ # + ## $ $$## ## + # #$## # + # # $ $$ # ### + # $ # # $ ##### +#### # $$ # # +#### ## $ # +#. ### ######## +#.. ..# #### +#...#.# +#.....# +####### +; 11 + +################ +# # +# # ###### # +# # $ $ $ $# # +# # $@$ ## ## +# # $ $ $###...# +# # $ $ ##...# +# ###$$$ $ ##...# +# # ## ##...# +##### ## ##...# + ##### ### + # # + ####### +; 12 + + ######### + ## ## ###### +### # # ### +# $ #$ # # ... # +# # $#@$## # #.#. # +# # #$ # . . # +# $ $ # # #.#. # +# ## ##$ $ . . # +# $ # # #$#.#. # +## $ $ $ $... # + #$ ###### ## # + # # ########## + #### +; 13 + + ####### + ####### # + # # $@$ # + #$$ # ######### + # ###......## # + # $......## # # + # ###...... # +## #### ### #$## +# #$ # $ # # +# $ $$$ # $## # +# $ $ ###$$ # # +##### $ # # + ### ### # # + # # # + ######## # + #### +; 14 + + ######## + # # # + # $ # + ### #$ #### + # $ ##$ # + # # @ $ # $# + # # $ #### + ## ####$## # + # $#.....# # # + # $..**. $# ### +## #.....# # +# ### ####### +# $$ # # +# # # +###### # + ##### +; 15 + +##### +# ## +# # #### +# $ #### # +# $$ $ $# +###@ #$ ## + # ## $ $ ## + # $ ## ## .# + # #$##$ #.# + ### $..##.# + # #.*...# + # $$ #.....# + # ######### + # # + #### +; 16 + + ########## + #.. # # + #.. # + #.. # #### + ####### # ## + # # + # # ## # # +#### ## #### ## +# $ ##### # # +# # $ $ # $ # +# @$ $ # ## +#### ## ####### + # # + ###### +; 17 + + ########### + # . # # + # #. @ # + ##### ##..# #### +## # ..### ### +# $ #... $ # $ # +# .. ## ## ## # +####$##$# $ # # # + ## # #$ $$ # # + # $ # # # $## # + # # + # ########### # + #### #### +; 18 + + ###### + # @#### +##### $ # +# ## #### +# $ # ## # +# $ # ##### # +## $ $ # # +## $ $ ### # # +## # $ # # # +## # #$# # # +## ### # # ###### +# $ #### # #....# +# $ $ ..#.# +####$ $# $ ....# +# # ## ....# +################### +; 19 + + ########## +##### #### +# # $ #@ # +# #######$#### ### +# # ## # #$ ..# +# # $ # # #.# +# # $ # #$ ..# +# # ### ## #.# +# ### # # #$ ..# +# # # #### #.# +# #$ $ $ #$ ..# +# $ # $ $ # #.# +#### $### #$ ..# + # $$ ###....# + # ## ###### + ######## +; 20 + +######### +# # +# #### +## #### # # +## #@## # +# $$$ $ $$# +# # ## $ # +# # ## $ #### +#### $$$ $# # + # ## ....# + # # # #.. .# + # # # ##...# + ##### $ #...# + ## ##### + ##### +; 21 + +###### #### +# ####### ##### +# $# # $ # # +# $ $ $ # $ $ # +##$ $ # @# $ # +# $ ########### ## +# # #.......# $# +# ## # ......# # +# # $........$ # +# # $ #.... ..# # +# $ $####$#### $# +# $ ### $ $ ## +# $ $ $ $ # +## ###### $ ##### # +# # # +################### +; 22 + + ####### + # # #### +##### $#$ # ## +#.. # # # # +#.. # $#$ # $#### +#. # #$ # # +#.. $# # $ # +#..@# #$ #$ # # +#.. # $# $# # +#.. # #$$#$ # ## +#.. # $# # $#$ # +#.. # # # # # +##. #### ##### # + #### #### ##### +; 23 + +############### +#.......... .#### +#..........$$.# # +###########$ # ## +# $ $ $ # +## #### # $ # # +# # ## # ## +# $# # ## ### ## +# $ #$### ### ## +### $ # # ### ## +### $ ## # # ## + # $ # $ $ $ # + # $ $#$$$ # # + # # $ ##### + # @## # # # + ############## +; 24 + +#### +# ############## +# # ..#......# +# # # ##### ...# +##$# ........# +# ##$###### #### +# $ # ######@ # +##$ # $ ###### # +# $ #$$$## # +# # #$#$### +# #### #$$$$$ # +# # $ # # +# # ## ### +# ######$###### $ # +# # # # +########## ##### +; 25 + + ####### + # # ##### +## # #...### +# $# #... # +# $ #$$ ... # +# $# #... .# +# # $######## +##$ $ $ # +## # $$ # # + ###### ##$$@# + # ## + ######## +; 26 + + ################# + #... # # ## +##..... $## # #$ # +#......# $ # # +#......# # # # # +######### $ $ $ # + # #$##$ ##$## + ## $ # $ # + # ## ### # ##$ # + # $ $$ $ $ # + # $ $##$ ###### + ####### @ ## + ###### +; 27 + + ##### + ##### # + ## $ $ #### +##### $ $ $ ##.# +# $$ ##..# +# ###### ###.. # +## # # #... # +# $ # #... # +#@ #$ ## ####...# +#### $ $$ ##..# + ## $ $ $...# + # $$ $ # .# + # $ $ #### + ###### # + ##### +; 28 + +##### +# ## +# $ ######### +## # # ###### +## # $#$#@ # # +# # $ # $ # +# ### ######### ## +# ## ..*..... # ## +## ## *.*..*.* # ## +# $########## ##$ # +# $ $ $ $ # +# # # # # # +################### +; 29 + + ########### + # # # +##### # $ $ # +# ##### $## # ## +# $ ## # ## $ # +# $ @$$ # ##$$$ # +## ### # ## # +## # ### #####$# +## # $ #....# +# ### ## $ #....## +# $ $ # #..$. # +# ## $ # ##.... # +##### ######...## + ##### ##### +; 30 + + #### + # ######### + ## ## # # + # $# $@$ #### + #$ $ # $ $# ## +## $## #$ $ # +# # # # $$$ # +# $ $ $## #### +# $ $ #$# # # +## ### ###$ # + # #.... # + ####......#### + #....#### + #...## + #...# + ##### +; 31 + + #### + ##### # + ## $# +## $ ## ### +#@$ $ # $ # +#### ## $# + #....#$ $ # + #....# $# + #.... $$ ## + #... # $ # + ######$ $ # + # ### + #$ ### + # # + #### +; 32 + +############ +## ## # +## $ $ # +#### ## $$ # +# $ # # +# $$$ # #### +# # # $ ## +# # # $ # +# $# $# # +# ..# #### +####.. $ #@# +#.....# $# # +##....# $ # +###..## # +############ +; 33 + + ######### + #.... ## + #.#.# $ ## +##....# # @## +# ....# # ## +# #$ ##$ # +## ### $ # + #$ $ $ $# # + # # $ $ ## # + # ### ## # + # ## ## ## + # $ # $ # + ###$ $ ### + # ##### + #### +; 34 + +############ ###### +# # # ###....# +# $$# @ .....# +# # ### # ....# +## ## ### # ....# + # $ $ # # #### + # $ $## # # +#### # #### # ## # +# # #$ ## # # +# $ $ # ## # ## +# # $ $ # # # +# $ ## ## # ##### +# $$ $$ # +## ## ### $ # + # # # # + ###### ###### +; 35 + + ##### +##### ###### # +# #### $ $ $ # +# $ ## ## ## ## +# $ $ $ $ # +### $ ## ## ## + # ##### #####$$ # + ##$##### @## # + # $ ###$### $ ## + # $ # ### ### + # $$ $ # $$ # + # # ## # + #######.. .### + #.........# + #.........# + ########### +; 36 + +########### +#...... ######### +#...... # ## # +#..### $ $ # +#... $ $ # ## # +#...#$##### # # +### # #$ #$ # + # $$ $ $ $## # + # $ #$#$ ##$ # + ### ## # ## # + # $ $ ## ###### + # $ $ # + ## # # # + #####@##### + ### +; 37 + + #### +####### @# +# $ # +# $## $# +##$#...# # + # $... # + # #. .# ## + # # #$ # + #$ $ # + # ####### + #### +; 38 + + ###### + #############....# +## ## ##....# +# $$## $ @##....# +# $$ $# ....# +# $ ## $$ # # ...# +# $ ## $ # ....# +## ##### ### ##.### +## $ $ ## . # +# $### # ##### ### +# $ # # +# $ #$ $ $### # +# $$$# $ # #### +# # $$ # +###### ### + ##### +; 39 + + ############ + # ## + # # #$$ $ # + #$ #$# ## @# + ## ## # $ # ## + # $ #$ # # + # # $ # # + ## $ $ ## # + # # ## $ # + # ## $$# # +######$$ # # +#....# ######## +#.#... ## +#.... # +#.... # +######### +; 40 + + ##### + ## ## + ## # + ## $$ # + ## $$ $ # + # $ $ # +#### # $$ ##### +# ######## ## # +#. $$$@# +#.# ####### ## ## +#.# #######. #$ $## +#........... # # +############## $ # + ## ## + #### +; 41 + + ######## + #### ###### + # ## $ $ @# + # ## ##$#$ $ $## +### ......# $$ ## +# ......# # # +# # ......#$ $ # +# #$...... $$# $ # +# ### ###$ $ ## +### $ $ $ $ # + # $ $ $ $ # + ###### ###### + ##### +; 42 + + ####### + ##### # #### + # # $ # + #### #$$ ## ## # +## # # ## ### +# ### $#$ $ $ # +#... # ## # # +#...# @ # ### ## +#...# ### $ $ # +######## ## # # + ######### +; 43 + + ##### + # # + # # ####### + # $@###### + # $ ##$ ### # + # #### $ $ # + # ##### # #$ #### +## #### ##$ # +# $# $ # ## ## # +# # #...# # +###### ### ... # + #### # #...# # + # ### # # + # # + ######### +; 44 + +##### #### +#...# # #### +#...### $ # +#....## $ $### +##....## $ # +###... ## $ $ # +# ## # $ # +# ## # ### #### +# $ # #$ $ # +# $ @ $ $ # +# # $ $$ $ ### +# ###### ### +# ## #### +### +; 45 + +########## +# #### +# ###### # ## +# # $ $ $ $ # +# #$ # +###$ $$# ### + # ## # $## + ##$# $ @# + # $ $ ### + # # $ # + # ## # # + ## ##### # + # # + #.......### + #.......# + ######### +; 46 + + #### + ######### ## +## $ $ ##### +# ## ## ##...# +# #$$ $ $$#$##...# +# # @ # ...# +# $# ###$$ ...# +# $ $$ $ ##....# +###$ ####### + # ####### + #### +; 47 + + ######### + #*.*#*.*# + #.*.*.*.# + #*.*.*.*# + #.*.*.*.# + #*.*.*.*# + ### ### + # # +###### ###### +# # +# $ $ $ $ $ # +## $ $ $ $ ## + #$ $ $ $ $# + # $@$ # + # ##### # + #### #### +; 48 + + #### + # ## + # ## + # $$ ## + ###$ $ ## + #### $ # +### # ##### # +# # #....$ # +# # $ ....# # +# $ # #.*..# # +### #### ### # + #### @$ ##$## + ### $ # + # ## # + ######### +; 49 + + ############ + ##.. # # + ##..* $ $ # + ##..*.# # # $## + #..*.# # # $ # +####...# # # # +# ## # # +# @$ $ ### # ## +# $ $ # # # +###$$ # # # # # + # $ # # ##### + # $# ##### # + #$ # # # # + # ### ## # + # # # ## + #### ###### +; 50 + + ######### + # # + # $ $$ $# +### # $ # +#.# $$ ## +#.### $ # +#.#. $ ## #### +#... $## $ # +#...$ $ # +#..###$### #@# +#..# # ### +#### ####### +; 51 + + ######## + #......# + #### #......# + # #########...# + # $ $ #...# + # # # # # # # +##### # # #@# # # +# # ### ### ## ## +# $ # $ $ $ # # +# $$$ $ # # +# # ###$###$## # +### # $ # # + ## $ # $ $ $ ### + # # ### ### ## + # $ # + # ########### + #### +; 52 + +#################### +# ### +# $# $ ## $ ## +# $### # $$ ## +#.### $ $ ## ## +#...# # # #$ # +#..##$$#### $ # # +#...# $ ## ### +#...$ ### # # # +##.. $# ## ##@ # +###.# # +#################### +; 53 + +#################### +# # # # #@# +# $ $ $ # # +## ###..## ### # +# #....#$# $### # +# $ #....# $ $ $ # +# #....# # # $ $ # +# ##..## #$# # +##$## ## # #$## +# $ $ # # # +# # # # # +#################### +; 54 + +#################### +# @## # ## +# ## $ $ ## +# ###....# # # ### +# #....# # # $ # +### #...# # # +## ##.# $ $ # +## $ $ ### # # ### +## $ # # $ # +#### $ $# # # # $ # +#### # # ## +#################### +; 55 + +#################### +# # ## # @### +## $ # $### # +##$# $ ##$# $ $ # +# $# $ ### +# ## $ ### #....# +# # $# # # # #....## +# $ $ # #....### +##$ ### $ #....#### +# # $ ###### +# # # ###### +#################### +; 56 + +#################### +#@ ### # # # +# # # # $ $ # +##### # $ $#$# # +#.#..# ##$ $ # +#..... $ # ## +#..... ###$##$### +#.#..# $ # # +##### # #$ $ # +##### # $ $ $ # +##### # # # # # +#################### +; 57 + +#################### +##... ## # # # +#.... $ ## # +#....# # #$###$ # +#...# # # # +##.# #$ # $## # +# # # $ $ ### $ # +# $ $ # # ## # +## # ## #$$# $# # # +# # $ $ # ## +# # # # @# +#################### +; 58 + +#################### +# # #@# ## ##### +# # # $ $ ##### +# # ###### $ ### +# # #....# $$ # +##$##$##....# # +# #....##$##$## +# $$ #....# # +# $ $ # # ### # +##### $ $ $ # +##### # # # ## +#################### +; 59 + +#################### +# # # # +# $ ## ### ## +##### ## $ $ # +##..## # # $ # # # +#.... $ ##$# ## +#.... $##### #$## +##..# # # # $ # +###.# # $ $ # @# +## $ $ # # #### +## ########### +#################### +; 60 + +#################### +# ###..### # +# $$ ###..### $@ # +# # ##......# $ # +# #......# $ # +#### ###..######$ # +# $$$ #..# # # +# $# $ $ $$ #$ # +# # ## $ ## # # +# $ $ ## $ $ # +# # ## ## # # +#################### +; 61 + +#################### +# # # # # # # +# @# # ## $ $ ## +#### # # # $ # +# # ## #$ ## ## # +# $ $ $ # +#..###$$## $##$ ## # +#..#.# # $ $ # # +#....# $$ ##$ #### +#....# ##### # +#...### ## # +#################### +; 62 + +#################### +#....# # # # +#....# # $ $ # +#.... ## $# # $#$ # +#...# $ $# $ # +#..#### # $ $$ # +# #### #### ### +# # # # +# ## # $ # $ $ # +# ## $ ## $ $ # +# @# # # # +#################### +; 63 + +#################### +#....### # +#....##### # #$# ## +#....### #$ $ # +#....### $ #$$## +## #### $# #$ $ # +## #### $ $ # # +#@ ####$###$## $ # +## # # $ # +## ### # $ #### +######## # # # +#################### +; 64 + +#################### +# # @#...### +# # ##...## +# # # ##$## ## ....# +# $ # $$$ ....# +###$### $$ ### ##.# +# $ # # #### +# $ # ### # # # +## #$## $ $$ # +# $ ## # # # # +# # # # # +#################### +; 65 + +#################### +# # #...#@ # +# # ....# # +# $ # #....# # +# ##$#### ##....# # +# $ $ # #...# # +# $$ # # # $$ # +### $$$# $$ $ # +# $ # # # $# # +# $# # $ # +# # # # # # +#################### +; 66 + +#################### +#####@###.##...## # +#####$ ..#...# # +#### ......# $ # +### $ #.....## # ## +## $$# ##### $ $ # +## $# $ ## $$ # +## # # # $ $ # +## $$ ### #$## # +## $# $ $ $ ## +### # # ### +#################### +; 67 + +#################### +#@ # # # +## ### ## #### # ## +# # # $$ # +# # # # $ # $ ## ## +# $ # #$$ # # +# ### # ## ## +#..#.# $ # $ # # +#..#.# $ # ## $$ # +#....## $$ $ # # +#.....## # # +#################### +; 68 + +#################### +# # # # ## +# $# $ $ ##...$ $ # +# $ # ##....# $ # +# ## $ ##....# $ # +# $ #....## $ # +# $## #...# # +# $$$##$## ### ## +# # # # # # # +# $ # $ ## # +# # #@ # +#################### +; 69 + +#################### +# # # # # # # +# $ $ $ # +## # #$###$## ## # +# $ $ # $ # +# ###$##$# # $ # +# # $ $ ###### $# +# $ $$ $ #@#.#...# +# # # # #.#...# +# ########## #.....# +# #.....# +#################### +; 70 + +#################### +# # # ## ## +# $# $ # ## # +# $ $ #..# $ # +# $ $ #....# # ## +# $# #......### $ # +# # #....# #$ # +# $ ####..# # # +## $ ## # # $ $## +### $ $#@$ $# # +#### # # # +#################### +; 71 + +#################### +# ....# #### +# .... # +# # ########## # +# #$ # ###..# +# $ #$$### #..# +# $ ### $ $ #..# +# $ # $ $ # ##..# +# # $$ # $ ## ## +#@## $# $ $ ## +## ## # ### +#################### +; 72 + +#################### +# # #@ # # +# $$ #$$# # # ## # +# # $ $ #$$ # # +## # # # # # # # +# ## # # +# # $ # # # # +# $ #$ # # $ #..# +##$ # #### #...# +# $ #....# +# # # #.....# +#################### +; 73 + +#################### +# # ##### # +## $ # #### $ # +#### $$ #..# # # +# $ $ ##..#### ## +# $ ###.... $$ # +# #$# ....# # $ # +# # # $ ..###$# # +# # $ #..# ## # +# $# #### # $## +# # # @# ## +#################### +; 74 + +#################### +# # # # #@# +# $ $ # $ # # +##$# $### # $$# # +# # #.### #$ $ # +# #$#....# # ### # +# $ #.....## # # +##$ #.#....#$$ $ # +# ######..## # # # +# $ $ ### # +# # # # # +#################### +; 75 + +#################### +# # # # #@## # # +# $ # +# ##$# ##### $ # ## +## ##.....# # # +##$##$#.....###$#$ # +# # ##.....# # ## +# $ ##..## # # +# $ # $ $ $$$ # +## $ $# # # $ # +# ## # # # +#################### +; 76 + +###### ##### +# # # # +# $ #### $ # +# $ $ # +# ###@###$ # +########## ### +#.. ## # +#.. ##$ # +#.. ## $ # +#.. ## $ # +#.. $ $ # +### ######### + #### +; 77 + + ########### + # # + # $ $ # +###### # $ ##### # +# ##### $ ##$# +# $ $ # +# ## ## # +# ##@##### ## # +# #### # ## ## +#....# # $ # +#....# # # +###### ####### +; 78 + +############# +# # +# ### $$ # +# # $ $ # +# $####$###### +# $ ## ##### +# $$ $ ...# +### ## $$# ...# + # ## # ...# + # # ...# + ###@############# + ### +; 79 + + ################# +###@## ...# +# # ...# +# $ # ...# +# $$ # ...# +## $ ###$########## + # ### $ # +## $ $ # +# $ # $ # +# $ # # +# $ # # +# # # +########### +; 80 + + ##### + ########## # + # # # + # $ $ $$ # + # ##### ## $ # + #$$ #$## $ # + # ### # ##$ # +###### ### $ $ # +#.... ## # +#.... ###### +#.... # +###########@## + ### +; 81 + + ###### + #### # + # ## # + # $ # +### #### ######## +# $ $ ## ...# +# $$ $$ ...# +# $ $## ...# +##@## ## ## ...# + ### $ ######## + # $$ # + # # # + ######### +; 82 + +####### ######### +# # # ## # +# ### # # $ # +# # $ ### $ # +# $$ ##$ # +# #### ## # +#@############ ## +###.. #####$ # + #.. #### # + #.. $$ # + #.. #### $ # + #.. # # # + ######## ##### +; 83 + +####### +# ########## +# # # ## +# $ # $ $ # +# $ # $ ## # +# $$ ##$ $ # +## # ## ####### +## # ## ...# +# #$ ...# +# $$ ...# +# ##@# ...# +################ +; 84 + +############ +# # ## +# $ $ # ###### +#### ##### # + #.. # #### # + #.#### #### # + #.... # $ #### + # ...# # $$$# ## +###.#### ## $@$ # +# ##### $ # # +# #.# $ $###$ # +# #.######## # $ # +# #.. ## $ # +# # ####### $ # # # +# # # ## +##### ########## +; 85 + +################ +# #@ # # +# # # # # $ $$# +# #...# #$$$ # +# ...# # $ $$## +# ##.## # ## # +# #... $ # +# ## ### ####### +# # #### +###### +; 86 + + ##### + #### ## ##### + # $ ### # + # $@$ $ $ # + # #$######## ## + # # $ # # + # # $ $ # # # +## # $# # ##### +# ## # # +# $ # ### # +##### ## #....# +# $ ....# +# #....# +################ +; 87 + +############# +#........#### +#...#### # ##### +#...# ### $ # +#...$$ $ $ # +# .# $ $# $ ## +#...# #$# $ # +#.# # $ $ # +#. #$###$####$# +## # $ $ # + # # $@$ # # + # # #### $ $# + # # ### # + # # $$ # ##### + # # # + ######### +; 88 + + ################## + # $ ...#.## + # ####..... # + # ####### #..... # + # # $ $ ##....## + # # $ # # ###...# + # # $@$ $ ##### # +## # $ $ $$ $ # +# #$# $# # $## # +# ## ## ## $ # # +# # $# $ $ # # +# # ####### +# ########$## # +# # $ # +######## ##### + ### # + #### +; 89 + +#################### +#..# # # +#.$ $ #$$ $## $## +#.$# ### ## ## # +# # $ # $$ $ # +# ### # # #$ #### +# ## # $ #@ # # +# $ $ ##.## $ # +# # $# $# $ ### +# # # # ### # +# ######## # # +# # #.#.# +##$########$# ...# +# .* # ##.#.# +# .*...* $ .....# +#################### +; 90 \ No newline at end of file diff --git a/scripts/games/sokoban_game.lua b/scripts/games/sokoban_game.lua index 503d3c0..09d2c94 100644 --- a/scripts/games/sokoban_game.lua +++ b/scripts/games/sokoban_game.lua @@ -1,189 +1,233 @@ - -- SOKOBAN GAME, by rnd, robots port - - - if not sokoban then - sokoban = {}; - local players = find_player(8); - if not players then error("sokoban: no player near") end - name = players[1]; - - -- self.show_form(name, - -- "size[2,1.25]".. - -- "label[0,0;SELECT LEVEL 1-90]".. - -- "field[0.25,1;1,1;LVL;LEVEL;1]".. - -- "button_exit[1.25,0.75;1,1;OK;OK]" - -- ) - state = 1 -- will wait for form receive otherwise game play - self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks") - - player_ = puzzle.get_player(name); -- get player entity - player must be present in area - player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}) -- reset player - - - self.spam(1) - sokoban.push_time = 0 - sokoban.blocks = 0;sokoban.level = 0; sokoban.moves=0; - imax = 0; jmax = 0 - - sokoban.load=0;sokoban.playername =""; sokoban.pos = {}; - SOKOBAN_WALL = "moreblocks:cactus_brick" - SOKOBAN_FLOOR = "default:silver_sandstone" - SOKOBAN_GOAL = "default:aspen_tree" - SOKOBAN_BOX = "basic_robot:buttonFFFFFF" - - load_level = function(lvl) - - local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1; - sokoban.pos = pos; - sokoban.playername = name - - if lvl == nil then return end - if lvl <0 or lvl >89 then return end - - local file = _G.io.open(minetest.get_modpath("basic_robot").."\\scripts\\sokoban.txt","r") - if not file then return end - local str = ""; local s; local p = {x=pos.x,y=pos.y,z=pos.z}; local i,j;i=0; - local lvl_found = false - while str~= nil do - str = file:read("*line"); - if str~=nil and str =="; "..lvl then lvl_found=true break end - end - if not lvl_found then file:close();return end - - sokoban.blocks = 0;sokoban.level = lvl+1; sokoban.moves=0; - imax=0; jmax = 0; - while str~= nil do - str = file:read("*line"); - if str~=nil then - if string.sub(str,1,1)==";" then - imax=i; - file:close(); - player_:set_physics_override({jump=0}) - player_:set_eye_offset({x=0,y=20,z=0},{x=0,y=0,z=0}); - return - end - i=i+1; - if string.len(str)>jmax then jmax = string.len(str) end -- determine max dimensions - for j = 1,string.len(str) do - p.x=pos.x+i;p.y=pos.y; p.z=pos.z+j; s=string.sub(str,j,j); - p.y=p.y-1; - if puzzle.get_node(p).name~=SOKOBAN_FLOOR then puzzle.set_node(p,{name=SOKOBAN_FLOOR}); end -- clear floor - p.y=p.y+1; - if s==" " and puzzle.get_node(p).name~="air" then puzzle.set_node(p,{name="air"}) end - if s=="#" and puzzle.get_node(p).name~=SOKOBAN_WALL then puzzle.set_node(p,{name=SOKOBAN_WALL}) end - if s=="$" then puzzle.set_node(p,{name=SOKOBAN_BOX});sokoban.blocks=sokoban.blocks+1 end - if s=="." then p.y=p.y-1;puzzle.set_node(p,{name=SOKOBAN_GOAL}); p.y=p.y+1;puzzle.set_node(p,{name="air"}) end - --starting position - if s=="@" then - player_:setpos({x=p.x,y=p.y-0.5,z=p.z}); -- move player to start position - --p.y=p.y-1;puzzle.set_node(p,{name="default:glass"}); - puzzle.set_node(p,{name="air"}) - p.y=p.y+1;puzzle.set_node(p,{name="air"}) - --p.y=p.y+2;puzzle.set_node(p,{name="default:ladder"}) - end - if s~="@" then p.y = pos.y+2;puzzle.set_node(p,{name="air"}); -- ceiling default:obsidian_glass - else --p.y=pos.y+2;puzzle.set_node(p,{name="default:ladder"}) - end -- roof above to block jumps - - end - end - end - - file:close(); - end - - clear_game = function() - local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1; - for i = 1, 20 do - for j = 1,20 do - local node = minetest.get_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}).name - if node ~= "default:silver_sandstone" then minetest.set_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}, {name = "default:silver_sandstone"}) end - node = minetest.get_node({x=pos.x+i,y=pos.y,z=pos.z+j}).name - if node ~= "air" then minetest.set_node({x=pos.x+i,y=pos.y,z=pos.z+j}, {name = "air"}) end - end - end - end - - end - - -if state == 1 then - clear_game() - load_level(20) - state = 0 - self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks") -else - - local ppos = player_:getpos() - if math.abs(ppos.y-sokoban.pos.y)~= 0.5 then minetest.chat_send_player(name,colorize("red", "SOKOBAN: " .. name .. " QUITS ! ")); - player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}); self.remove() end - - event = keyboard.get(); - - if event then - - local pname = event.puncher - if pname ~= name then goto quit end - local pos = {x=event.x, y = event.y, z = event.z}; - local p=player.getpos(pname);local q={x=pos.x,y=pos.y,z=pos.z} - p.x=p.x-q.x;p.y=p.y-q.y;p.z=p.z-q.z - if math.abs(p.y+0.5)>0 then goto quit end - if math.abs(p.x)>math.abs(p.z) then -- determine push direction - if p.z<-0.5 or p.z>0.5 or math.abs(p.x)>1.5 then goto quit end - if p.x+q.x>q.x then q.x= q.x-1 - else q.x = q.x+1 - end - else - if p.x<-0.5 or p.x>0.5 or math.abs(p.z)>1.5 then goto quit end - if p.z+q.z>q.z then q.z= q.z-1 - else q.z = q.z+1 - end - end - - - if minetest.get_node(q).name=="air" then -- push crate - sokoban.moves = sokoban.moves+1 - local old_infotext = minetest.get_meta(pos):get_string("infotext"); - minetest.set_node(pos,{name="air"}) - minetest.set_node(q,{name=SOKOBAN_BOX}) - minetest.sound_play("default_dig_dig_immediate", {pos=q,gain=1.0,max_hear_distance = 24,}) -- sound of pushing - local meta = minetest.get_meta(q); - q.y=q.y-1; - if minetest.get_node(q).name==SOKOBAN_GOAL then - if old_infotext~="GOAL REACHED" then - sokoban.blocks = sokoban.blocks -1; - end - meta:set_string("infotext", "GOAL REACHED") - else - if old_infotext=="GOAL REACHED" then - sokoban.blocks = sokoban.blocks +1 - end - --meta:set_string("infotext", "push crate on top of goal block") - end - end - - if sokoban.blocks~=0 then -- how many blocks left - --say("move " .. sokoban.moves .. " : " ..sokoban.blocks .. " crates left "); - else - say("games: ".. name .. " just solved sokoban level ".. sokoban.level .. " in " .. sokoban.moves .. " moves."); - player_:set_physics_override({jump=1}) - player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0}) - - local player = _G.minetest.get_player_by_name(event.puncher); - if player then - local inv = player:get_inventory(); - inv:add_item("main",_G.ItemStack("skyblock:sokoban 4 ")) - end - - local i,j; - for i = 1,imax do - for j=1,jmax do - minetest.set_node({x= sokoban.pos.x+i,y=sokoban.pos.y,z=sokoban.pos.z+j}, {name = "air"}); -- clear level - end - end - - sokoban.playername = ""; sokoban.level = 1 - end - ::quit:: - end + -- SOKOBAN GAME, by rnd, robots port + + if not sokoban then + load_player_progress = true -- true will disable level loading and load saved player progress + sokoban = {}; + local players = find_player(8); + if not players then error("sokoban: no player near") end + name = players[1]; + + _, text = book.read(1) + gamedata = minetest.deserialize(text); + + if gamedata and gamedata[name] then + say("#sokoban: welcome back " .. name .. ", loading level " .. gamedata[name].lvl+1) + else + say("sokoban: welcome new player " .. name) + end + + if not load_player_progress then + self.show_form(name, + "size[3,1.25]".. + "label[0,0;SELECT LEVEL 1-90]".. + "field[0.25,1;1.25,1;LVL;LEVEL;1]".. + "button_exit[1.25,0.75;1,1;OK;OK]" + ) + self.read_form() -- clear inputs + end + + state = 1 -- will wait for form receive otherwise game play + self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks") + + player_ = puzzle.get_player(name); -- get player entity - player must be present in area + player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}) -- reset player + + + self.spam(1) + sokoban.push_time = 0 + sokoban.blocks = 0;sokoban.level = 0; sokoban.moves=0; + imax = 0; jmax = 0 + + sokoban.load=0;sokoban.playername =""; sokoban.pos = {}; + SOKOBAN_WALL = "moreblocks:cactus_brick" + SOKOBAN_FLOOR = "default:silver_sandstone" + SOKOBAN_GOAL = "default:aspen_tree" + SOKOBAN_BOX = "basic_robot:buttonFFFFFF" + + + load_level = function(lvl) + + local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1; + sokoban.pos = pos; + sokoban.playername = name + + if lvl == nil then return end + if lvl <0 or lvl >89 then return end + + local file = _G.io.open(minetest.get_modpath("basic_robot").."/scripts/games/sokoban.txt","r") + if not file then return end + local str = ""; local s; local p = {x=pos.x,y=pos.y,z=pos.z}; local i,j;i=0; + local lvl_found = false + while str~= nil do + str = file:read("*line"); + if str~=nil and str =="; "..lvl then lvl_found=true break end + end + if not lvl_found then file:close();return end + + sokoban.blocks = 0;sokoban.level = lvl+1; sokoban.moves=0; + imax=0; jmax = 0; + while str~= nil do + str = file:read("*line"); + if str~=nil then + if string.sub(str,1,1)==";" then + imax=i; + file:close(); + player_:set_physics_override({jump=0}) + player_:set_eye_offset({x=0,y=20,z=0},{x=0,y=0,z=0}); + return + end + i=i+1; + if string.len(str)>jmax then jmax = string.len(str) end -- determine max dimensions + for j = 1,string.len(str) do + p.x=pos.x+i;p.y=pos.y; p.z=pos.z+j; s=string.sub(str,j,j); + p.y=p.y-1; + if puzzle.get_node(p).name~=SOKOBAN_FLOOR then puzzle.set_node(p,{name=SOKOBAN_FLOOR}); end -- clear floor + p.y=p.y+1; + if s==" " and puzzle.get_node(p).name~="air" then puzzle.set_node(p,{name="air"}) end + if s=="#" and puzzle.get_node(p).name~=SOKOBAN_WALL then puzzle.set_node(p,{name=SOKOBAN_WALL}) end + if s=="$" then puzzle.set_node(p,{name=SOKOBAN_BOX});sokoban.blocks=sokoban.blocks+1 end + if s=="." then p.y=p.y-1;puzzle.set_node(p,{name=SOKOBAN_GOAL}); p.y=p.y+1;puzzle.set_node(p,{name="air"}) end + --starting position + if s=="@" then + player_:set_pos({x=p.x,y=p.y-0.5,z=p.z}); -- move player to start position + --p.y=p.y-1;puzzle.set_node(p,{name="default:glass"}); + puzzle.set_node(p,{name="air"}) + p.y=p.y+1;puzzle.set_node(p,{name="air"}) + --p.y=p.y+2;puzzle.set_node(p,{name="default:ladder"}) + end + if s~="@" then p.y = pos.y+2;puzzle.set_node(p,{name="air"}); -- ceiling default:obsidian_glass + else --p.y=pos.y+2;puzzle.set_node(p,{name="default:ladder"}) + end -- roof above to block jumps + + end + end + end + + file:close(); + end + + clear_game = function() + local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1; + for i = 1, 20 do + for j = 1,20 do + local node = minetest.get_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}).name + if node ~= "default:silver_sandstone" then minetest.set_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}, {name = "default:silver_sandstone"}) end + node = minetest.get_node({x=pos.x+i,y=pos.y,z=pos.z+j}).name + if node ~= "air" then minetest.set_node({x=pos.x+i,y=pos.y,z=pos.z+j}, {name = "air"}) end + end + end + end + + force_load = function() + local lvl = 0 -- 1st level at idx 0 - sokoban level 1 + clear_game() + if gamedata and gamedata[name] then lvl = gamedata[name].lvl end -- load next level to play + load_level(lvl) + state = 0 + self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand can't pull. goal is to get all white boxes pushed on aspen blocks") + + end + + if load_player_progress then + force_load() -- this prevents selecting custom level and loads players progress if any + end + + end + + +if state == 1 then -- wait to load game + sender,fields = self.read_form() + if fields then + if fields.OK then + local lvl = tonumber(fields.LVL or 1)-1; + clear_game() + load_level(lvl) + state = 0 + self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks") + else + self.remove() + end + end + + +else -- game + + local ppos = player_:get_pos() + if math.abs(ppos.y-sokoban.pos.y)~= 0.5 then minetest.chat_send_player(name,colorize("red", "SOKOBAN: " .. name .. " QUITS ! ")); + player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}); self.remove() end + + event = keyboard.get(); + + if event then + + local pname = event.puncher + if pname ~= name then goto quit end + local pos = {x=event.x, y = event.y, z = event.z}; + if minetest.get_node(pos).name == "air" then return end -- ignore move, block wasnt punched + local p=player.getpos(pname);local q={x=pos.x,y=pos.y,z=pos.z} + p.x=p.x-q.x;p.y=p.y-q.y;p.z=p.z-q.z + if math.abs(p.y+0.5)>0 then goto quit end + if math.abs(p.x)>math.abs(p.z) then -- determine push direction + if p.z<-0.5 or p.z>0.5 or math.abs(p.x)>1.5 then goto quit end + if p.x+q.x>q.x then q.x= q.x-1 + else q.x = q.x+1 + end + else + if p.x<-0.5 or p.x>0.5 or math.abs(p.z)>1.5 then goto quit end + if p.z+q.z>q.z then q.z= q.z-1 + else q.z = q.z+1 + end + end + + + if minetest.get_node(q).name=="air" then -- push crate + sokoban.moves = sokoban.moves+1 + local old_infotext = minetest.get_meta(pos):get_string("infotext"); + minetest.set_node(pos,{name="air"}) + minetest.set_node(q,{name=SOKOBAN_BOX}) + minetest.sound_play("default_dig_dig_immediate", {pos=q,gain=1.0,max_hear_distance = 24,}) -- sound of pushing + local meta = minetest.get_meta(q); + q.y=q.y-1; + if minetest.get_node(q).name==SOKOBAN_GOAL then + if old_infotext~="GOAL REACHED" then + sokoban.blocks = sokoban.blocks -1; + end + meta:set_string("infotext", "GOAL REACHED") + else + if old_infotext=="GOAL REACHED" then + sokoban.blocks = sokoban.blocks +1 + end + --meta:set_string("infotext", "push crate on top of goal block") + end + end + + if sokoban.blocks~=0 then -- how many blocks left + --say("move " .. sokoban.moves .. " : " ..sokoban.blocks .. " crates left "); + else + say("games: ".. name .. " just solved sokoban level ".. sokoban.level .. " in " .. sokoban.moves .. " moves."); + + if not gamedata then gamedata = {} end + gamedata[name] = {lvl = sokoban.level}; -- increased level + book.write(1,"sokoban players", minetest.serialize(gamedata)) + + player_:set_physics_override({jump=1}) + player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0}) + + local player = _G.minetest.get_player_by_name(event.puncher); + if player then + local inv = player:get_inventory(); + inv:add_item("main",_G.ItemStack("default:gold_ingot " .. 2*sokoban.level)) + end + + local i,j; + for i = 1,imax do + for j=1,jmax do + minetest.set_node({x= sokoban.pos.x+i,y=sokoban.pos.y,z=sokoban.pos.z+j}, {name = "air"}); -- clear level + end + end + + sokoban.playername = ""; sokoban.level = 1 + end + ::quit:: + end end \ No newline at end of file diff --git a/scripts/music/music_playback.lua b/scripts/music/music_playback.lua new file mode 100644 index 0000000..bc6e9f8 --- /dev/null +++ b/scripts/music/music_playback.lua @@ -0,0 +1,121 @@ +--rnd music robot v12/10/2019, 22:00 +--tuning by Jozet + +--@F2 E D C G3 G F2 E D C G3 G F2 A A F E G G E D E F D C3 C + +if not init then + _G.minetest.forceload_block(self.pos(),true) + song = nil + self.listen(1) + local ent = _G.basic_robot.data[self.name()].obj:get_luaentity(); + dt = 0.001 + ent.timestep = dt -- time step + + pitches = { -- definitions of note pitches + 0.59, + 0.62, + 0.67, + 0.7, + 0.74, + 0.79, + 0.845, + 0.89, + 0.945, + 1, + 1.05, + 1.12, + 1.19, + 1.25, + 1.34, + 1.41, + 1.48, + 1.58, + 1.68, + 1.78, + 1.88, + 2.0, + 2.15, + 2.28 + } + + notenames = { -- definition of note names + C = 1, + Db = 2, + D = 3, + Eb = 4, + E = 5, + F = 6, + Gb = 7, + G = 8, + Ab = 9, + A = 10, + Bb = 11, + B = 12, + ["2C"] = 13, + ["2Db"] = 14, + ["2D"] = 15, + ["2Eb"] = 16, + ["2E"] = 17, + ["2F"] = 18, + ["2Gb"] = 19, + ["2G"] = 20, + ["2Ab"] = 21, + ["2A"] = 22, + ["2Bb"] = 23, + ["2B"] = 24 + } + +say("available notes : C Db D Eb E F Gb G Ab A Bb B 2C 2Db 2D 2Eb 2E 2F 2Gb 2G 2Ab 2A 2Bb 2B . example: @A5 A A A A10 A A A , number after note name denotes change of tempo. To replay last song do @R") + + + songdata = {} + t=0 -- current timer + idx = 0 -- current note to play + tempo = 1 -- default pause + + parse_song = function() + songdata = {} -- reset song data + for notepart in string.gmatch(song,"([^ ]+)") do -- parse song + if notepart then + local note,duration; + note,duration = _G.string.match(notepart,"(%d*%a+)(%d*)") + if not duration or duration == "" then duration = 0 end + songdata[#songdata+1] = {notenames[note], tonumber(duration)} + end + end + tempo = 3; -- default tempo + t=0 + idx = 0 -- reset current idx + end + + init = true + +end + +if not song then + speaker,msg = self.listen_msg() + if msg and string.find(msg,"@") then + song = string.sub(msg,2) + if song ~= "R" then -- R for replay + parse_song() + self.label("playing song by " .. speaker.. ", " .. song ) + else + idx = 0; t = 0; -- try play again! + end + end +elseif t<=1 then -- play next note! + idx = idx+1 + if idx>#songdata then + self.label("song " .. song .. ". ended.") + song = nil + else + if songdata[idx][2]>0 then tempo = songdata[idx][2] end + t = tempo; + self.sound( "piano",{ + pos = self.pos(), gain = 1000.0, pitch = pitches[ songdata[idx][1] ], + max_hear_distance = 1000000 + }) + end +else + t=t-1 +end \ No newline at end of file diff --git a/scripts/programming/hash_table_implementation.lua b/scripts/programming/hash_table_implementation.lua new file mode 100644 index 0000000..e2af33d --- /dev/null +++ b/scripts/programming/hash_table_implementation.lua @@ -0,0 +1,108 @@ +if not get_hash then + + get_hash = function(s,p) + if not s then return end + local h = 0; local n = string.len(s);local m = 4; -- put 4 characters together + local r = 0;local i = 0; + while i maxlen then maxlen = length; maxidx = i end + count = count + 1 + end + end + + if maxlen>0 then + local data = hashdb[maxidx]; + say("number of used hash entries is " .. count .. ", average " .. (step/count) .. " entries per hash, ".. + " max length of list is " .. maxlen .. " at hash ".. maxidx )--.. " : " .. + --string.gsub(_G.dump(data),"\n","") ) + + end + end + + -- LOAD DICTIONARY WORDS into hashtable + + lang = "german" + + fname = "F:\\games\\rpg\\minetest-0415server\\mods\\basic_translate\\"..lang; + local f = _G.assert(_G.io.open(fname, "r"));local dicts = f:read("*all");f:close() + + step = 0; maxwords = 10000; + i=0 + dict = {}; -- for comparison + + while(step 2*n then return {} end + j=perm[j]; + visited[j] = 1; + if j and j~=cycle[1] then cycle[#cycle+1]=j else break end + end + + i=n+1; + for k=1,n do -- find smallest non visited yet + if not visited[k] and k cycles = " .. arr2string(cycles) ) + +end \ No newline at end of file diff --git a/scripts/programming/radix_sort.lua b/scripts/programming/radix_sort.lua new file mode 100644 index 0000000..6e3462b --- /dev/null +++ b/scripts/programming/radix_sort.lua @@ -0,0 +1,104 @@ +if not integer_sort then + -- sorts input according to keys, changes ordering to permutation corresponding to new order + integer_sort = function(input, keys, ordering,temp_ordering, m) -- input, keys same length n, m = how many keys, O(2*n+m) + + local n = #input; + local freq = {} -- frequencies of keys + local kpos = {} -- position of keys in sorted array + + for i=1,n do -- count how many occurences - O(n) + local key = keys[ordering[i]]+1; + freq[key]=(freq[key] or 0)+1 + temp_ordering[i]=ordering[i]; + end + + local curpos = 1;kpos[1]=1; + for i =1,m-1 do -- determine positions of keys in final sorted array - O(m) + curpos = curpos + (freq[i] or 0); + kpos[i+1]=curpos -- {2=3, 3 = 6,..., n-1 = 321} + end + + -- actually place values here + for i = 1,n do -- O(n) + local key = keys[temp_ordering[i]]+1; + local pos = kpos[key]; + ordering[pos] = temp_ordering[i]; + kpos[key]=kpos[key]+1; -- move to next spot for that key place + end + end + + permutate = function(input,ordering) + local output = {}; + for i =1,#input do + output[i] = input[ordering[i]] + end + return output + end + + get_digits = function(Num,d,base) -- number, how many digits, what base + local digits = {}; + local num = Num; + local r; + for i = 1, d do + r = num % base; + digits[#digits+1] = r; + num = (num-r)/base + end + return digits + end + + dumparr = function(array) + if _G.type(array) ~= "table" then return array end + local ret = "{"; + for i =1,#array-1 do + ret = ret .. dumparr(array[i]) .. "," + end + ret = ret .. dumparr(array[#array]) + return ret .. "}" + end + + + radix_sort = function(input,d,base) -- array of numbers; base is also number of keys = m, d = how many steps of sorting = number of digits for single number + + out = out .."\nRADIX SORT\n\narray to be sorted " .. dumparr(input) + + local n = #input; + local ordering = {}; local temp_ordering = {}; for i = 1, n do ordering[i]=i end + local keys = {}; + local keylist = {}; + for i = 1,n do + keylist[i] = get_digits(input[i],d,base) + end + + + out = out .."\nlist of keys - ".. d .. " digits of base " .. base .. " expansion of numbers : \n" .. + dumparr(keylist) + + for step = 1, d do + for i =1,n do + keys[i] = keylist[i][step]; + end + integer_sort(input, keys, ordering, temp_ordering, base) + + out = out .."\n"..step .. ". pass integer_sort : " .. dumparr(permutate(input,ordering)) + end + + out = out .. "\nradix sort final result : " .. dumparr(permutate(input,ordering)) + + end + + + --input = {"a","b","c","d","e"} + --keys = {5,3,4,1,2} + --ordering = {}; temp_ordering = {}; for i = 1, #input do ordering[i]=i end + --m=5; + + --integer_sort(input, keys, ordering, temp_ordering,m); + --say(string.gsub(_G.dump(ordering),"\n","")) + --say(string.gsub(_G.dump(permutate(input,ordering)),"\n","")) + out = ""; self.label("") + + input = {23,42,15,8,87}; + radix_sort(input,5,3) -- d, base + self.display_text(out,60,3) +end \ No newline at end of file diff --git a/scripts/programming/room_finder.lua b/scripts/programming/room_finder.lua new file mode 100644 index 0000000..0ac7afd --- /dev/null +++ b/scripts/programming/room_finder.lua @@ -0,0 +1,54 @@ +-- room finder by rnd (45 minutes) +-- given starting position it explores 3d world to find enclosed area of room, up to max 2000 nodes + +if not init then init = true +local rpos = self.pos() +local radius = 16 +bpos = minetest.find_node_near(rpos, radius,"beds:bed_bottom");bpos.y=bpos.y+1 -- bed +--say(bpos.x .. " " .. bpos.y .. " " .. bpos.z) + + + walls = { -- define possible walls here + ["default:cobble"]=true, ["default:wood"] = true, + ["default:obsidian_glass"]=true,["default:glass"] = true, + ["doors:door_obsidian_glass_a"]=true,["doors:door_obsidian_glass_b"]=true, + ["doors:hidden"] = true, + } + + +local find_room = function(bpos) + + + local cbdata = {bpos} -- explore boundary + local cdata = {[minetest.hash_node_position(bpos)] = true} -- db of room pos + local dirs = {{x=-1,y=0,z=0},{x=1,y=0,z=0},{x=0,y=-1,z=0},{x=0,y=1,z=0},{x=0,y=0,z=-1},{x=0,y=0,z=1}} + local ccount = 1 + + crawl_step = function() + local pos = cbdata[#cbdata];cbdata[#cbdata] = nil; + for i = 1,#dirs do + local p = {x=pos.x+dirs[i].x,y=pos.y+dirs[i].y,z=pos.z+dirs[i].z} + if not cdata[minetest.hash_node_position(p)] and not walls[minetest.get_node(p).name] then + cdata[minetest.hash_node_position(p)] = true + cbdata[#cbdata+1] = p + ccount = ccount +1 + end + end + end + + + local maxsteps = 2000; + local step = 0 + + while #cbdata>0 and step " .. msg) end keywords = { - {"tp", 14}, + {"tpr", 14}, + {"tpy",19}, {"help", {"robot",6},{"",1} }, {"how", - {"play",1},{"robot", 6},{"stone",4},{"tree",3},{"wood",3},{"lava",5},{"cobble",4},{"dirt",10}, - {"do i get",1},{"do i make",1}, {"to get",1} + {"play",1},{"island",17},{"robot", 6},{"stone",4},{"tree",3},{"wood",3},{"lava",5},{"mossy",16},{"cobble",4},{"dirt",10},{"clay",23}, + {"do i get",1},{"do i make",1}, {"to get",1},{"farm",15}, }, {"i need", {"wood",3} @@ -19,40 +43,77 @@ if not init then {"hello",2}, -- words matched must appear at beginning {"hi",2}, {"back",7}, - {" hard",{"",9}}, -- word matched can appear anywhere + {" 'hard",{"",9}}, -- word matched can appear anywhere {" died", {"",9}}, {" die",{"",8}}, {" dead",{"",8}}, - {"rnd",{"",11}}, - {"bye",{"",12}}, +-- {"rnd",{"",11}}, + {"^bye",{"",12}}, {"!!",{"",9}}, {"calc", 13}, + {"day",18}, + {"RESET_LEVEL",20}, + {"YES",21}, } answers = { "%s open inventory, click 'Quests' and do them to get more stuff", --1 "hello %s", - "do the dirt quest to get sticks then do sapling quest", + "you can get wood from bush stems or trees. tree sapling needs to be planted on fertilized composter with 10 N nutrient", "get pumice from lava and water. then search craft guide how to make cobble", - "you get lava as compost quest reward or with grinder", -- 5 + "you get lava as dig tree quest reward. warning - put lava away from flammable blocks. full composter does not burn - its safe.", -- 5 "you have to write a program so that robot knows what to do. for list of commands click 'help' button inside robot.", "wb %s", "dont die, you lose your stuff and it will reset your level on level 1", "you suck %s!", -- 9 - "to get dirt craft composter and use it with leaves", -- 10 + "to get dirt craft composter, put gravel on it, punch it and wait until its ready. then punch again to get dirt.", -- 10 "rnd is afk. in the meantime i can answer your questions", "bye %s", function(speaker,msg) -- 13, calc - local expr = string.sub(msg,5); if string.find(expr,"%a") then return end - local res = _G.loadstring("return " .. expr)(); say(expr .. " = " .. res) + local expr = string.sub(msg,5); + if string.find(expr,"%a") then return end; + if string.find(expr,"{") then return end; + local exprfunc = _G.loadstring("return " .. expr); + local res = exprfunc and exprfunc() or say("error in expression: " .. expr); + if type(res) == "number" then say(expr .. " = " .. res) end end, - function(speaker,msg) -- 14,tp - local p1 = minetest.get_player_by_name(speaker); - local p2 = minetest.get_player_by_name(string.sub(msg,4)); + function(speaker,msg) -- 14,tpr + tpr[1] = speaker + tpr[2] = string.sub(msg,5) or ""; + minetest.chat_send_player(tpr[2], "#TPR: " .. speaker .. " wants to teleport to you. say \\tpy") + end, + "to make farm craft composter, put leaves on it,punch and wait until its ready. Then right click composter to see if it has enough nutrients. If yes, plant your crops on top.", -- 15 +"put cobble near water and wait.", --16 + "you get your own island when you finish all quests on level 1. before that your island will be reused by other players.", --17 + function(speaker,msg) -- 18, day + minetest.set_timeofday(0.25); say("time set to day") + end, + function(speaker,msg) -- 19,tpy + if tpr[2] ~= speaker then return end; + local p1 = minetest.get_player_by_name(tpr[1]); + local p2 = minetest.get_player_by_name(tpr[2]);tpr[2] = "" if p1 and p2 then p1:setpos(p2:getpos()) end end, + function(speaker,msg) -- 20,RESET_LEVEL + say("this will reset your skyblock level to beginning of level 1. are you sure " .. speaker .. "? say YES") + qa[1] = speaker; qa[2] = 22 + end, + function(speaker,msg) -- 21,yes to some question + if qa[1] ~= speaker then return end + answers[qa[2]](speaker,msg) + qa[1] = "" + end, + + function(speaker,msg) -- 22 reset level + say("your level is reset " .. speaker .. ". have a nice day.") + end, + + "you get clay by grinding dirt in grinder ( basic_machines ). Either craft grinder from constructor or get as level 3 1st quest reward" -- 23 + } + tpr = {"",""} -- used for teleport + qa = {"",1}; -- speaker end speaker,msg = self.listen_msg(); @@ -71,7 +132,6 @@ if msg then end end end - end end diff --git a/scripts/utils/helper_chat_bot2.lua b/scripts/utils/helper_chat_bot2.lua new file mode 100644 index 0000000..310b52b --- /dev/null +++ b/scripts/utils/helper_chat_bot2.lua @@ -0,0 +1,300 @@ +--[[ + help bot 2 + + 'how to make/craft X' = 'how' 'make' 'x' + pattern match: + word1 word2 ... wordn + + word1 - start of pattern + + patterns = { + ["pattern_start"] = { + { -- list of possible sequences + {{"pattern2", ..},{"pattern3", ..}, ..},"response"} -- { patternsequence, "response"} + }, + ... + } + +--]] + +if not init then init = true + _G.minetest.forceload_block(self.pos(),true) + patterns = { + ["calc"] = { {{}, "calc"} }, + ["day"] = { {{}, "day"} }, + ["day4ever"] = { {{}, "day4ever"} }, + ["crazy_mode"] = { {{}, "crazy_mode"} }, + ["normal_mode"] = { {{}, "normal_mode"} }, + + ["hi"] = { {{}, "greeting"} }, + ["hey"] = { {{}, "greeting"} }, + ["hello"] = { {{}, "greeting"} }, + + ["bye"] = { {{}, "goodbye"} }, + + ["help"] = { + { + {{"play"}}, "play_help" + }, + { + {}, "help_general" + }, + }, + + ["tell"] = { + { + {{"bot"}}, "tell_bot" + }, + }, + + ["ask"] = { + { + {{"bot"}}, "ask_bot" + }, + }, + + ["how"] = { + { + {{"plant"},{"tree"}}, "plant_tree_help" + }, + { + {{"play"}}, "play_help" + }, + { + {{"get"},{"island"}}, "getting_island_help" + }, + { + {{"get"},{"tree"}}, "plant_tree_help" + }, + { + {{"get"},{"wood"}}, "getting_wood_help" + }, + { + {{"get"},{"clay"}}, "getting_clay_help" + }, + { + {{"get"},{"stone"}}, "getting_stone_help" + }, + { + {{"get","make"},{"dirt"}}, "getting_dirt_help" + }, + { + {{"get","find"}}, "getting_stuff_help" + }, + { + {{"craft"}}, "craft_help" + }, + { + {{"farm"}}, "farm_help" + }, + { + {{"robot"}}, "robot_help" + }, + { + {{"craft","make"}}, "craft_help" + }, + }, + + ["tpr"] = { {{}, "tpr"} }, + ["tpy"] = { {{}, "tpy"} }, + } + + bot_knowledge = {}; -- bot knows stuff players tell it + chat_data = {} -- various player data + --[[ + [name] = {greet = true} -- already said hi + tpr = requester -- someone want to teleport to 'name' + --]] + + responses = { + ["greeting"] = function(name,imatch, iendmatch, words) + if imatch>2 then return end + if not chat_data[name] then chat_data[name] = {greet = true} elseif chat_data[name].greet then return end + + chat_data[name].greet = true -- remember we said hi + + local ipdata = _G.helloip.players[name]; local country = 'en' + if ipdata then country = ipdata.country end + + local greetings = { + ["ZZ"] = "hi ", + ["EN"] = "hello and welcome ", + ["DE"] = "hallo und willkommen ", + ["FR"] = "bonjour et bienvenue ", + ["PL"] = "czesc i witaj ", + ["RU"] = "privet i dobro pozhalovat' ", + ["NL"] = "hallo en welkom ", + } + talk((greetings[country] or greetings["EN"]) .. name ) + end, + + ["goodbye"] = function(name,imatch,iendmatch, words) + if imatch>2 then return end + talk("see you later " .. name) + end, + ["farm_help"] = function(name) + _G.basic_robot.gui["farming_help"].show(name) + end, + ["robot_help"] = function(name) + _G.basic_robot.gui["robot_help"].show(name) + end, + ["craft_help"] = function(name) + local text = "Build 3x3 normal wood table on the ground. Drop items in shape of crafting recipe on the table. Then use craft tool on table to craft item.\n\nIt is important to be looking in direction toward top of recipe.\n\nYou can craft more than 1 item - try dropping 5 of each items in recipe to craft 5 items ...\n\nUsing craft tool on bush will give you wood." + local form = "size[8,4.5] textarea[0,0.25;9,5.5;msg;CRAFT HELP;"..text .."]" + minetest.show_formspec(name, "basic_craft_help_text",form) + end, + ["plant_tree_help"] = function(name) talk(name .. " you need to plant tree on composter. insert 10 leaves in composter first by putting leaves on top and punching composter. repeat this 10 times.") end, + ["play_help"] = function(name) talk(name .. " open inventory and read crafting and farming help. Look at quests too and do them to progress. You can make island larger with leaves.") end, + ["getting_island_help"] = function(name) talk(name .. " you need to complete level 1 to get your own island. for now your island only temporary.") end, + ["help_general"] = function(name) talk("what you need help with " .. name .. " ?") end, + ["getting_clay_help"] = function(name) talk("you get clay by grinding dirt in grinder ( basic_machines ). Either craft grinder from constructor or get it as level 3 1st quest reward") end, + ["getting_wood_help"] = function(name) talk("you can get wood from bush stems or trees - use craft tool on bush stem.") end, + ["getting_stone_help"] = function(name) talk("get pumice from lava and water. then search craft guide how to make cobble") end, + ["getting_dirt_help"] = function(name) talk("place gravel on composter and punch composter.when composting done punch again to get out dirt.") end, + + ["tpr"] = function(name,imatch, iendmatch, words) + local target = words[2]; if not target then return end + if not minetest.get_player_by_name(target) then return end + local tdata = chat_data[target]; + if not tdata then chat_data[target] = {}; tdata = chat_data[target] end + tdata.tpr = name + talk(name .. " wants to teleport to you - say tpy", target) + end, + ["tpy"] = function(name,imatch, iendmatch, words) + local data = chat_data[name]; + if not data then chat_data[name] = {}; data = chat_data[name] end + local requester = data.tpr; if not requester then return end + + local rpl = minetest.get_player_by_name(requester) + if not rpl then return end + rpl:set_pos( minetest.get_player_by_name(name):get_pos() ) + data.tpr = nil + end, + ["calc"] = function(name,imatch,__,words) -- calculator + if imatch~=1 then return end + local expr = string.sub(table.concat(words," "),5) + if string.find(expr,"%a") then return end; + if string.find(expr,"{") then return end; + local exprfunc = _G.loadstring("return " .. expr); + local res = exprfunc and exprfunc() or say("error in expression: " .. expr); + if type(res) == "number" then talk(expr .. " = " .. res) end + end, + + ["ask_bot"] = function(name,imatch, iendmatch, words) + if imatch>1 then return end + local expr = string.sub(table.concat(words," "),9) + --i = string.find(expr," ") + --if i then expr = string.sub(expr,1,i-1) end + local answer = bot_knowledge[expr]; + if not answer then + talk("i don't know about " .. expr) + else + talk(expr .. " " .. answer) + end + end, + + ["tell_bot"] = function(name,imatch, iendmatch, words) + if imatch>1 then return end + local expr = string.sub(table.concat(words," "),10) + local i = string.find(expr, " ") + if not i then + talk("what did you want to tell me about " .. expr .. " ?") + else + local dwords = {" is ", " are "} + local j; + for k=1,#dwords do j = string.find(expr,dwords[k]);if j then break end end + local topic, value + if j then + topic = string.sub(expr,1,j-1) value = string.sub(expr,j) + else + topic = string.sub(expr,1,i-1) value = string.sub(expr,i+1) + end + bot_knowledge[topic] = value + talk("i will remember what you told me about " .. topic, name) + end + end, + + ["day"] = function() + minetest.set_timeofday(0.25); talk("time set to day") + end, + ["day4ever"] = function() + minetest.set_timeofday(0.25); talk("forever day") + minetest.settings:set("time_speed",0); + end, + ["crazy_mode"] = function() + talk("crazy mode ON") + minetest.settings:set("time_speed",500000); + end, + ["normal_mode"] = function() + talk("crazy mode OFF") + minetest.settings:set("time_speed",72); + end + + } + + talk = function(text,name) + if not name then + minetest.chat_send_all(" " .. text) + else + minetest.chat_send_player(name," " .. text) + end + end + + check_msg = function(text) + local level = 0; + local pattern; + local words = {} + for word in string.gmatch(text,"[^%s,?:]+") do words[#words+1] = word end + + local imatch + for i = 1,#words do + if patterns[words[i]] then pattern = patterns[words[i]] imatch = i end -- perhaps this will match? + end + if not imatch then return end + --say("possible match: " .. words[imatch]) + + -- check out all pattern possibilities + local jmatch + local iendmatch = imatch + + for j = 1, #pattern do + if jmatch then break end -- already got it + + local level = 1 + local pat = pattern[j] + --say("pattern " .. j .. " length :" .. #pat[1]+1) + if #pat[1] == 0 then -- empty pattern, we have match + jmatch = j; break + end + + for i = imatch+1, #words do -- only search from next word + if jmatch then break end + for k = 1, #pat[1][level] do + if words[i] == pat[1][level][k] then + level = level +1; + if #pat[1]+1 == level then jmatch = j iendmatch = i end + break + end + end + end + end + + if jmatch then + local responseid = pattern[jmatch][2] + --say("match: " .. words[imatch] .. ", response " .. responseid) + return responses[responseid],imatch,iendmatch, words + end + + end + + self.listen(1); +--self.label("help bot - ask me questions") +self.label("") +end + +speaker,msg = self.listen_msg() +if msg then + local response, imatch, iendmatch, words + response, imatch,iendmatch, words = check_msg(msg) + if response then response(speaker, imatch, iendmatch, words) end +end \ No newline at end of file diff --git a/scripts/utils/text_printer.lua b/scripts/utils/text_printer.lua new file mode 100644 index 0000000..9f2d8ae --- /dev/null +++ b/scripts/utils/text_printer.lua @@ -0,0 +1,43 @@ +-- text printer by rnd +-- instruction: go to position where text starts and look in desired direction +-- say: t TEXT\nTEXT... + +if not init then init = true + name = "_" + + get_dir = function(view) + local dir + if math.abs(view.x)>math.abs(view.z) then + if view.x>0 then dir = {1,0} else dir = {-1,0} end + else + if view.z>0 then dir = {0,1} else dir = {0,-1} end + end + return dir + end + + render_text = function(text) + local player = minetest.get_player_by_name(name) + local pos = player:get_pos() + local dir = get_dir(player:get_look_dir()) + local i=0; + local x=0;local y=0 + while iwidth then newline = true end + else + ret[#ret+1]=c;x=0; -- adding new line character + end + end + + if newline then -- add word and space between words if not in newline + ret[#ret+1] = "\n"..word.. " "; + x=x+cwidths[" "] + x=x-xw; -- word width in new line + else + x=x+cwidths[" "] + ret[#ret+1] = word.." " + end + end + return table.concat(ret,"") + end + + + display = function(width) + local res = format_text(text, width,cwidths) + self.label(res) + end + + display(width) + + --self.show_form("_", + --"size[6,6] label[0,0;".. + --res .. "]" ) + +--self.remove() + self.listen(1) +end + +speaker,msg = self.listen_msg() +if speaker == "_" then + display( tonumber(msg) or width) +end \ No newline at end of file