From 046bcc445aab5b17f489f54fc44683ca8ee6f07f Mon Sep 17 00:00:00 2001 From: rnd1 Date: Sat, 21 Oct 2017 15:40:52 +0200 Subject: [PATCH] -new robot scripts -puzzle privs, puzzle mechanics --- commands.lua | 319 ++++++- init.lua | 328 +++++--- scripts/fractal_bot.lua | 92 ++ scripts/minesweeper_game.lua | 33 +- scripts/player_appearance.lua | 21 + scripts/redstone_emulator.lua | 448 ++++++++++ scripts/sokoban.txt | 1398 +++++++++++++++++++++++++++++++ scripts/sokoban_game.lua | 174 ++++ scripts/terrain_smoother.lua | 223 +++++ textures/puzzle_NOT.png | Bin 0 -> 274 bytes textures/puzzle_button_off.png | Bin 0 -> 252 bytes textures/puzzle_button_on.png | Bin 0 -> 256 bytes textures/puzzle_delayer.png | Bin 0 -> 294 bytes textures/puzzle_diode.png | Bin 0 -> 274 bytes textures/puzzle_equalizer.png | Bin 0 -> 248 bytes textures/puzzle_piston.png | Bin 0 -> 281 bytes textures/puzzle_platform.png | Bin 0 -> 276 bytes textures/puzzle_setter.png | Bin 0 -> 262 bytes textures/puzzle_switch_off.png | Bin 0 -> 278 bytes textures/puzzle_switch_off1.png | Bin 0 -> 2192 bytes textures/puzzle_switch_on.png | Bin 0 -> 285 bytes textures/puzzle_switch_on1.png | Bin 0 -> 2193 bytes 22 files changed, 2861 insertions(+), 175 deletions(-) create mode 100644 scripts/fractal_bot.lua create mode 100644 scripts/player_appearance.lua create mode 100644 scripts/redstone_emulator.lua create mode 100644 scripts/sokoban.txt create mode 100644 scripts/sokoban_game.lua create mode 100644 scripts/terrain_smoother.lua create mode 100644 textures/puzzle_NOT.png create mode 100644 textures/puzzle_button_off.png create mode 100644 textures/puzzle_button_on.png create mode 100644 textures/puzzle_delayer.png create mode 100644 textures/puzzle_diode.png create mode 100644 textures/puzzle_equalizer.png create mode 100644 textures/puzzle_piston.png create mode 100644 textures/puzzle_platform.png create mode 100644 textures/puzzle_setter.png create mode 100644 textures/puzzle_switch_off.png create mode 100644 textures/puzzle_switch_off1.png create mode 100644 textures/puzzle_switch_on.png create mode 100644 textures/puzzle_switch_on1.png diff --git a/commands.lua b/commands.lua index 6067821..87293aa 100644 --- a/commands.lua +++ b/commands.lua @@ -21,22 +21,38 @@ local function pos_in_dir(obj, dir) -- position after we move in specified direc local yaw = obj:getyaw(); local pos = obj:getpos(); - if dir == 1 then + if dir == 1 then -- left yaw = yaw + pi/2; - elseif dir == 2 then - yaw = yaw - pi/2; - elseif dir == 3 then - elseif dir == 4 then - yaw = yaw+pi; - elseif dir == 5 then -- up - pos.y=pos.y+1 - elseif dir == 6 then -- down - pos.y=pos.y-1 - elseif dir == 7 then -- forward, down - pos.y=pos.y-1 + elseif dir == 2 then --right + yaw = yaw - pi/2; + elseif dir == 3 then -- forward + elseif dir == 4 then + yaw = yaw+pi; -- backward + elseif dir == 5 then -- up + pos.y=pos.y+1 + elseif dir == 6 then -- down + pos.y=pos.y-1 + + elseif dir == 7 then -- left_down + yaw = yaw + pi/2;pos.y=pos.y-1 + elseif dir == 8 then -- right_down + yaw = yaw - pi/2;pos.y=pos.y-1 + elseif dir == 9 then -- forward_down + pos.y=pos.y-1 + elseif dir == 10 then -- backward_down + yaw = yaw + pi; pos.y=pos.y-1 + + elseif dir == 11 then -- left_up + yaw = yaw + pi/2;pos.y=pos.y+1 + elseif dir == 12 then -- right_up + yaw = yaw - pi/2;pos.y=pos.y+1 + elseif dir == 13 then -- forward_up + pos.y=pos.y+1 + elseif dir == 14 then -- backward_up + yaw = yaw + pi; pos.y=pos.y-1 end - if dir<5 or dir == 7 then -- left, right, back + if dir ~= 5 and dir ~= 6 then pos.x = pos.x+math.cos(yaw) pos.z = pos.z+math.sin(yaw) end @@ -125,7 +141,7 @@ basic_robot.commands.dig = function(name,dir) local data = basic_robot.data[name]; local energy = (data.menergy or 0) - digcost; if energy<0 then - return false, "need " .. digcost .. " energy " + error("need " .. digcost .. " energy to dig " .. nodename .. ". Use machine.generate(...) to get some energy."); end data.menergy = energy; end @@ -281,6 +297,7 @@ basic_robot.commands.pickup = function(r,name) picklist[#picklist+1]=detected_obj; if inv:room_for_item("main", stack) then inv:add_item("main", stack); + obj:setpos({x=0,y=0,z=0}) -- no dupe end obj:remove(); end @@ -542,16 +559,43 @@ end local register_robot_button = function(R,G,B,type) -minetest.register_node("basic_robot:button"..R..G..B, + minetest.register_node("basic_robot:button"..R..G..B, + { + description = "robot button", + tiles = {"robot_button.png^[colorize:#"..R..G..B..":180"}, + inventory_image = "robot_button.png^[colorize:#"..R..G..B..":180", + wield_image = "robot_button.png^[colorize:#"..R..G..B..":180", + + is_ground_content = false, + groups = {cracky=3}, + on_punch = function(pos, node, player) + local name = player:get_player_name(); if name==nil then return end + local round = math.floor; + local r = 32; local ry = 2*r; -- note: this is skyblock adjusted + local ppos = {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry+1,z=round(pos.z/r+0.5)*r}; -- just on top of basic_protect:protector! + local meta = minetest.get_meta(ppos); + local name = meta:get_string("name"); + local data = basic_robot.data[name]; + if data then data.keyboard = {x=pos.x,y=pos.y,z=pos.z, puncher = player:get_player_name(), type = type} end + end + + }) +end + +local register_robot_button_number = function(number,type) +minetest.register_node("basic_robot:button"..number, { description = "robot button", - tiles = {"robot_button.png^[colorize:#"..R..G..B..":180"}, + tiles = {"robot_button".. number .. ".png"}, + inventory_image = "robot_button".. number .. ".png", + wield_image = "robot_button".. number .. ".png", + is_ground_content = false, groups = {cracky=3}, on_punch = function(pos, node, player) local name = player:get_player_name(); if name==nil then return end local round = math.floor; - local r = 20; local ry = 2*r; + local r = 32; local ry = 2*r; local ppos = {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry+1,z=round(pos.z/r+0.5)*r}; local meta = minetest.get_meta(ppos); local name = meta:get_string("name"); @@ -561,17 +605,20 @@ minetest.register_node("basic_robot:button"..R..G..B, }) end -local register_robot_button_number = function(number,type) -minetest.register_node("basic_robot:button"..number, + +local register_robot_button_char = function(number,type) +minetest.register_node("basic_robot:button_"..number, { description = "robot button", - tiles = {"robot_button".. number .. ".png"}, + tiles = {string.format("%03d",number).. ".png"}, + inventory_image = string.format("%03d",number).. ".png", + wield_image = string.format("%03d",number).. ".png", is_ground_content = false, groups = {cracky=3}, on_punch = function(pos, node, player) local name = player:get_player_name(); if name==nil then return end local round = math.floor; - local r = 20; local ry = 2*r; + local r = 32; local ry = 2*r; local ppos = {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry+1,z=round(pos.z/r+0.5)*r}; local meta = minetest.get_meta(ppos); local name = meta:get_string("name"); @@ -581,6 +628,29 @@ minetest.register_node("basic_robot:button"..number, }) end +local register_robot_button_custom = function(number,texture) +minetest.register_node("basic_robot:button_"..number, + { + description = "robot button", + tiles = {texture .. ".png"}, + inventory_image = texture .. ".png", + wield_image = texture .. ".png", + is_ground_content = false, + groups = {cracky=3}, + on_punch = function(pos, node, player) + local name = player:get_player_name(); if name==nil then return end + local round = math.floor; + local r = 32; local ry = 2*r; + local ppos = {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry+1,z=round(pos.z/r+0.5)*r}; + local meta = minetest.get_meta(ppos); + local name = meta:get_string("name"); + local data = basic_robot.data[name]; + if data then data.keyboard = {x=pos.x,y=pos.y,z=pos.z, puncher = player:get_player_name(), type = number} end + end + }) +end + + register_robot_button("FF","FF","FF",1); register_robot_button("80","80","80",2); register_robot_button("FF","80","80",3); @@ -589,6 +659,21 @@ register_robot_button("80","80","FF",5); register_robot_button("FF","FF","80",6); for i = 0,9 do register_robot_button_number(i,i+7) end +for i = 0,255 do register_robot_button_char(i,i+17) end + +register_robot_button_custom(273,"puzzle_switch_off") +register_robot_button_custom(274,"puzzle_switch_on") +register_robot_button_custom(275,"puzzle_button_off") +register_robot_button_custom(276,"puzzle_button_on") + +register_robot_button_custom(277,"puzzle_equalizer") +register_robot_button_custom(278,"puzzle_setter") +register_robot_button_custom(279,"puzzle_piston") + +register_robot_button_custom(280,"puzzle_diode") +register_robot_button_custom(281,"puzzle_NOT") +register_robot_button_custom(282,"puzzle_delayer") +register_robot_button_custom(283,"puzzle_platform") @@ -631,8 +716,10 @@ basic_robot.commands.keyboard = { nodename = "basic_robot:button8080FF"; elseif type == 6 then nodename = "basic_robot:buttonFFFF80"; - elseif type>=7 then + elseif type>=7 and type <= 16 then nodename = "basic_robot:button"..(type-7); + else + nodename = "basic_robot:button_"..(type-17); end minetest.swap_node(pos, {name = nodename}) @@ -644,8 +731,8 @@ basic_robot.commands.keyboard = { basic_robot.commands.craftcache = {}; -basic_robot.commands.craft = function(item, mode, name) - if not item then return end +basic_robot.commands.craft = function(item, mode, idx, name) + if not item then return false end local cache = basic_robot.commands.craftcache[name]; if not cache then basic_robot.commands.craftcache[name] = {}; cache = basic_robot.commands.craftcache[name] end @@ -655,8 +742,14 @@ basic_robot.commands.craft = function(item, mode, name) output = cache.output; else - local craft = minetest.get_craft_recipe(item); - if craft and craft.type == "normal" and craft.items then else return end + local craft; + + if not idx then + craft = minetest.get_craft_recipe(item); + else + craft = minetest.get_all_craft_recipes(item)[idx] + end + if craft and craft.type == "normal" and craft.items then else return false end output = craft.output; local items = craft.items; for _,item in pairs(items) do @@ -1085,4 +1178,176 @@ basic_robot.commands.machine = { say("INPUT: " .. text .. " ENC: " .. enc .. " DEC: " .. dec) end -basic_robot.commands.crypto = {encrypt = encrypt, decrypt = decrypt, scramble = scramble, basic_hash = get_hash} \ No newline at end of file +basic_robot.commands.crypto = {encrypt = encrypt, decrypt = decrypt, scramble = scramble, basic_hash = get_hash} + +-- PUZZLE GAMEPLAY - need puzzle privs + +local is_same_block = function(pos1,pos2) + local round = math.floor; + local r = 32; local ry = 2*r; -- note: this is skyblock adjusted + local ppos1 = {round(pos1.x/r+0.5)*r,round(pos1.y/ry+0.5)*ry,round(pos1.z/r+0.5)*r}; + local ppos2 = {round(pos2.x/r+0.5)*r,round(pos2.y/ry+0.5)*ry,round(pos2.z/r+0.5)*r}; + return ppos1[1]==ppos2[1] and ppos1[2]==ppos2[2] and ppos1[3] == ppos2[3] +end + +local cmd_set_node = function(data,pos,node) + if minetest.is_protected(pos,data.owner) then return end + local spos = data.spawnpos; + if not is_same_block(pos,spos) then return end -- only allow to edit same block as spawner is in + minetest.swap_node(pos,node) +end + +local cmd_get_node_inv = function(data,pos) + local spos = data.spawnpos; + if minetest.is_protected(pos,data.owner) then return end + if not is_same_block(pos,spos) then return end + return minetest.get_meta(pos):get_inventory() +end + +local cmd_get_player = function(data,pname) -- return player for further manipulation + local player = minetest.get_player_by_name(pname) + if not player then return end + local spos = data.spawnpos; + local ppos = player:getpos(); + if not is_same_block(ppos,spos) then return end + return player +end + +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(); + if not is_same_block(ppos,spos) then return end + return player:get_inventory(); +end + +-- spatial triggers with hashing + +local trigger_range = 5 -- how close player must be to "activate" - also size/2 of cells + +local round = math.floor + +local cmd_get_pos_id = function(data,pos, no_neighbors) -- return 4 nearby block ids + local r = trigger_range*2; + local range = 1000*2; -- coordinates from -1000 to +1000 allowed + local n = range/r; + + local x1 = round(pos.x/r+0.5) + local z1 = round(pos.z/r+0.5) + local y1 = round(pos.y/r+0.5) + local baseid = x1 + z1*n + y1*n^2 -- hash value + if no_neighbors then return baseid end + + --check 4 nearby closest squares: 2D + local x0 = round(pos.x/r); + local z0 = round(pos.z/r); + if x00 then robot_activate_furnace(tpos) end + return true + end + + local table = minetest.registered_nodes[node.name]; + if table and table.mesecons and table.mesecons.effector then + else + return false + end -- error + + local effector=table.mesecons.effector; + + if not mode then mode = 1 end + if mode > 0 then + if not effector.action_on then return false end + effector.action_on(tpos,node,16) + elseif mode<0 then + if not effector.action_off then return false end + effector.action_off(tpos,node,16) + end + return true + end, +} \ No newline at end of file diff --git a/init.lua b/init.lua index 7a04acd..3526921 100644 --- a/init.lua +++ b/init.lua @@ -4,8 +4,8 @@ basic_robot = {}; ---- SETTINGS ------ basic_robot.call_limit = 48; -- how many execution calls per script run allowed -basic_robot.entry_count = 2 -basic_robot.advanced_count = 16 +basic_robot.entry_count = 2 -- how many robot ordinary player can have +basic_robot.advanced_count = 16 -- how many robots player with robot privs can have basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes inventories @@ -15,9 +15,9 @@ basic_robot.maxoperations = 2; -- how many operations (dig, generate energy,..) basic_robot.dig_require_energy = true; -- does robot require energy to dig? ---------------------- +basic_robot.http_api = minetest.request_http_api(); - -basic_robot.version = "2017/07/18a"; +basic_robot.version = "2017/10 /07a"; basic_robot.data = {}; -- stores all robot data --[[ @@ -39,19 +39,26 @@ local check_code, preprocess_code,is_inside_string; function getSandboxEnv (name) local commands = basic_robot.commands; + local directions = {left = 1, right = 2, forward = 3, backward = 4, up = 5, down = 6, + left_down = 7, right_down = 8, forward_down = 9, backward_down = 10, + left_up = 11, right_up = 12, forward_up = 13, backward_up = 14 + } + local env = { pcall=pcall, robot_version = function() return basic_robot.version end, - move = { -- changes position of robot - left = function() return commands.move(name,1) end, - right = function() return commands.move(name,2) end, - forward = function() return commands.move(name,3) end, - backward = function() return commands.move(name,4) end, - up = function() return commands.move(name,5) end, - down = function() return commands.move(name,6) end, - }, + 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); + 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)}); + end, turn = { left = function() commands.turn(name,math.pi/2) end, @@ -59,73 +66,12 @@ function getSandboxEnv (name) angle = function(angle) commands.turn(name,angle*math.pi/180) end, }, - dig = { - left = function() return commands.dig(name,1) end, - right = function() return commands.dig(name,2) end, - forward = function() return commands.dig(name,3) end, - backward = function() return commands.dig(name,4) end, - down = function() return commands.dig(name,6) end, - up = function() return commands.dig(name,5) end, - forward_down = function() return commands.dig(name,7) end, - }, - - place = { - left = function(nodename, param2) return commands.place(name,nodename, param2, 1) end, - right = function(nodename, param2) return commands.place(name,nodename, param2, 2) end, - forward = function(nodename, param2) return commands.place(name,nodename, param2, 3) end, - backward = function(nodename, param2) return commands.place(name,nodename, param2, 4) end, - down = function(nodename, param2) return commands.place(name,nodename, param2, 6) end, - up = function(nodename, param2) return commands.place(name,nodename, param2, 5) end, - forward_down = function(nodename, param2) return commands.place(name,nodename, param2, 7) end, - }, - - insert = { -- insert item from robot inventory into another inventory - left = function(item, inventory) return commands.insert_item(name,item, inventory,1) end, - right = function(item, inventory) return commands.insert_item(name,item, inventory,2) end, - forward = function(item, inventory) return commands.insert_item(name,item, inventory,3) end, - backward = function(item, inventory) return commands.insert_item(name,item, inventory,4) end, - down = function(item, inventory) return commands.insert_item(name,item, inventory,6) end, - up = function(item, inventory) return commands.insert_item(name,item, inventory,5) end, - forward_down = function(item, inventory) return commands.insert_item(name,item, inventory,7) end, - }, - - take = { -- takes item from inventory and puts it in robot inventory - left = function(item, inventory) return commands.take_item(name,item, inventory,1) end, - right = function(item, inventory) return commands.take_item(name,item, inventory,2) end, - forward = function(item, inventory) return commands.take_item(name,item, inventory,3) end, - backward = function(item, inventory) return commands.take_item(name,item, inventory,4) end, - down = function(item, inventory) return commands.take_item(name,item, inventory,6) end, - up = function(item, inventory) return commands.take_item(name,item, inventory,5) end, - forward_down = function(item, inventory) return commands.take_item(name,item, inventory,7) end, - - }, - check_inventory = { - left = function(itemname, inventory,i) return commands.check_inventory(name,itemname, inventory,i,1) end, - right = function(itemname, inventory,i) return commands.check_inventory(name,itemname, inventory,i,2) end, - forward = function(itemname, inventory,i) return commands.check_inventory(name,itemname, inventory,i,3) end, - backward = function(itemname, inventory,i) return commands.check_inventory(name,itemname, inventory,i,4) end, - down = function(itemname, inventory,i) return commands.check_inventory(name,itemname, inventory,i,6) end, - up = function(itemname, inventory,i) return commands.check_inventory(name,itemname, inventory,i,5) end, - forward_down = function(itemname, inventory,i) return commands.check_inventory(name,itemname, inventory,i,7) end, - self = function(itemname, inventory,i) return commands.check_inventory(name,itemname, inventory,i,0) end, - }, - - activate = { - left = function(mode) return commands.activate(name,mode, 1) end, - right = function(mode) return commands.activate(name,mode, 2) end, - forward = function(mode) return commands.activate(name,mode, 3) end, - backward = function(mode) return commands.activate(name,mode, 4) end, - down = function(mode) return commands.activate(name,mode, 6) end, - up = function(mode) return commands.activate(name,mode, 5) end, - forward_down = function(mode) return commands.activate(name,mode, 7) end, - }, - pickup = function(r) -- pick up items around robot return commands.pickup(r, name); end, - craft = function(item, mode) - return commands.craft(item, mode, name) + craft = function(item, idx,mode) + return commands.craft(item, mode, idx, name) end, self = { @@ -135,8 +81,7 @@ function getSandboxEnv (name) viewdir = function() local yaw = basic_robot.data[name].obj:getyaw(); return {x=math.cos(yaw), y = 0, z=math.sin(yaw)} end, set_properties = function(properties) - if not properties then return end - local obj = basic_robot.data[name].obj; + if not properties then return end; local obj = basic_robot.data[name].obj; obj:set_properties(properties); end, @@ -212,26 +157,28 @@ function getSandboxEnv (name) end end, - fire = function(speed, pitch,gravity) -- experimental: fires an projectile + fire = function(speed, pitch,gravity, is_entity) -- experimental: fires an projectile local obj = basic_robot.data[name].obj; local pos = obj:getpos(); local yaw = obj:getyaw(); 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)}; -- fire particle - -- minetest.add_particle( - -- { - -- pos = pos, - -- expirationtime = 10, - -- velocity = {x=speed*math.cos(yaw)*math.cos(pitch), y=speed*math.sin(pitch),z=speed*math.sin(yaw)*math.cos(pitch)}, - -- size = 5, - -- texture = "default_apple.png", - -- acceleration = {x=0,y=-gravity,z=0}, - -- collisiondetection = true, - -- collision_removal = true, - -- } - --); - + if not is_entity then + minetest.add_particle( + { + pos = pos, + expirationtime = 10, + velocity = {x=speed*math.cos(yaw)*math.cos(pitch), y=speed*math.sin(pitch),z=speed*math.sin(yaw)*math.cos(pitch)}, + size = 5, + texture = "default_apple.png", + acceleration = {x=0,y=-gravity,z=0}, + collisiondetection = true, + collision_removal = true, + } + ); + return + end local obj = minetest.add_entity(pos, "basic_robot:projectile"); if not obj then return end obj:setvelocity(velocity); @@ -258,7 +205,16 @@ function getSandboxEnv (name) return commands.display_text(obj,text,linesize,size) end, - sound = function(sample,volume) + sound = function(sample,volume, pos) + if pos then + return minetest.sound_play( sample, + { + pos = pos, + gain = volume or 1, + max_hear_distance = 32, -- default, uses an euclidean metric + }) + end + local obj = basic_robot.data[name].obj; return minetest.sound_play( sample, { @@ -289,6 +245,7 @@ function getSandboxEnv (name) scramble = commands.crypto.scramble, basic_hash = commands.crypto.basic_hash, }; + keyboard = { get = function() return commands.keyboard.get(name) end, @@ -306,9 +263,10 @@ function getSandboxEnv (name) end, -- in radius around position find_player = - function(r) - if r>8 then return false end - local objects = minetest.get_objects_inside_radius(basic_robot.data[name].obj:getpos(), r); + function(r,pos) + pos = pos or basic_robot.data[name].obj:getpos(); + if r>10 then return false end + local objects = minetest.get_objects_inside_radius(pos, r); local plist = {}; for _,obj in pairs(objects) do if obj:is_player() then @@ -339,36 +297,6 @@ function getSandboxEnv (name) grab = function(target) return basic_robot.commands.grab(name,target) end, - read_node = { -- returns node name - left = function() return commands.read_node(name,1) end, - right = function() return commands.read_node(name,2) end, - forward = function() return commands.read_node(name,3) end, - backward = function() return commands.read_node(name,4) end, - down = function() return commands.read_node(name,6) end, - up = function() return commands.read_node(name,5) end, - forward_down = function() return commands.read_node(name,7) end, - }, - - read_text = { -- returns text - left = function(stringname,mode) return commands.read_text(name,mode,1,stringname ) end, - right = function(stringname,mode) return commands.read_text(name,mode,2,stringname) end, - forward = function(stringname,mode) return commands.read_text(name,mode,3,stringname) end, - backward = function(stringname,mode) return commands.read_text(name,mode,4,stringname) end, - down = function(stringname,mode) return commands.read_text(name,mode,6,stringname) end, - up = function(stringname,mode) return commands.read_text(name,mode,5,stringname) end, - forward_down = function(stringname,mode) return commands.read_text(name,mode,7,stringname) end, - }, - - write_text = { -- returns text - left = function(text) return commands.write_text(name,1,text) end, - right = function(text) return commands.write_text(name,2,text) end, - forward = function(text) return commands.write_text(name,3,text) end, - backward = function(text) return commands.write_text(name,4,text) end, - down = function(text) return commands.write_text(name,6,text) end, - up = function(text) return commands.write_text(name,5,text) end, - forward_down = function(text) return commands.write_text(name,7,text) end, - - }, say = function(text, owneronly) if not basic_robot.data[name].quiet_mode and not owneronly then @@ -487,6 +415,8 @@ function getSandboxEnv (name) }, colorize = core.colorize, + serialize = minetest.serialize, + deserialize = minetest.deserialize, tonumber = tonumber, pairs = pairs, ipairs = ipairs, error = error, type=type, @@ -501,6 +431,93 @@ function getSandboxEnv (name) basic_robot.data[name].ccounter = _ccounter + 1; end, }; + + -- ROBOT FUNCTIONS: move,dig, place,insert,take,check_inventory,activate,read_node,read_text,write_text + + env.move = {}; -- changes position of robot + for dir, dir_id in pairs(directions) do + env.move[dir] = function() return commands.move(name,dir_id) end + end + + env.dig = {}; + for dir, dir_id in pairs(directions) do + env.dig[dir] = function() return commands.dig(name,dir_id) end + end + + env.place = {}; + for dir, dir_id in pairs(directions) do + env.place[dir] = function(nodename, param2) return commands.place(name,nodename, param2, dir_id) end + end + + env.insert = {}; -- insert item from robot inventory into another inventory + for dir, dir_id in pairs(directions) do + env.insert[dir] = function(item, inventory) return commands.insert_item(name,item, inventory,dir_id) end + end + + env.take = {}; -- takes item from inventory and puts it in robot inventory + for dir, dir_id in pairs(directions) do + env.take[dir] = function(item, inventory) return commands.take_item(name,item, inventory,dir_id) end + end + + env.check_inventory = {}; + for dir, dir_id in pairs(directions) do + env.check_inventory[dir] = function(itemname, inventory,i) return commands.check_inventory(name,itemname, inventory,i,dir_id) end + end + env.check_inventory.self = function(itemname, inventory,i) return commands.check_inventory(name,itemname, inventory,i,0) end; + + env.activate = {}; + for dir, dir_id in pairs(directions) do + env.activate[dir] = function(mode) return commands.activate(name,mode, dir_id) end + end + + env.read_node = {}; + for dir, dir_id in pairs(directions) do + env.read_node[dir] = function() return commands.read_node(name,dir_id) end + end + + env.read_text = {} -- returns text + for dir, dir_id in pairs(directions) do + env.read_text[dir] = function(stringname,mode) return commands.read_text(name,mode,dir_id,stringname) end + end + + env.write_text = {} -- returns text + for dir, dir_id in pairs(directions) do + env.write_text[dir] = function(text) return commands.write_text(name, dir_id,text) end + end + + -- set up sandbox for puzzle + + local ispuzzle = basic_robot.data[name].ispuzzle; -- need puzzle privs + if ispuzzle == 1 then + basic_robot.data[name].puzzle = {}; + local data = basic_robot.data[name]; + local pdata = data.puzzle; + pdata.triggerdata = {}; + pdata.gamedata = {}; + pdata.block_ids = {} + pdata.triggers = {}; + env.puzzle = { -- puzzle functionality + set_node = function(pos,node) commands.puzzle.set_node(data,pos,node) end, + get_node = function(pos) return minetest.get_node(pos) end, + activate = function(mode,pos) commands.puzzle.activate(data,mode,pos) end, + get_meta = function(pos) return commands.puzzle.get_meta(data,pos) end, + get_gametime = function() return minetest.get_gametime() end, + get_node_inv = function(pos) return commands.puzzle.get_node_inv(data,pos) end, + get_player = function(pname) return commands.puzzle.get_player(data,pname) end, + chat_send_player = function(pname, text) minetest.chat_send_player(pname or "", text) end, + get_player_inv = function(pname) return commands.puzzle.get_player_inv(data,pname) end, + 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) + 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, + pdata = pdata, + ItemStack = ItemStack, + } + end + --special sandbox for admin local isadmin=basic_robot.data[name].isadmin @@ -508,6 +525,7 @@ function getSandboxEnv (name) if isadmin~=1 then env._G = env; else + env.minetest = minetest; env._G=_G; debug = debug; end @@ -738,7 +756,7 @@ local robot_spawner_update_form = function (pos, mode) end - if mode ==1 then return form end + if mode == 1 then return form end meta:set_string("formspec",form) end @@ -789,6 +807,8 @@ local function init_robot(obj) -- check if admin robot if self.isadmin then basic_robot.data[name].isadmin = 1 end + -- can we do puzzles? + if self.ispuzzle then basic_robot.data[name].ispuzzle = 1 end --robot appearance,armor... obj:set_properties({infotext = "robot " .. name}); @@ -1040,7 +1060,7 @@ local spawn_robot = function(pos,node,ttl) luaent.code = meta:get_string("code"); luaent.spawnpos = {x=pos.x,y=pos.y-1,z=pos.z}; if meta:get_int("admin") == 1 then luaent.isadmin = 1 end - + if meta:get_int("puzzle") == 1 then luaent.ispuzzle = 1 end local data = basic_robot.data[name]; if data == nil then @@ -1279,7 +1299,7 @@ local on_receive_robot_form = function(pos, formname, fields, sender) return end - if fields.help then + if fields.help then ----- INGAME HELP ------ local text = "BASIC LUA SYNTAX\n \nif x==1 then A else B end".. "\n for i = 1, 5 do something end \nwhile i<6 do A; i=i+1; end\n".. @@ -1287,7 +1307,8 @@ local on_receive_robot_form = function(pos, formname, fields, sender) " access table entries with myTable1[1] or myTable2.entry1 or myTable2[\"entry1\"]\n \n".. "ROBOT COMMANDS\n \n".. "**MOVEMENT,DIGGING, PLACING, INVENTORY TAKE/INSERT\n move.direction(), where direction is forward, backward, left,right, up, down)\n".. - " forward_down direction only works with dig, place and read_node\n".. + " left_down, ..., backward_down, left_up, ..., backward_up\n".. + " boost(v) sets robot velocity, -60 it returns itemname. if itemname == \"\" it checks if inventory empty\n".. " activate.direction(mode) activates target block\n".. " pickup(r) picks up all items around robot in radius r<8 and returns list or nil\n".. - " craft(item,mode) crafts item if required materials are present in inventory. mode = 1 returns recipe\n".. + " craft(item,idx,mode) crafts item if required materials are present in inventory. mode = 1 returns recipe, optional recipe idx\n".. " take.direction(item, inventory) takes item from target inventory into robot inventory\n".. " read_text.direction(stringname,mode) reads text of signs, chests and other blocks, optional stringname for other meta,\n mode 1 read number\n".. " write_text.direction(text,mode) writes text to target block as infotext\n".. @@ -1306,7 +1327,7 @@ local on_receive_robot_form = function(pos, formname, fields, sender) " code.set(text) replaces current bytecode of robot\n".. " find_nodes(\"default:dirt\",3) returns distance to node in radius 3 around robot, or false if none\n".. "**PLAYERS\n".. - " find_player(3) finds players in radius 3 around robot and returns list, if none returns nil\n".. + " find_player(3,pos) finds players in radius 3 around robot(position) and returns list, if none returns nil\n".. " attack(target) attempts to attack target player if nearby \n".. " grab(target) attempt to grab target player if nearby and returns true if succesful \n".. " player.getpos(name) return position of player, player.connected() returns list of players\n".. @@ -1329,11 +1350,11 @@ local on_receive_robot_form = function(pos, formname, fields, sender) " self.fire_pos() returns last hit position\n".. " self.label(text) changes robot label\n".. " self.display_text(text,linesize,size) displays text instead of robot face, if no size return tex\n".. - " self.sound(sample,volume) plays sound named 'sample' at robot location\n".. + " self.sound(sample,volume, opt. pos) plays sound named 'sample' at robot location (opt. pos)\n".. " rom is aditional table that can store persistent data, like rom.x=1\n".. "**KEYBOARD : place spawner at coordinates (20i,40j+1,20k) to monitor events\n".. " keyboard.get() returns table {x=..,y=..,z=..,puncher = .. , type = .. } for keyboard event\n".. - " keyboard.set(pos,type) set key at pos of type 0=air, 1..6, limited to range 10 around\n".. + " keyboard.set(pos,type) set key at pos of type 0=air,1-6,7-15,16-271, limited to range 10 around\n".. " keyboard.read(pos) return node name at pos\n".. "**TECHNIC FUNCTIONALITY: namespace 'machine'. most functions return true or nil, error\n" .. " energy() displays available energy\n".. @@ -1350,7 +1371,21 @@ local on_receive_robot_form = function(pos, formname, fields, sender) " encrypt(input,password) returns encrypted text, password is any string \n".. " decrypt(input,password) attempts to decrypt encrypted text\n".. " scramble(input,randomseed,sgn) (de)permutes text randomly according to sgn = -1,1\n".. - " basic_hash(input,n) returns simple mod hash from string input within range 0...n-1\n"; + " basic_hash(input,n) returns simple mod hash from string input within range 0...n-1\n".. + "**PUZZLE: namespace 'puzzle' - need puzzle priv\n".. + " set_triggers({trigger1, trigger2,...}) sets and initializes spatial triggers\n".. + " check_triggers(pname) check if player is close to any trigger and run that trigger\n".. + " set_node(pos,node) - set any node, limited to current protector mapblock & get_node(pos)\n".. + " get_player(pname) return player objRef in current mapblock\n".. + " chat_send_player(pname, text) \n".. + " get_node_inv(pos) / get_player_inv(pname) - return inventories of nodes/players in current mapblock\n".. + " get_meta(pos) - return meta of target position\n".. + " get_gametime() - return current gametime\n".. + " ItemStack(itemname) returns ItemRef to be used with inventory\n".. + " count_objects(pos,radius)\n".. + " pdata contains puzzle data like .triggers and .gamedata\n".. + " add_particle(def)\n" + text = minetest.formspec_escape(text); @@ -1601,6 +1636,7 @@ minetest.register_on_player_receive_fields( lines[selection]= fields.input end local meta = minetest.get_meta(pos); + if not lines then return end local code = table.concat(lines,"\n"); meta:set_string("code",code); basic_robot.editor[name].lines = {}; @@ -1685,7 +1721,9 @@ minetest.register_node("basic_robot:spawner", { after_place_node = function(pos, placer) local meta = minetest.env:get_meta(pos) meta:set_string("owner", placer:get_player_name()); - local privs = minetest.get_player_privs(placer:get_player_name()); if privs.privs then meta:set_int("admin",1) end + local privs = minetest.get_player_privs(placer:get_player_name()); + if privs.privs then meta:set_int("admin",1) end + if privs.puzzle then meta:set_int("puzzle",1) end meta:set_string("code",""); meta:set_int("id",1); -- initial robot id @@ -1787,6 +1825,22 @@ minetest.register_craftitem("basic_robot:control", { on_use = function(itemstack, user, pointed_thing) local owner = user:get_player_name(); + + local script = itemstack:get_metadata(); + if script == "@" then -- remote control as a tool - notify robot in current block of pointed position + local round = math.floor; + local r = 32; local ry = 2*r; -- note: this is skyblock adjusted + local pos = pointed_thing.under + if not pos then return end + local ppos = {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry+1,z=round(pos.z/r+0.5)*r}; -- just on top of basic_protect:protector! + local 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 = owner, type = 0} end + return + end + + local ids = basic_robot.ids[owner]; if not ids then setupid(owner) end local id = basic_robot.ids[owner].id or 1; -- read active id local name = owner .. id @@ -1812,7 +1866,6 @@ minetest.register_craftitem("basic_robot:control", { end end - local script = itemstack:get_metadata(); if script == "" then --display control form minetest.show_formspec(owner, "robot_manual_control_" .. name, get_manual_control_form(name)); @@ -1912,4 +1965,5 @@ minetest.register_craft({ }) -minetest.register_privilege("robot", "increased number of allowed active robots") \ No newline at end of file +minetest.register_privilege("robot", "increased number of allowed active robots") +minetest.register_privilege("puzzle", "allow player to use puzzle. namespace in robots") \ No newline at end of file diff --git a/scripts/fractal_bot.lua b/scripts/fractal_bot.lua new file mode 100644 index 0000000..7279c44 --- /dev/null +++ b/scripts/fractal_bot.lua @@ -0,0 +1,92 @@ +-- robot can construct classic fractals like menger sponge, jerusalem cube, sierpinski triangles,.. +-- use: build a pattern at position 1,1,1 relative to robot. when run robot will analyse pattern and construct fractal +if not init then + minetest.forceload_block(self.pos(),true) + init = true; local spos = self.spawnpos(); + + offsets = {["default:dirt"] = 0, ["default:wood"] = -1, ["default:cobble"] = 1} + + read_form = function(fractal) -- read shape from world + local form = {}; local i = 0; + local spos = self.spawnpos(); spos.x = spos.x+1;spos.y = spos.y+1;spos.z = spos.z+1; + local nx = 0; local ny = 0; local nz = 0; + fractal.form = {} + + for x = 0,fractal.nx-1 do + for y = 0,fractal.ny-1 do + for z = 0,fractal.nz-1 do + local node = _G.minetest.get_node({x=spos.x+x,y=spos.y+y,z=spos.z+z}).name; + local offset = offsets[node] or 0; + if node~= "air" then + form[i] = {x,y,z,offset}; i=i+1 + if nxm+1 or j<0 or j>n+1 then return 0 end; count = 0 for k = -1,1 do for l = -1,1 do @@ -45,27 +51,32 @@ if event then 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:diamond 5")) -- diamond reward + _G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward else - say("FAIL! " .. count .. " mines remaining ") + 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 - elseif event.type == 2 then + else --if event.type == 2 then local ppos = player.getpos(event.puncher) if ppos and math.abs(ppos.x-event.x)<0.5 and math.abs(ppos.z-event.z)<0.5 then -- just mark mine if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:button808080" then - keyboard.set({x=event.x,y=event.y,z=event.z},2) + puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:button808080"}) else - keyboard.set({x=event.x,y=event.y,z=event.z},3) + 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 ");keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3);self.remove() + 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 keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4) - else keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},7+count) end + 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 diff --git a/scripts/player_appearance.lua b/scripts/player_appearance.lua new file mode 100644 index 0000000..05d7e09 --- /dev/null +++ b/scripts/player_appearance.lua @@ -0,0 +1,21 @@ +if not init then + self.set_properties({ + visual = "mesh", mesh = "character.b3d", + textures = {"character.png"}, + visual_size = {x = 2, y = 2} + }); + move.up() + init = 1; + + animation = { + -- Standard animations. + stand = { x= 0, y= 79, }, + lay = { x=162, y=166, }, + walk = { x=168, y=187, }, + mine = { x=189, y=198, }, + walk_mine = { x=200, y=219, }, + sit = { x= 81, y=160, }, + } + self.set_animation(animation.stand.x,animation.stand.y, 15, 0) +t=0 +end \ No newline at end of file diff --git a/scripts/redstone_emulator.lua b/scripts/redstone_emulator.lua new file mode 100644 index 0000000..eea67e2 --- /dev/null +++ b/scripts/redstone_emulator.lua @@ -0,0 +1,448 @@ +-- REDSTONE EMULATOR & EDITOR +--v 10/14a + +if not init then + local players = find_player(5); + if not players then + name = ""; + else + name = players[1] + player_ = puzzle.get_player(name) + local inv = player_:get_inventory(); + inv:set_stack("main", 8, puzzle.ItemStack("basic_robot:control 1 0 \"@\"")) -- add controller in players inventory + --add items for building + inv:set_stack("main", 1, puzzle.ItemStack("default:pick_diamond")) + inv:set_stack("main", 2, puzzle.ItemStack("basic_robot:button_273 999")) -- switch 9 = 273/274 + inv:set_stack("main", 3, puzzle.ItemStack("basic_robot:button_275 999")) -- button 7 = 275/276 + inv:set_stack("main", 4, puzzle.ItemStack("basic_robot:button_277 999")) -- equalizer 61 = 277 + inv:set_stack("main", 5, puzzle.ItemStack("basic_robot:button_278 999")) -- setter 15 = 278 + inv:set_stack("main", 6, puzzle.ItemStack("basic_robot:button_279 999")) -- piston 171 = 279 + inv:set_stack("main", 7, puzzle.ItemStack("basic_robot:button_282 999")) -- delayer 232 = 282 + inv:set_stack("main", 9, puzzle.ItemStack("basic_robot:button_281 999")) -- NOT 33 = 281 + inv:set_stack("main", 10, puzzle.ItemStack("basic_robot:button_280 999")) -- diode 175 = 280 + inv:set_stack("main", 11, puzzle.ItemStack("basic_robot:button_283 999")) -- platform 22 = 283 + + local round = math.floor; protector_position = function(pos) local r = 32;local ry = 2*r; return {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry,z=round(pos.z/r+0.5)*r}; end + local spawnblock = protector_position(self.spawnpos()) + + local meta = puzzle.get_meta(spawnblock); + meta:set_string("shares", name) -- add player to protection! + puzzle.chat_send_player(name,colorize("yellow","#EDITOR: if you need any blocks get them by using 'give me' in craft guide. you can now use controller to make links from pointed at blocks. In addition hold SHIFT to display infos. Reset block links by selecting block 2x")) + end + + init = true + self.spam(1) + self.label(colorize("orange","REDSTONE EMULATOR/EDITOR")) + + + + + -- 1. EMULATOR CODE + + + TTL = 16 -- signal propagates so many steps before dissipate + --self.label(colorize("red","REDSTONE")..colorize("yellow","EMULATOR")) + + + -- DEFINITIONS OF BLOCKS THAT CAN BE ACTIVATED + toggle_button_action = function(mode,pos,ttl) -- SIMPLE TOGGLE BUTTONS - SWITCH + if not ttl or ttl <=0 then return end + if mode == 1 then -- turn on + puzzle.set_node(pos,{name = "basic_robot:button_274"}) + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + else -- turn off + puzzle.set_node(pos,{name = "basic_robot:button_273"}) + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + for i = 1,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + end + + + button_action = function(mode,pos,ttl) -- SIMPLE ON BUTTON, TOGGLES BACK OFF after 1s + if not ttl or ttl <=0 then return end + if mode == 0 then return end + puzzle.set_node(pos,{name = "basic_robot:button_276"}) + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + + minetest.after(1, function() + puzzle.set_node(pos,{name = "basic_robot:button_275"}) + for i = 1,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end) + for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + + equalizer_action = function(mode,pos,ttl) -- CHECK NODES AT TARGET1,TARGET2. IF EQUAL ACTIVATE TARGET3,TARGET4,... + if not ttl or ttl <=0 then return end + if mode == 0 then return end + + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + local node1 = puzzle.get_node({x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}).name + local node2 = puzzle.get_node({x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z}).name + + + if node1==node2 then + for i = 3,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + else + for i = 3,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + end + + delayer_action = function(mode,pos,ttl) -- DELAY FORWARD SIGNAL, delay determined by distance of target1 from delayer ( in seconds) + if not ttl or ttl <=0 then return end + + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + local pos1 = {x=meta:get_int("x1"),y=meta:get_int("y1"),z=meta:get_int("z1")} + local delay = math.sqrt(pos1.x^2+pos1.y^2+pos1.z^2); + + if delay > 0 then + minetest.after(delay, function() + if mode == 1 then + for i = 2,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + else + for i = 2,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + end) + end + end + + diode_action = function(mode,pos,ttl) -- ONLY pass through ON signal + if not ttl or ttl <=0 then return end + if mode ~= 1 then return end + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + + not_action = function(mode,pos,ttl) -- negate signal: 0 <-> 1 + if not ttl or ttl <=0 then return end + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + for i = 1,n do activate(1-mode,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + + setter_action = function(mode,pos,ttl) -- SETS NODES IN TARGET AREA TO PRESELECTED NODE + if not ttl or ttl <=0 then return end + if mode == 0 then return end + + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + if n ~= 3 then say("#setter: error, needs to be set with 3 links"); return end + local node1 = puzzle.get_node({x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}) + local pos1 = {x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z} + local pos2 = {x=meta:get_int("x3")+pos.x,y=meta:get_int("y3")+pos.y,z=meta:get_int("z3")+pos.z} + + if pos1.x>pos2.x then pos1.x,pos2.x = pos2.x,pos1.x end + if pos1.y>pos2.y then pos1.y,pos2.y = pos2.y,pos1.y end + if pos1.z>pos2.z then pos1.z,pos2.z = pos2.z,pos1.z end + + local size = (pos2.x-pos1.x+1)*(pos2.y-pos1.y+1)*(pos2.z-pos1.z+1) + if size > 27 then say("#setter: target area too large, more than 27 blocks!"); return end + for x = pos1.x,pos2.x do + for y = pos1.y,pos2.y do + for z = pos1.z,pos2.z do + puzzle.set_node({x=x,y=y,z=z},node1) + end + end + end + end + + local piston_displaceable_nodes = {["air"] = 1,["default:water_flowing"] = 1} + + piston_action = function(mode,pos,ttl) -- PUSH NODE AT TARGET1 AWAY FROM PISTON + if not ttl or ttl <=0 then return end + --if mode == 0 then return end + + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + if n < 1 or n>2 then say("#piston: error, needs to be set with at least link and most two"); return end + local pos1 = {x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z} + + -- determine direction + local dir = {x=pos1.x-pos.x, y= pos1.y-pos.y, z= pos1.z-pos.z}; + + local dirabs = {x=math.abs(dir.x), y= math.abs(dir.y), z= math.abs(dir.z)}; + local dirmax = math.max(dirabs.x,dirabs.y,dirabs.z); + + if dirabs.x == dirmax then dir = { x = dir.x>0 and 1 or -1, y = 0,z = 0 } + elseif dirabs.y == dirmax then dir = { x = 0, y = dir.y>0 and 1 or -1, z=0} + else dir = {x = 0, y = 0, z = dir.z>0 and 1 or -1} + end + + local pos2 = {x=pos1.x+dir.x,y=pos1.y+dir.y,z=pos1.z+dir.z}; + + if mode == 0 then pos1,pos2 = pos2,pos1 end + + local node1 = puzzle.get_node(pos1) + if node1.name == "air" then return end + + + if piston_displaceable_nodes[puzzle.get_node(pos2).name] then + puzzle.set_node(pos2, node1) + puzzle.set_node(pos1, {name = "air"}) + minetest.check_for_falling(pos2) + self.sound("doors_door_open",1,pos) + end + end + + platform_action = function(mode,pos,ttl) -- SPAWN MOVING PLATFORM + + if mode~=1 then return end + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + if n ~= 2 then say("#platform: error, needs to be set with 2 targets"); return end + local pos1 = {x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z} + local pos2 = {x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z} + + -- determine direction + local dir = {x=pos2.x-pos1.x, y= pos2.y-pos1.y, z= pos2.z-pos1.z}; + + local obj = minetest.add_entity(pos1, "basic_robot:projectile"); + + if not obj then return end + obj:setvelocity(dir); + --obj:setacceleration({x=0,y=-gravity,z=0}); + local luaent = obj:get_luaentity(); + luaent.name = name; + luaent.spawnpos = pos1; + + local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name; + local tiles = minetest.registered_nodes[nodename].tiles; tiles = tiles or {}; + local texture = tiles[1] or "default_stone"; + obj:set_properties({visual = "cube",textures = {texture,texture,texture,texture,texture,texture},visual_size = {x=1,y=1}, + collisionbox={-0.5,-0.5,-0.5,0.5,0.5,0.5}}) + end + + + -- HOW TO ACTIVATE TARGET ELEMENT - adds mesecons/basic machines compatibility + activate = function(mode, pos, ttl) + if not ttl or ttl <=0 then return end + local nodename = puzzle.get_node(pos).name; + local active_element = active_elements[nodename]; + if active_element then + active_element(mode,pos,ttl-1) + else -- try mesecons activate + local nodename = puzzle.get_node(pos).name + local table = minetest.registered_nodes[nodename]; + if table and table.mesecons then else return end + + local effector=table.mesecons.effector; + + if mode == 1 then + if effector.action_on then + effector.action_on(pos,node,ttl) + end + else + if effector.action_off then + effector.action_off(pos,node,ttl) + end + end + end + end + + -- THESE REACT WHEN PUNCHED + interactive_elements = { + [275] = {button_action,1}, -- BUTTON, 1 means it activates(ON) on punch + [273] = {toggle_button_action,1}, -- TOGGLE BUTTON_OFF + [274] = {toggle_button_action,0} -- TOGGLE BUTTON_ON, 0 means it deactivates + --TODO + -- inventory checker(taker) : basic_robot:button_63 -> if player has stuff it will activate items after punch + -- inventory give: give item that is at position1 and activate selected machine at position2 + } + + -- THESE CAN BE ACTIVATED WITH SIGNAL + active_elements = { + ["basic_robot:button_275"] = button_action, -- BUTTON, what action to do on activate + ["basic_robot:button_273"] = toggle_button_action, -- TOGGLE BUTTON_OFF + ["basic_robot:button_274"] = toggle_button_action, -- TOGGLE BUTTON_ON + ["basic_robot:button_278"] = setter_action, -- SETTER + ["basic_robot:button_277"] = equalizer_action, -- EQUALIZER + ["basic_robot:button_279"] = piston_action, -- PISTON + ["basic_robot:button_283"] = platform_action, -- PLATFORM + ["basic_robot:button_282"] = delayer_action, -- DELAYER + ["basic_robot:button_280"] = diode_action, -- DIODE + ["basic_robot:button_281"] = not_action, -- NOT + } + + + -- EDITOR CODE -- + + edit = {}; + edit.state = 1; -- tool state + edit.source = {}; edit.sourcenode = ""; -- selected source + + -- blocks that can be activated + edit.active_elements = { + ["basic_robot:button_275"] = "button: now select one or more targets", -- button + ["basic_robot:button_273"] = "switch: now select one or more targets", -- switch OFF + ["basic_robot:button_274"] = "switch: now select one or more targets", -- switch ON + ["basic_robot:button_278"] = "setter: set block at target region {target2,target3} to block at target1", -- equalizer + ["basic_robot:button_277"] = "equalizer: target1 and target2 are for comparison, other targets are activated", -- equalizer + ["basic_robot:button_279"] = "piston: push block at target1 in direction away from piston", -- equalizer + ["basic_robot:button_283"] = "platform: select target1 to set origin, target2 for direction", -- PLATFORM + ["basic_robot:button_282"] = "delayer: distance from delayer to target1 determines delay", -- delayer + ["basic_robot:button_280"] = "diode: only pass through ON signal", -- DIODE + ["basic_robot:button_281"] = "NOT gate: negates the signal", -- NOT + } + + linker_use = function(pos) + if not pos then return end + + --say(serialize(player_:get_player_control())) + if edit.state < 0 then -- link edit mode! + local meta = puzzle.get_meta(edit.source); + local i = -edit.state; + meta:set_int("x" ..i, pos.x-edit.source.x); meta:set_int("y" ..i, pos.y-edit.source.y); meta:set_int("z" ..i, pos.z-edit.source.z) + puzzle.chat_send_player(name, colorize("red", "EDIT ".. " target " .. i .. " changed")) + edit.state = 1 + goto display_particle + end + + if player_:get_player_control().sneak then -- SHOW LINKS + local meta = puzzle.get_meta(pos); + local n = meta:get_int("n"); + local nodename = puzzle.get_node(pos).name; + local active_element = edit.active_elements[nodename] + if active_element and edit.source.x == pos.x and edit.source.y == pos.y and edit.source.z == pos.z then -- gui with more info + local form = "size[4,"..(0.75*n).."] label[0,-0.25; "..active_element .."]" ; + for i = 1,n do -- add targets as lines + form = form .. + "button[0,".. (0.75*i-0.5) .. ";1,1;".."S"..i..";" .. "show " .. i .. "]".. + "button_exit[1,".. (0.75*i-0.5) .. ";1,1;".."E"..i..";" .. "edit " .. "]" .. + "button_exit[2,".. (0.75*i-0.5) .. ";1,1;".."D"..i..";" .. "delete " .. "]".. + "label[3,".. (0.75*i-0.25) .. "; " .. meta:get_int("x"..i) .. " " .. meta:get_int("y"..i) .. " " .. meta:get_int("z"..i) .."]" + end + self.show_form(name,form); + edit.state = 3; + return + end + edit.source = {x=pos.x,y=pos.y, z=pos.z}; + edit.state = 1 + if not active_element then return end + local i = string.find(active_element,":"); + if not i then return end + puzzle.chat_send_player(name,colorize("red","#INFO ".. string.sub(active_element,1,i-1) ..":") .." has " .. n .. " targets. Select again for more info.") + meta:set_string("infotext",string.sub(active_element,1,i-1)) -- write name of element on it! + + for i = 1, n do + minetest.add_particle( + { + pos = {x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z}, + expirationtime = 5, + velocity = {x=0, y=0,z=0}, + size = 18, + texture = "010.png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + } + ) + end + return + end + + if edit.state == 1 then -- SET SOURCE + local nodename = puzzle.get_node(pos).name; + local active_element = edit.active_elements[nodename] + if not active_element then puzzle.chat_send_player(name,colorize("red","#ERROR linker:").. " source must be valid element like switch"); return end + edit.source = {x=pos.x,y=pos.y, z=pos.z}; + sourcenode = nodename; + puzzle.chat_send_player(name, colorize("yellow","SETUP " ..edit.state .. ": ").. active_element) + edit.state = 2 + else -- SET TARGET + local meta = puzzle.get_meta(edit.source); + local n = meta:get_int("n"); + + if edit.state == 2 and pos.x == edit.source.x and pos.y == edit.source.y and pos.z == edit.source.z then -- RESET LINK FOR SOURCE + local meta = puzzle.get_meta(pos);meta:set_int("n",0) -- reset links + puzzle.chat_send_player(name, colorize("red", "SETUP " .. edit.state .. ":") .. " resetted links for selected source.") + edit.state = 1;return + else + n=n+1; + meta:set_int("x"..n, pos.x-edit.source.x);meta:set_int("y"..n, pos.y-edit.source.y);meta:set_int("z"..n, pos.z-edit.source.z) -- relative to source! + meta:set_int("n",n) + puzzle.chat_send_player(name, colorize("red", "SETUP "..edit.state .. ":") .. " added target #" .. n) + edit.state = 1 + end + end + + -- display + ::display_particle:: + + minetest.add_particle( + { + pos = pos, + expirationtime = 5, + velocity = {x=0, y=0,z=0}, + size = 18, + texture = "009.png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + } + ) + end + + tools = { + ["basic_robot:control"] = linker_use + } + + ------ END OF EDIT PROGRAM + +end + + +event = keyboard.get() -- handle keyboard +if event then + if event.type == 0 then -- EDITING + if event.puncher == name then -- players in protection can edit -- not minetest.is_protected({x=event.x,y=event.y,z=event.z},event.puncher) + local wield_item = player_:get_wielded_item():get_name() + local tool = tools[wield_item] + if tool then tool({x=event.x,y=event.y,z=event.z}) end + end + else -- EMULATOR + local typ = event.type; + local interactive_element = interactive_elements[typ] + if interactive_element then + interactive_element[1](interactive_element[2],{x=event.x,y=event.y,z=event.z},TTL) + self.sound("doors_glass_door_open",1,{x=event.x,y=event.y,z=event.z}) + end + end +end + + +sender,fields = self.read_form() -- handle gui for editing +if sender then + edit.state = 1 + for k,_ in pairs(fields) do + local c = string.sub(k,1,1); + local i = tonumber(string.sub(k,2)) or 1; + if c == "S" then + + local meta = puzzle.get_meta(edit.source); + minetest.add_particle( + { + pos = {x=meta:get_int("x"..i)+edit.source.x,y=meta:get_int("y"..i)+edit.source.y,z=meta:get_int("z"..i)+edit.source.z}, + expirationtime = 5, + velocity = {x=0, y=0,z=0}, + size = 18, + texture = "010.png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + } + ) + elseif c == "E" then + edit.state = -i; + puzzle.chat_send_player(name, colorize("yellow", "#EDIT: select target " .. i)); + elseif c == "D" then + local meta = puzzle.get_meta(edit.source); + local n = meta:get_int("n") + if n > 0 then + for j = i,n-1 do + meta:set_int("x"..j, meta:get_int("x"..(j+1))) + meta:set_int("y"..j, meta:get_int("y"..(j+1))) + meta:set_int("z"..j, meta:get_int("z"..(j+1))) + end + meta:set_int("n",n-1) + end + puzzle.chat_send_player(name, colorize("red", "#EDIT: target " .. i .. " deleted")); + end + --say(serialize(fields)) + end +end \ No newline at end of file diff --git a/scripts/sokoban.txt b/scripts/sokoban.txt new file mode 100644 index 0000000..f6c2351 --- /dev/null +++ b/scripts/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/sokoban_game.lua b/scripts/sokoban_game.lua new file mode 100644 index 0000000..99d0b1c --- /dev/null +++ b/scripts/sokoban_game.lua @@ -0,0 +1,174 @@ + -- SOKOBAN GAME, by rnd, robots port + if not sokoban then + sokoban = {}; + local players = find_player(4); + 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 + + 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 = "default:brick" + SOKOBAN_FLOOR = "default:stonebrick" + SOKOBAN_GOAL = "default:diamondblock" + SOKOBAN_BOX = "basic_robot:button8080FF" + + 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}) + say("games: sokoban level "..sokoban.level .." loaded by ".. name .. ". It has " .. sokoban.blocks .. " boxes to push. "); 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+1,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 + + + end + + +if state == 1 then + sender,fields = self.read_form(); -- get fields from form submittal + if sender then + --say(serialize(fields)) + + if fields.LVL then + load_level((tonumber(fields.LVL) or 1)-1) + state = 0 + self.label("stand close to blue box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all boxes pushed on diamond blocks") + end + end +else + + local ppos = player_:getpos() + if math.abs(ppos.y-sokoban.pos.y)~= 0.5 then say(colorize("red", "SOKOBAN: " .. name .. " CHEATS ! ")); 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 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/terrain_smoother.lua b/scripts/terrain_smoother.lua new file mode 100644 index 0000000..6ab519a --- /dev/null +++ b/scripts/terrain_smoother.lua @@ -0,0 +1,223 @@ +--smoothie robot: smooths selected terrain with slopes: define region with chat c1,c2, smooth with s + +if not paste then + _G.minetest.forceload_block(self.pos(),true) + paste = {}; + round = function(x) + if x>0 then + return math.floor(x+0.5) + else + return -math.floor(-x+0.5) + end + end + + is_solid = function(i,j,k, is_return) + local dat = data[i] + if not is_return then + if not dat then return false end + dat = dat[j]; + if not dat then return false end + dat = dat[k]; + if not dat then return false end + return true + else + if not dat then return 0 end + dat = dat[j]; + if not dat then return 0 end + dat = dat[k]; + if not dat then return 0 end + return dat + end + end + + solidnodes = { + ["default:stone"] = 1, + ["default:dirt"] = 2, + ["default:stone_with_coal"] = 0, + ["default:dirt_with_dry_grass"] = 2, + ["default:silver_sand"] = 0, + ["default:sand"] = 0, + } + + solidtypes = + { + [1] = "stone", + [2] = "dirt" + }; + + data = {}; + self.listen(1) + self.label("mr. smoothie") +end + + +speaker, msg = self.listen_msg() + +if msg and (true or speaker == "rnd") then + 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 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) + 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 msg == "c" then -- LOAD geometry in memory + 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); + x1=x1-1;y1=y1-1;z1=z1-1;x2=x2+1;y2=y2+1;z2=z2+1; + local count = 0; data = {}; + for i = x1,x2 do + for j = y1,y2 do + for k = z1,z2 do + local node = _G.minetest.get_node({x=i,y=j,z=k}); + local typ = solidnodes[node.name]; + if not typ then if string.sub(node.name,1,4) == "more" then typ = 1 end end + if typ then + if not data[i] then data[i]= {} end + if not data[i][j] then data[i][j]= {} end + data[i][j][k] = -typ + count = count +1; + end + end + end + end + say(count .. " nodes copied "); + elseif msg == "s" then -- SMOOTHING PROCESS + 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; local newnode = {}; + for i = x1,x2 do + for j = y1,y2 do + for k = z1,z2 do + local x = i;local y = j; local z = k; + --say(x .. " " .. y .. " " .. z) + if is_solid(x,y,z) and not is_solid(x,y+1,z) then -- floor node + local xs1,xs2,zs1,zs2 + if is_solid(x-1,y,z) then if is_solid(x-1,y+1,z) then xs1 = 1 else xs1 = 0 end else xs1 = -1 end + if is_solid(x+1,y,z) then if is_solid(x+1,y+1,z) then xs2 = 1 else xs2 = 0 end else xs2 = -1 end + + if is_solid(x,y,z-1) then if is_solid(x,y+1,z-1) then zs1 = 1 else zs1 = 0 end else zs1 = -1 end + if is_solid(x,y,z+1) then if is_solid(x,y+1,z+1) then zs2 = 1 else zs2 = 0 end else zs2 = -1 end + + local dx = xs2 - xs1; local dz = zs2 - zs1; ch = 0; + if dx > 0 and dz == 0 then + if xs1<0 then + newnode[1] = "moreblocks:slope_stone"; newnode[2] = 1; ch = 1 + data[x][y][z] = 2; + end + elseif dx<0 and dz == 0 then + if xs2<0 then + newnode[1] = "moreblocks:slope_stone"; newnode[2] = 3; ch = 1 + data[x][y][z] = 2; + end + elseif dx == 0 and dz < 0 then + if zs2<0 then + newnode[1] = "moreblocks:slope_stone"; newnode[2] = 2; ch = 1 + data[x][y][z] = 2; + end + elseif dx == 0 and dz > 0 then + if zs1<0 then + newnode[1] = "moreblocks:slope_stone"; newnode[2] = 0; ch = 1 + data[x][y][z] = 2; + end + + elseif dx<0 and dz>0 then + newnode[2] = 0; ch = 1 + if xs2 == 0 and zs1 == 0 then + newnode[1] = "moreblocks:slope_stone_inner"; + data[x][y][z] = 5; + else + newnode[1] = "moreblocks:slope_stone_outer_cut" + data[x][y][z] = 3; + end + elseif dx>0 and dz>0 then + newnode[2] = 1; ch = 1 + if xs1 == 0 and zs1 == 0 then + newnode[1] = "moreblocks:slope_stone_inner" + data[x][y][z] = 5; + else + newnode[1] = "moreblocks:slope_stone_outer_cut" + data[x][y][z] = 3; + end + elseif dx>0 and dz<0 then + newnode[2] = 2; ch = 1 + if xs1==0 and zs2 == 0 then + newnode[1] = "moreblocks:slope_stone_inner" + data[x][y][z] = 5; + else + newnode[1] = "moreblocks:slope_stone_outer_cut" + data[x][y][z] = 3; + end + elseif dx<0 and dz<0 then + newnode[2] = 3; ch = 1 + if xs2 == 0 and zs2 == 0 then + newnode[1] = "moreblocks:slope_stone_inner" + data[x][y][z] = 5; + else + newnode[1] = "moreblocks:slope_stone_outer_cut" + data[x][y][z] = 3; + end + end + if ch == 1 then _G.minetest.swap_node({x=x,y=y,z=z},{name = newnode[1], param2 = newnode[2]}) end + + end + end + end + end + + --2nd pass + -- smooth stones below slope_stone_outer_cut if there is at least one air in neighbor 4 diag. positions + -- and set same param2 as above outer_cut + -- slope = 2, outer cut = 3, inner cut = 4, inner = 5 + + for i = x1,x2 do + for j = y1,y2 do + for k = z1,z2 do + local x = i;local y = j; local z = k; + + if is_solid(x,y,z) and is_solid(x,y+1,z,true) == 3 then -- fix stone below outer cut + if not is_solid(x-1,y,z-1) or not is_solid(x+1,y,z-1) or not is_solid(x-1,y,z+1) + or not is_solid(x+1,y,z+1) then + -- replace with inner cut to smooth diag. ramps + local param2 = _G.minetest.get_node({x=x,y=y+1,z=z}).param2; + _G.minetest.swap_node({x=x,y=y,z=z},{name = "moreblocks:slope_stone_inner_cut", param2 = param2}) + end + + --fix possible holes + elseif is_solid(x,y,z,true) == 5 and not is_solid(x,y+1,z) then --hole fix: inner + if is_solid(x-1,y,z-1) and is_solid(x+1,y,z-1) and is_solid(x-1,y,z+1) + and is_solid(x+1,y,z+1) then + _G.minetest.swap_node({x=x,y=y,z=z},{name = "default:stone"}) + end + elseif is_solid(x,y,z,true) == 4 and not is_solid(x,y+1,z) then -- hole fix: inner cut + if is_solid(x-1,y,z-1) and is_solid(x+1,y,z-1) and is_solid(x-1,y,z+1) + and is_solid(x+1,y,z+1) then + _G.minetest.swap_node({x=x,y=y,z=z},{name = "default:stone"}) + end + + elseif is_solid(x,y,z,true)<0 and not is_solid(x,y+1,z) then -- attempt to smooth blocky stones near outer cuts + local x0,z0; + if is_solid(x-1,y,z,true) == 3 -- outer cut + then x0 = x-1; z0 = z; + elseif is_solid(x+1,y,z,true) == 3 + then x0 = x+1; z0 = z; + elseif is_solid(x-1,y,z-1,true) == 3 + then x0 = x-1; z0 = z-1; + elseif is_solid(x+1,y,z+1,true) == 3 + then x0 = x+1; z0 = z+1; + end + if x0 then + local param2 = _G.minetest.get_node({x=x0,y=y,z=z0}).param2; + _G.minetest.swap_node({x=x,y=y,z=z},{name = "moreblocks:slope_stone_inner_cut", param2 = param2}) + end + end + + end + end + end + + + + + end +end \ No newline at end of file diff --git a/textures/puzzle_NOT.png b/textures/puzzle_NOT.png new file mode 100644 index 0000000000000000000000000000000000000000..f2520412f364207888e73dd0578db035c2bf2a3d GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|Ea{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK1jNi|YVTQNO2) zV~B!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK1jNi|YVTQKhGg zV~B-+@(ug_cb%O*f@jQBVA5jNN@EE6zgeQg#o;KA!&-%DT;iKp)S5Idh^Dq?DF`KA zT%dbm!I5?c2fa-?V!RHnCm2q+PLtK({Xdc6tjQl{=0zq9hXY&XRTy0y*d8Vv?H7>p r@Q_kbxAXYaW+7$Iq;=bVzbwNmN#18Y!Or)9j$rU~^>bP0l+XkKGXhEW literal 0 HcmV?d00001 diff --git a/textures/puzzle_button_on.png b/textures/puzzle_button_on.png new file mode 100644 index 0000000000000000000000000000000000000000..8de2a3b9607aff55f116f7082a2f699170fd08fb GIT binary patch literal 256 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|Ea{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK1jNi|YVTQLU$o zV~B-+@(ug_cY~ch!lz7CVA5jNN@EE6zgeQg#o;KA!&-%DT;iKp)S5Idh@`b;DF`KA zT%dhq!6AMj79{~^4nbP0l+XkKFt$sr literal 0 HcmV?d00001 diff --git a/textures/puzzle_delayer.png b/textures/puzzle_delayer.png new file mode 100644 index 0000000000000000000000000000000000000000..3afdbd52dfb730d0595ed674f5815fd2012fa404 GIT binary patch literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|Ea{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK1jNi|YVT(PB>* z#}EtuwUZw59#Rl+`Dg4S@@?zW>x;oDX=+c;eNCSc$ov2AAHSb9*6fekn>TNAKPCix(a~ZsiqY lWXL|e{BiFu(*&g=_9g#cJ==QB%LV9M22WQ%mvv4FO#ll$Y`g#f literal 0 HcmV?d00001 diff --git a/textures/puzzle_diode.png b/textures/puzzle_diode.png new file mode 100644 index 0000000000000000000000000000000000000000..d625b6b1e8a23a25a113ab88bf2ed6cf3503844d GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|Ea{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK1jNi|YVTQNO2) zV~Bv7a;QjUeQMJA4R`Ek>g0pxRnOXZ!xz^E9ocCKHsfE{jt?97} z9@hqmL}n?c5BHwW*%*?xKi!KZ$m`lI?gtEO!h*jtC(+u2zpC@>va_bmG@ua&5wpe5t|rB9mVtT*QX PoyOql>gTe~DWM4fgSTAb literal 0 HcmV?d00001 diff --git a/textures/puzzle_equalizer.png b/textures/puzzle_equalizer.png new file mode 100644 index 0000000000000000000000000000000000000000..01cdee5126c62b1334965e5e86325f5865063ff6 GIT binary patch literal 248 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|Ea{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK1jNi|YVTQK_ek zV~BYce^;VqYdepV5O< zW-9<$7bO;d-#6t@1{Y(?oXy4S*l$RGV_0~tiuK&alPU`4J(u;F%=T9wzIA+UX!4YP nUU3P91z$h)RteX#_=fVEbiGQr)Au0~=l}*!S3j3^P6!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK1jNi|YVT(KJsN z#}EturIVt$S`2txKP%pKx*nJozL~Rp%HOS9d&89jRV)_AF(noIeGp7~Ch_=#djq3d zjLVWKMzL%R1_xM7pU(-(TKn(>Zxln;TE!OE0}M-#^Y2{h_r6yAnxj(ur|e$!2);Q_ zTQ|tcJmx$Rd{FM?m%=q$Uhg^Fx%tvN>xcLHcW;;4RkPz@LpKA%Hl6wF%&*L6Ja)$T Y{Jm?Zo@g){0A0x7>FVdQ&MBb@0Q^v9AOHXW literal 0 HcmV?d00001 diff --git a/textures/puzzle_platform.png b/textures/puzzle_platform.png new file mode 100644 index 0000000000000000000000000000000000000000..9f13dcf63d3ab105d76a623216621ab5780ac1ed GIT binary patch literal 276 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|Ea{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK1jNi|YVT(L_%d z#}Etuy_2H34l4+_{^iI!Sn45mL-y>y8kgcXEhc9Vn~78jUfKD)>xG11;W^%T4xSqZ zSEG`9ET6x0ieN}M(3Zh*u)5#zWk>{rrfPOB_luj|m5L2Pr?fXr+5OA%gM@GT9@cq3 z#V#BTT)maQq|Hb@JSchf)_>2#cYQDX9_2i{pO?Lah2h#P|J$=)xHop5;L&mu@D8e7 RlMQqngQu&X%Q~loCIEcbVQ~Ne literal 0 HcmV?d00001 diff --git a/textures/puzzle_setter.png b/textures/puzzle_setter.png new file mode 100644 index 0000000000000000000000000000000000000000..9d0267ea8ebd6826da69a1511f5d26f9bd91e550 GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|Ea{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK1jNi|YVTQM0Fu zV~BF$hvaPzfW z$F{2(kD#S%U#V_4W8F<|Kk%cUw^{gf96~PrPXDA5f{Vj zeLB|M;(U1Pc&v10#LT=By}Z;C1rt33 zJwt=FTwDi$irPF~978PpuTC=LV^-v0akL7&^yxqUk9vm#<}6c0IPU~5Ibo7uqaAt3 z&Bcl1IPZlCyzQ%&O_B2!0J1JhEdIW4%Aw2Q912h7%q`Y+*t@}+W9zl@1e?{f_}!ep z&S!f3?MXUg)dwx>%MDwnu@%f@`(~GyZ~E&~;Fguo6&7rL`ug=Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02y>eSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E-^5z3JSmg00;m{L_t(YOSP7HP*YhJhvmJ13koVbNkGPRe|1vSHzkogS~BT$zhqi0c`TMp^!DCL z%_xYA?_zZExxB~I)1L%@MN^ZLEH=BUu#mKSkD$0@gx)sV(l*xCKHlB|Hu1{F+uKLz zEh8wH-DI1Fq~U3l6zHWFdZW250LYum&mmJhp2?WT!Md5-W3{+_Vnq~ zw;68$a2UF~yDQ2lDSm`fe?ra9pyu$1zSFS-=Msm`5=X(}lE%(~T^Nl6OBy*#9E?5R z_t!IRp?m9h23Gj`XA^c+mQx@PaHV(pHeh;snnIzZL`Gi!!tbWvuBO1s!<2H$ z)YR1LB=i87ceJ+`2#f2kRAXx^IAc{rReBuUf#F7 zzohL7?g`!_J@5y`&R{X#S!(Af#W_k{J>~9Ra`)|E3b$=i2Um%cyZp-l$({p(-TN8A z;dgv|i?@4cMtyrFKmT==^Z+>cscEUn;o(Kww%+sc=bSq8bYxV$u%K4Y&+<58AA-!z zd9I;Jtx{>!3mUartyI$XL_AEpw6tbHrBNx>^Uv>|JW;lFYuetu7n748Kh|FhJwPUt zkx0o29v&s0p0u#Ns<|gJv55A^qxAEM3kwUvn%iyJs0*Q-r^T$(2Kb|@*wR4%w$Xd{-Se9T)FxRil!erm`X}h&d=*S>j4m21+_B$%$bV@E3Tu4x7KcGF|*-V*!0=j zL7}8L0Juu+oFNaHy{p7-DSSYR?Hv28?YR~>hM7(Mx{VaXFcUS*h&>A(K^Hzq4}iHy zBq}N@im|pNqe%HG<62Y8E^{p30x!09kYJsGvE&p;uS_=<+c*lX@I4l|<_(tBD_31J zK;m%NlHw9RpRaQZdIM0YRMpkhxv{ZC6uFLKs#cra-(cBoj^kU}iLEz@Z5{iu&Op+c z80#dmaS&MIS(Z5ZMytEVrllB6DvIQvJ4dae!Vj&^El3Xl0Yjls)Yson2nt9;(L&Vl zrm^Y$4OZ=DHY{^2&jK&7wC}Up1lDV{38-ufT&J0J^DFOV7^BMu2r(?AwzgI(mA)X2Lw#MWhesxY{0qfUR~tidX=YXob8NQ-j%k5sn&TgsVLQ#SZ5yo{P0VUm zttrExWE3TOZEI|5e9+ykUDWE`z0xuONU2mZdzh_sTAZ(68j4&+kaCRCt(9x;n3&d? zZfsm{(YW5c!Nja??fRP4YpJNw4a6`DHAp1*b+ooJyO>btx5Ed$3jhIw$K$cstn9x; z#jP{RMv(826(tB-i5gKcE2}W8sL0AP6muOxa!@pWok`B$P7Byv7K`0j$T00;pZ zfLtc$uvm0j)8&)j{KH`r5o4Hvpjie8)H=(+Aj!xm-q9f^`kOX-3yaC@mxwf)WtDlY z_W*Q)n*o2~;OsIV^zga-78$f!2qVvU7(oybsyG803h9_!ROn8kaYvyn*XAY1O6wyeEA=p*{W>5 SX?YF+00000#LT=By}Z;C1rt33 zJwt=FTwDi$ih4a=978Pp&rUkabwq)OO}WE;{l5R)PyYVNw#rKtpO(AbR^*p#mT#Ji z!J<`~OAC||4jf8K+j{M(0Yk!prc-LiZbU8qV!XG!Vb137bsRDdnP%lo2Rh{wr=4{< zVrjlkhRHrB{cukB1M9rMTJuEj?R5WfD#s>%$G3v(ZpqU7g18PjG8{PeA@_0bFY|;| ayO?!bp77=x#)pUXO@geCxQq-Q7q literal 0 HcmV?d00001 diff --git a/textures/puzzle_switch_on1.png b/textures/puzzle_switch_on1.png new file mode 100644 index 0000000000000000000000000000000000000000..c67d4cdde099cdcf9b8cab511191384c897ffaf9 GIT binary patch literal 2193 zcmV;C2yXX@P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02y>eSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E-^5z3JSmg00;p|L_t(YOSP5>RFhX0$N2&X35&9dShckX zB83Ko1_@!+DpG5y9i^uuQk_nn)1HowT1P+~1tF|O6k7$Y7&gTS2oxz02_R$-iGcu$ z2}#Jlp$J((=V>x1Emr5ung6-xJNJCwd-wP5eeZqmSv>te>%Rs(dGf^Uy_wz=Fg-mz zIXS7-YRAXNfip8RWB%|uy%GRM3Z&QT$HvA62M3vSCWA(2F`2BsJ{pxqr!s~Hhs0tr zXaHb;kk%^!27`ge<5OuAQg3fHzWQcUedDd0bxru%hU*Q@_4Q5lHMQ5NR2tCesK976 zf-du7<^h0qpcH)!MsIglR%yoh#$v9FGo&0CR1R=e;MqT@05-r9(hKqV`ImAj6f%>^ zeE9I;8v#Hgg+flHkSp=!TXQ#8u*+`qI*1}dhlD_s5{NQFCot*lPD#gYaXV2!JVz?{ zQ|?xf5Sc{Qs5QT5{1%{6sk*y5&s;hCVR7W{#-xe|l~ug*8WHY>7*{JTuan_ShL_;L z%W8f_6*5j`K+|}6$mVi0XfAj(X;8Qvsfk61F>|AUyCaNYZv1|M3`#Hse zte+%VrE=^=Wj3%&&z~gS_SA1Chz4n_5uLjf@)S@ zt6HBM65oyPEJwn+9%ug#c23eZfGnS71|OKQ;GWihXXhqZen7>9CQ`{>T{>(PSLrd z=+h|FjStYoka%`*B0nry5|t#we5MNgSnV6H@!q6)XQO&$oZ9PSwcj=+a+ea5B#lVq zultA-5_V@Tx-NKQK}2b3DTBcpw({SYS$>WM}pQrUi~gwmxy4vwKCLs|l3a6zfLN2;o- zOkshZ0l@GmWQbFj-|F>KIjpqZ+W>)8PShH3ppR&m_WYN=9WG)@a8LL?XN#eq2! ztbA&hZO9HwOG%YTBy%!QDwSF388I1X=Q2k`Gqjv;5%6Ha7u0C6LOf}l&`fJ|hN&iM zJ%;DGTO0i>gT5azYNx-`{tT8MU&IUG!dLV`jxDgIl}?}MC8Qlqee~$j8~~W~f`WoQ zxjP+xT(F=XT0(;P4OxYV9X2pl9wGk~OF#U#?rmtk2S($*S>k<&@%2v1dbGwRLhk-` zcfy{IEo=I1mr*Qjn=Cy_;NRrzEk0K~yP3_C4AfX(cQpUFYp#t|736%|!ixprj5UR+z#W z3>?ILW^yN#bm3YN-0z;%3I^oZYGGexy$tJe{_=%ex0=BLZVvh~07xVh{rXFD;;96u zGgb?#VRo$+o@AIe$I^eq8Y!?19FzHz$Efb2E4@z~FE=R5C5^ z5_Z+`6&|N;7X56AsI;`jL$-Ag0@w}6@v4D3EV619Jz|K-xpY90W{ z188hiNFtMHBy#qp%nz{62+xzHDaxaNSrrf`O-*opfcl(`Q z378e=tkG!rd_I`K{^tT0J@9)67YLB3xzVfjdVr~7K=b;~oBWYp5BQ(-^y$9=7H7?C T2v)&|00000NkvXXu0mjf%dQ60 literal 0 HcmV?d00001