-new robot scripts

-puzzle privs, puzzle mechanics
master
rnd1 2017-10-21 15:40:52 +02:00
parent 6b97de14d5
commit 046bcc445a
22 changed files with 2861 additions and 175 deletions

View File

@ -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}
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 x0<x1 and z0<z1 then -- lower left
data.block_ids = {baseid,baseid-1-n,baseid-n,baseid-1};
return
elseif x0==x1 and z0<z1 then
data.block_ids = {baseid, baseid-n, baseid+1-n, baseid+1}
return
elseif x0==x1 and z0==z1 then
data.block_ids = {baseid, baseid+1, baseid+1+n, baseid+n}
return
else -- upper left
data.block_ids = {baseid,baseid-1, baseid-1+n, baseid+n}
return
end
end
local cmd_set_triggers = function(pdata, triggers)
pdata.triggers = {};
local dtriggers = pdata.triggers;
for i = 1,#triggers do
dtriggers[i] = triggers[i];
end
-- init triggerdata
pdata.triggerdata = {};
local triggerdata = pdata.triggerdata;
for i = 1, #triggers do
local data = triggers[i];
local id = cmd_get_pos_id(nil,data.pos, true);
local tdata = triggerdata[id];
if not tdata then triggerdata[id] = {}; tdata = triggerdata[id] end
tdata[#tdata+1] = i; -- add index (=id)
triggers[i].init(i) -- initialize trigger
end
end
local cmd_checkpos = function(data,pos,pname) -- does the position pos trigger any triggers?
cmd_get_pos_id(data,pos); -- we dont init new table structure every time but store ids in block_ids
local block_ids = data.block_ids;
local gamedata = data.gamedata;
local triggerdata = data.triggerdata;
local triggers = data.triggers;
for j = 1,4 do -- check 4 nearby blocks cause we could be near border
local block_id = block_ids[j];
local gdata = gamedata;
local tdata = triggerdata[block_id]; -- list of trigger indices in this block
if tdata then
for i = 1,#tdata do -- check all triggers inside block
local trigger = triggers[tdata[i]];
local id = tdata[i]; -- trigger id
if trigger.onetime and gdata[id] then -- trigger already "triggered"
else
--say("trigger " .. id)
trigger.action(pname,id)
end
end
end
end
end
-- PUZZLE COMMANDS
basic_robot.commands.puzzle = {
set_triggers = cmd_set_triggers, checkpos = cmd_checkpos,set_node = cmd_set_node, get_node_inv=cmd_get_node_inv, get_player_inv = cmd_get_player_inv,
get_player = cmd_get_player,
get_meta = 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
if minetest.get_node(pos).name == "basic_robot:spawner" then return end
return minetest.get_meta(pos)
end,
get_gametime = function() return minetest.get_gametime() end,
activate = function(data,mode,tpos)
local spos = data.spawnpos;
if minetest.is_protected(tpos,data.owner) then return end
if not is_same_block(tpos,spos) then return end
local node = minetest.get_node(tpos);
if node.name == "default:furnace" or node.name == "default:furnace_active" then
if mode>0 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,
}

328
init.lua
View File

@ -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, -6<v<6, if v = 0 then stop\n"..
" turn.left(), turn.right(), turn.angle(45)\n"..
" dig.direction()\n"..
" place.direction(\"default:dirt\", optional orientation param)\n"..
@ -1297,7 +1318,7 @@ local on_receive_robot_form = function(pos, formname, fields, sender)
" if index>0 it returns itemname. if itemname == \"\" it checks if inventory empty\n"..
" activate.direction(mode) activates target block\n"..
" pickup(r) picks up all items around robot in radius r<8 and returns list or nil\n"..
" craft(item,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")
minetest.register_privilege("robot", "increased number of allowed active robots")
minetest.register_privilege("puzzle", "allow player to use puzzle. namespace in robots")

92
scripts/fractal_bot.lua Normal file
View File

@ -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 nx<x then nx = x end
if ny<y then ny = y end
if nz<z then nz = z end
end
end
end
end
form[0] = nil;
fractal.form = form;
fractal.nx = nx+1; fractal.ny = ny+1; fractal.nz = nz+1;
end
iterate = function(fractal)
local m = #sponge;
local nx = fractal.nx;
for j = 1, #sponge do
local scoords = sponge[j];
for i = 1, #(fractal.form) do
local coords = (fractal.form)[i];
sponge[#sponge+1] = {scoords[1] + (coords[1]+coords[4]/nx)*sidex, scoords[2] + coords[2]*sidey, scoords[3] + coords[3]*sidez};
end
end
sidex = sidex * fractal.nx;
sidey = sidey * fractal.ny;
sidez = sidez * fractal.nz;
end
make = function(fractal, iter)
sidex = 1;sidey = 1;sidez = 1;
sponge = {{0,0,0}};
for i = 1, iter do
iterate(fractal)
end
end
build = function(sponge)
local grid = 2^2;
local spos = self.spawnpos(); spos.x = spos.x+1;spos.y = spos.y+1;spos.z = spos.z+1;
for j = 1, #sponge do
local scoords = sponge[j];
--local color = (math.floor(scoords[1]/grid) + math.floor(scoords[3]/grid)) % 2;
local nodename = "default:stonebrick"
--if color == 0 then nodename = "default:goldblock" else nodename = "default:diamondblock" end
minetest.swap_node({x=spos.x+scoords[1],y=spos.y+scoords[2],z=spos.z+scoords[3]},{name = nodename})
end
end
clear = function()
local grid = 2^2;
local spos = self.spawnpos(); spos.x = spos.x+1;spos.y = spos.y+1;spos.z = spos.z+1;
for j = 1, #sponge do
local scoords = sponge[j];
minetest.swap_node({x=spos.x+scoords[1],y=spos.y+scoords[2],z=spos.z+scoords[3]},{name = "air"})
end
end
fractal0 = { nx = 5, ny = 5, nz = 5, form = {} }
read_form(fractal0)
self.label("form count: " .. 1+#fractal0.form .. " dim " .. fractal0.nx .. " " .. fractal0.ny .. " " .. fractal0.nz)
make(fractal0,3);
build(sponge)
end
--self.remove()

View File

@ -1,6 +1,11 @@
-- minesweeper
if not data then
m=10;n=10; minescount = 32;
m=24;n=22; minescount = m*n/5;
reward = 30;
if not find_player(4) then error("minesweeper: no players near") end
self.spam(1)
t0 = _G.minetest.get_gametime();
data = {}; spawnpos = self.spawnpos() -- place mines
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
@ -11,10 +16,11 @@ if not data then
for i = 1,m do for j = 1,n do -- render game
if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
puzzle.set_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},{name = "basic_robot:button808080"})
end
end end
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},4) -- safe start spot
puzzle.set_node({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},{name = "basic_robot:button80FF80"})
get_mine_count = function(i,j)
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
for k = -1,1 do for l = -1,1 do
@ -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

View File

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

View File

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

1398
scripts/sokoban.txt Normal file

File diff suppressed because it is too large Load Diff

174
scripts/sokoban_game.lua Normal file
View File

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

View File

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

BIN
textures/puzzle_NOT.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

BIN
textures/puzzle_delayer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

BIN
textures/puzzle_diode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

BIN
textures/puzzle_piston.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

BIN
textures/puzzle_setter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB