merge upstream
10
README.txt
@ -18,4 +18,12 @@ GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
----------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
|
||||
|
||||
GAMEPLAY:
|
||||
|
||||
- robot has limited operations available every run ( 1 run per 1 second).
|
||||
- while using for loops, while loops or function calls it is limited to default 48 such code executions per run
|
||||
- while using 'physical' operations like move/dig robot has (default) 10 operations available per run. Default costs are
|
||||
move=2, dig = 6, insert = 2, place = 2, machine.generate = 6, machine.smelt = 6, machine.grind = 6,
|
219
commands.lua
@ -21,18 +21,17 @@ 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 -- left
|
||||
if dir == 1 then -- left
|
||||
yaw = yaw + pi/2;
|
||||
elseif dir == 2 then --right
|
||||
yaw = yaw - pi/2;
|
||||
elseif dir == 3 then -- forward
|
||||
elseif dir == 4 then
|
||||
yaw = yaw+pi; -- backward
|
||||
elseif dir == 4 then -- backward
|
||||
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 -- left_down
|
||||
yaw = yaw + pi/2;pos.y=pos.y-1
|
||||
elseif dir == 8 then -- right_down
|
||||
@ -40,8 +39,7 @@ local function pos_in_dir(obj, dir) -- position after we move in specified direc
|
||||
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
|
||||
|
||||
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
|
||||
@ -49,12 +47,12 @@ local function pos_in_dir(obj, dir) -- position after we move in specified direc
|
||||
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
|
||||
yaw = yaw + pi;pos.y=pos.y+1
|
||||
end
|
||||
|
||||
if dir ~= 5 and dir ~= 6 then
|
||||
pos.x = pos.x+math.cos(yaw)
|
||||
pos.z = pos.z+math.sin(yaw)
|
||||
pos.x = pos.x - math.sin(yaw) -- math.cos(yaw+pi/2)
|
||||
pos.z = pos.z + math.cos(yaw) -- math.sin(yaw+pi/2)
|
||||
end
|
||||
|
||||
return pos
|
||||
@ -68,7 +66,7 @@ local check_operations = function(name, amount, quit)
|
||||
data.operations = operations
|
||||
else
|
||||
if quit then
|
||||
error("robot out of available operations in one step."); return false
|
||||
error("Robot out of available operations in one step (1s). View available operations with self.operations() and check help to see how much operations each action requires."); return false
|
||||
end
|
||||
return false
|
||||
end
|
||||
@ -77,9 +75,11 @@ end
|
||||
|
||||
|
||||
basic_robot.commands.move = function(name,dir)
|
||||
|
||||
check_operations(name,2,true)
|
||||
local obj = basic_robot.data[name].obj;
|
||||
local pos = pos_in_dir(obj, dir)
|
||||
|
||||
|
||||
-- can move through walkable nodes
|
||||
if minetest.registered_nodes[minetest.get_node(pos).name].walkable then return end
|
||||
-- up; no levitation!
|
||||
@ -90,7 +90,7 @@ basic_robot.commands.move = function(name,dir)
|
||||
|
||||
obj:moveto(pos, true)
|
||||
|
||||
-- sit and stand up for model - doenst work for overwriten obj export
|
||||
-- sit and stand up for model - doesnt work for overwriten obj export
|
||||
-- if dir == 5 then-- up
|
||||
-- obj:set_animation({x=0,y=0})
|
||||
-- elseif dir == 6 then -- down
|
||||
@ -113,14 +113,14 @@ end
|
||||
|
||||
basic_robot.digcosts = { -- 1 energy = 1 coal
|
||||
["default:stone"] = 1/25,
|
||||
|
||||
["default:cloud"] = 10^8,
|
||||
}
|
||||
|
||||
|
||||
basic_robot.commands.dig = function(name,dir)
|
||||
|
||||
local energy = 0;
|
||||
check_operations(name,2,true)
|
||||
check_operations(name,6,true)
|
||||
|
||||
local obj = basic_robot.data[name].obj;
|
||||
local pos = pos_in_dir(obj, dir)
|
||||
@ -168,6 +168,8 @@ end
|
||||
|
||||
|
||||
basic_robot.commands.insert_item = function(name,item, inventory,dir)
|
||||
|
||||
check_operations(name,2,true)
|
||||
local obj = basic_robot.data[name].obj;
|
||||
local tpos = pos_in_dir(obj, dir); -- position of target block
|
||||
local luaent = obj:get_luaentity();
|
||||
@ -210,6 +212,8 @@ basic_robot.commands.insert_item = function(name,item, inventory,dir)
|
||||
end
|
||||
|
||||
basic_robot.commands.take_item = function(name,item, inventory,dir)
|
||||
|
||||
check_operations(name,2,true)
|
||||
local obj = basic_robot.data[name].obj;
|
||||
local tpos = pos_in_dir(obj, dir); -- position of target block
|
||||
local luaent = obj:get_luaentity();
|
||||
@ -280,7 +284,8 @@ basic_robot.no_teleport_table = {
|
||||
basic_robot.commands.pickup = function(r,name)
|
||||
|
||||
if r>8 then return false end
|
||||
|
||||
|
||||
check_operations(name,4,true)
|
||||
local pos = basic_robot.data[name].obj:getpos();
|
||||
local spos = basic_robot.data[name].spawnpos; -- position of spawner block
|
||||
local meta = minetest.get_meta(spos);
|
||||
@ -337,6 +342,8 @@ basic_robot.commands.write_text = function(name,dir,text)
|
||||
end
|
||||
|
||||
basic_robot.commands.place = function(name,nodename, param2,dir)
|
||||
|
||||
check_operations(name,2,true)
|
||||
local obj = basic_robot.data[name].obj;
|
||||
local pos = pos_in_dir(obj, dir)
|
||||
local luaent = obj:get_luaentity();
|
||||
@ -380,7 +387,7 @@ end
|
||||
basic_robot.commands.attack = function(name, target) -- attack range 4, damage 5
|
||||
|
||||
local energy = 0;
|
||||
check_operations(name,2,true);
|
||||
check_operations(name,4,true);
|
||||
|
||||
local reach = 4;
|
||||
local damage = 5;
|
||||
@ -424,9 +431,18 @@ basic_robot.commands.grab = function(name,target)
|
||||
|
||||
end
|
||||
|
||||
--local minetest_version = minetest.get_version().string;
|
||||
basic_robot.commands.read_book = function (itemstack) -- itemstack should contain book
|
||||
local data = itemstack:get_meta():to_table().fields -- 0.4.16
|
||||
--local data = minetest.deserialize(itemstack:get_metadata()) -- pre 0.4.16
|
||||
local data;
|
||||
--if minetest_version == "0.4.16" then
|
||||
data = itemstack:get_meta():to_table().fields -- 0.4.16
|
||||
if data and data.text then
|
||||
data.text = data.text:gsub(string.char(13),string.char(10)) --for unknown reason books sometime? convert \n (10) to CR (13)
|
||||
end
|
||||
-- else
|
||||
-- local data = minetest.deserialize(itemstack:get_metadata()) -- pre 0.4.16
|
||||
-- end
|
||||
|
||||
if data then
|
||||
return data.title,data.text;
|
||||
else
|
||||
@ -441,10 +457,11 @@ basic_robot.commands.write_book = function(name,title,text) -- returns itemstack
|
||||
local data = {}
|
||||
|
||||
if title == "" or not title then title = "program book "..minetest.get_gametime() end
|
||||
data.title = title
|
||||
data.title = title or ""
|
||||
data.text = text or ""
|
||||
data.text_len = #data.text
|
||||
data.page = 1
|
||||
data.description = title or ""
|
||||
data.page_max = math.ceil((#data.text:gsub("[^\n]", "") + 1) / lpp)
|
||||
data.owner = name
|
||||
--local data_str = minetest.serialize(data) -- pre 0.4.16
|
||||
@ -468,15 +485,16 @@ basic_robot.give_drops = function(nodename, inv) -- gives apropriate drops when
|
||||
if max_items==0 then -- just drop all the items (taking the rarity into consideration)
|
||||
max_items = #table.drop.items or 0;
|
||||
end
|
||||
max_items = math.random(max_items) -- return random number of items
|
||||
local drop = table.drop;
|
||||
local i = 0;
|
||||
for k,v in pairs(drop.items) do
|
||||
if i > max_items then break end; i=i+1;
|
||||
if i > max_items then break end;
|
||||
i=i+1;
|
||||
local rare = v.rarity or 1;
|
||||
if math.random(1, rare)==1 then
|
||||
if rare>0 and math.random(1, rare)==1 then
|
||||
dropname = v.items[math.random(1,#v.items)]; -- pick item randomly from list
|
||||
inv:add_item("main",dropname);
|
||||
|
||||
end
|
||||
end
|
||||
else
|
||||
@ -493,6 +511,7 @@ local render_text = function(text,linesize)
|
||||
local count = math.floor(string.len(text)/linesize)+1;
|
||||
local width = 18; local height = 24;
|
||||
local tex = "";
|
||||
local ret = {};
|
||||
local y = 0; local x=0;
|
||||
for i=1,string.len(text) do
|
||||
local cb = string.byte(text,i);
|
||||
@ -501,27 +520,30 @@ local render_text = function(text,linesize)
|
||||
y=y+1; x=0
|
||||
else
|
||||
c = string.format("%03d",cb)..".png"
|
||||
tex = tex .. ":" .. (x*width) .. "," .. (y*height) .. "=" .. c;
|
||||
ret[#ret+1] = ":" .. (x*width) .. "," .. (y*height) .. "=" .. c;
|
||||
--tex = tex .. ":" .. (x*width) .. "," .. (y*height) .. "=" .. c;
|
||||
if x==linesize-1 then y=y+1 x=0 else x=x+1 end
|
||||
end
|
||||
end
|
||||
local background = "(black_screen.png^[resize:"..(linesize*width).. "x".. (linesize*height) ..")";
|
||||
tex = "([combine:"..(linesize*width).."x"..(linesize*height)..tex..")";
|
||||
tex = background .. "^" .. tex;
|
||||
return tex;
|
||||
--tex = "([combine:"..(linesize*width).."x"..(linesize*height)..tex..")";
|
||||
return background .. "^" .."([combine:"..(linesize*width).."x"..(linesize*height)..table.concat(ret,"")..")";
|
||||
end
|
||||
|
||||
|
||||
basic_robot.commands.display_text = function(obj,text,linesize,size)
|
||||
if not linesize or linesize<1 then linesize = 20 end
|
||||
if not linesize or linesize<1 then linesize = 20 elseif linesize>40 then linesize = 40 end
|
||||
if size and size<=0 then size = 1 end
|
||||
|
||||
if string.len(text)>linesize*linesize then text = string.sub(text,1,linesize*linesize) end
|
||||
local tex = render_text(text,linesize);
|
||||
if not size then return tex end
|
||||
|
||||
if string.len(tex)<60000 then
|
||||
obj:set_properties({textures={"arrow.png","basic_machine_side.png",tex,"basic_machine_side.png","basic_machine_side.png","basic_machine_side.png"},visual_size = {x=size,y=size}})
|
||||
if string.len(tex)<=1600 then
|
||||
obj:set_properties({
|
||||
textures = {"topface.png","legs.png",tex,"face-back.png","left-hand.png","right-hand.png"},
|
||||
--textures={"arrow.png","basic_machine_side.png",tex,"basic_machine_side.png","basic_machine_side.png","basic_machine_side.png"},
|
||||
visual_size = {x=size,y=size}})
|
||||
else
|
||||
self.label("error: string too long")
|
||||
end
|
||||
@ -529,6 +551,8 @@ end
|
||||
|
||||
local robot_activate_furnace = minetest.registered_nodes["default:furnace"].on_metadata_inventory_put; -- this function will activate furnace
|
||||
basic_robot.commands.activate = function(name,mode, dir)
|
||||
|
||||
check_operations(name,2,true);
|
||||
local obj = basic_robot.data[name].obj;
|
||||
local tpos = pos_in_dir(obj, dir); -- position of target block in front
|
||||
|
||||
@ -734,13 +758,16 @@ basic_robot.commands.keyboard = {
|
||||
|
||||
basic_robot.commands.craftcache = {};
|
||||
|
||||
basic_robot.commands.craft = function(item, mode, idx, name)
|
||||
basic_robot.commands.craft = function(item, mode, idx,amount, name)
|
||||
amount = amount and tonumber(amount) or 1;
|
||||
if amount<0 then amount = 1 end
|
||||
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
|
||||
local itemlist = {}; local output = "";
|
||||
if cache.item == item then-- read cache
|
||||
if cache.item == item and cache.idx == idx then -- read cache
|
||||
itemlist = cache.itemlist;
|
||||
output = cache.output;
|
||||
else
|
||||
@ -759,6 +786,7 @@ basic_robot.commands.craft = function(item, mode, idx, name)
|
||||
itemlist[item]=(itemlist[item] or 0)+1;
|
||||
end
|
||||
cache.item = item;
|
||||
cache.idx = idx;
|
||||
cache.itemlist = itemlist;
|
||||
cache.output = output;
|
||||
|
||||
@ -797,12 +825,12 @@ basic_robot.commands.craft = function(item, mode, idx, name)
|
||||
local inv = minetest.get_meta(pos):get_inventory();
|
||||
|
||||
for item,quantity in pairs(itemlist) do
|
||||
local stack = ItemStack(item .. " " .. quantity);
|
||||
local stack = ItemStack(item .. " " .. quantity*amount);
|
||||
if not inv:contains_item("main",stack) then return false end
|
||||
end
|
||||
|
||||
for item,quantity in pairs(itemlist) do
|
||||
local stack = ItemStack(item .. " " .. quantity);
|
||||
local stack = ItemStack(item .. " " .. quantity*amount);
|
||||
inv:remove_item("main",stack);
|
||||
end
|
||||
|
||||
@ -866,7 +894,7 @@ basic_robot.technic = { -- data cache
|
||||
|
||||
compressor_recipes = { --[in] ={fuel cost, out, quantity of material required for processing}
|
||||
["default:snow"] = {1,"default:ice"},
|
||||
["default:coalblock"] = {16,"default:diamond"},
|
||||
["default:coalblock"] = {41,"default:diamond"}, -- to smelt diamond dust to diamond need 25 coal + 16 for grinder
|
||||
},
|
||||
}
|
||||
|
||||
@ -885,7 +913,7 @@ basic_robot.commands.machine = {
|
||||
-- convert fuel into energy
|
||||
generate_power = function(name,input, amount) -- fuel used, if no fuel then amount specifies how much energy builtin generator should produce
|
||||
|
||||
check_operations(name,1.5, true)
|
||||
check_operations(name,6, true)
|
||||
|
||||
if amount and amount>0 then -- attempt to generate power from builtin generator
|
||||
local pos = basic_robot.data[name].spawnpos; -- position of spawner block
|
||||
@ -932,7 +960,7 @@ basic_robot.commands.machine = {
|
||||
smelt = function(name,input,amount) -- input material, amount of energy used for smelt
|
||||
|
||||
local energy = 0; -- can only do one step at a run time
|
||||
check_operations(name,2,true)
|
||||
check_operations(name,6,true)
|
||||
|
||||
if string.find(input," ") then return nil, "0: only one item per smelt" end
|
||||
|
||||
@ -994,6 +1022,7 @@ basic_robot.commands.machine = {
|
||||
-- grind
|
||||
grind = function(name,input)
|
||||
--[in] ={fuel cost, out, quantity of material required for processing}
|
||||
check_operations(name,6,true)
|
||||
local recipe = basic_robot.technic.grinder_recipes[input];
|
||||
if not recipe then return nil, "unknown recipe" end
|
||||
local cost = recipe[1]; local output = recipe[2];
|
||||
@ -1024,6 +1053,7 @@ basic_robot.commands.machine = {
|
||||
-- compress
|
||||
compress = function(name,input)
|
||||
--[in] ={fuel cost, out, quantity of material required for processing}
|
||||
check_operations(name,6,true)
|
||||
local recipe = basic_robot.technic.compressor_recipes[input];
|
||||
if not recipe then return nil, "unknown recipe" end
|
||||
local cost = recipe[1]; local output = recipe[2];
|
||||
@ -1050,13 +1080,15 @@ basic_robot.commands.machine = {
|
||||
end,
|
||||
|
||||
transfer_power = function(name,amount,target)
|
||||
|
||||
check_operations(name,2, true);
|
||||
local pos = basic_robot.data[name].spawnpos;
|
||||
local data = basic_robot.data[name];
|
||||
local tdata = basic_robot.data[target];
|
||||
if not tdata then return nil, "target inactive" end
|
||||
|
||||
local energy = 0; -- can only do one step at a run time
|
||||
check_operations(name,0.5, true);
|
||||
|
||||
|
||||
energy = data.menergy or 0;
|
||||
if amount>energy then return nil,"energy too low" end
|
||||
@ -1209,10 +1241,10 @@ 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
|
||||
if not player then error("player does not exist"); return end
|
||||
local spos = data.spawnpos;
|
||||
local ppos = player:getpos();
|
||||
if not is_same_block(ppos,spos) then return end
|
||||
if not is_same_block(ppos,spos) then error("can not get player in another protection zone") return end
|
||||
return player
|
||||
end
|
||||
|
||||
@ -1221,7 +1253,7 @@ local cmd_get_player_inv = function(data,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
|
||||
if not is_same_block(ppos,spos) then error("can not get player in another protection zone") return end
|
||||
return player:get_inventory();
|
||||
end
|
||||
|
||||
@ -1320,7 +1352,9 @@ basic_robot.commands.puzzle = {
|
||||
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)
|
||||
local meta = minetest.get_meta(pos);
|
||||
if not meta then error("get_meta in puzzle returned nil"); return end
|
||||
return meta
|
||||
end,
|
||||
get_gametime = function() return minetest.get_gametime() end,
|
||||
|
||||
@ -1354,3 +1388,106 @@ basic_robot.commands.puzzle = {
|
||||
return true
|
||||
end,
|
||||
}
|
||||
|
||||
-- VIRTUAL PLAYER --
|
||||
|
||||
|
||||
local Vplayer = {};
|
||||
function Vplayer:new(name) -- constructor
|
||||
if not basic_robot.data[name].obj then return end -- only make it for existing robot
|
||||
if basic_robot.virtual_players[name] then return end -- already exists
|
||||
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.obj = basic_robot.data[name].obj;
|
||||
o.data = basic_robot.data[name];
|
||||
|
||||
local spawnpos = o.data.spawnpos;
|
||||
local meta = minetest.get_meta(spawnpos); if not meta then return end
|
||||
o.inv = meta:get_inventory();
|
||||
|
||||
basic_robot.virtual_players[name] = o;
|
||||
end
|
||||
|
||||
-- functions
|
||||
function Vplayer:getpos() return self.obj:getpos() end
|
||||
function Vplayer:remove() end
|
||||
function Vplayer:setpos() end
|
||||
function Vplayer:move_to() end
|
||||
function Vplayer:punch() end
|
||||
function Vplayer:rightlick() end
|
||||
function Vplayer:get_hp() return 20 end
|
||||
function Vplayer:set_hp() return 20 end
|
||||
|
||||
function Vplayer:get_inventory() return self.inv end
|
||||
function Vplayer:get_wield_list() return "main" end
|
||||
function Vplayer:get_wield_index() return 1 end
|
||||
function Vplayer:get_wielded_item() return self.inv:get_stack("main", 1) end
|
||||
function Vplayer:set_wielded_item() end
|
||||
function Vplayer:set_armor_groups() end
|
||||
function Vplayer:get_armor_groups() return {fleshy = 100} end
|
||||
function Vplayer:set_animation() end
|
||||
function Vplayer:get_animation() end
|
||||
function Vplayer:set_attach() end
|
||||
function Vplayer:get_attach() end
|
||||
function Vplayer:set_detach() end
|
||||
function Vplayer:set_bone_position() end
|
||||
function Vplayer:get_bone_position() end
|
||||
function Vplayer:set_properties() end
|
||||
function Vplayer:get_properties() end
|
||||
function Vplayer:is_player() return true end
|
||||
function Vplayer:get_nametag_attributes() end
|
||||
function Vplayer:set_nametag_attributes() end
|
||||
|
||||
function Vplayer:set_velocity() end
|
||||
function Vplayer:get_velocity() end
|
||||
function Vplayer:set_acceleration() end
|
||||
function Vplayer:get_acceleration() end
|
||||
function Vplayer:set_yaw() end
|
||||
function Vplayer:get_yaw() end
|
||||
function Vplayer:set_texture_mod() end
|
||||
function Vplayer:get_luaentity() end
|
||||
|
||||
function Vplayer:get_player_name() return self.data.name end
|
||||
function Vplayer:get_player_velocity() return {x=0,y=0,z=0} end
|
||||
function Vplayer:get_look_dir() return {x=1,y=0,z=0} end
|
||||
function Vplayer:get_look_vertical() return 0 end
|
||||
function Vplayer:get_look_horizontal() return 0 end
|
||||
function Vplayer:set_look_vertical() end
|
||||
function Vplayer:set_look_horizontal() end
|
||||
function Vplayer:get_breath() return 1 end
|
||||
function Vplayer:set_breath() end
|
||||
function Vplayer:set_attribute() end
|
||||
function Vplayer:get_attribute() end
|
||||
function Vplayer:set_inventory_formspec() end
|
||||
function Vplayer:get_inventory_formspec() return "" end
|
||||
function Vplayer:get_player_control() return {} end
|
||||
function Vplayer:get_player_control_bits() return 0 end
|
||||
function Vplayer:set_physics_override() end
|
||||
function Vplayer:get_physics_override() return {} end
|
||||
function Vplayer:hud_add() end
|
||||
function Vplayer:hud_remove() end
|
||||
function Vplayer:hud_change() end
|
||||
function Vplayer:hud_get() end
|
||||
function Vplayer:hud_set_flags() end
|
||||
function Vplayer:hud_get_flags() return {} end
|
||||
function Vplayer:hud_set_hotbar_itemcount() end
|
||||
function Vplayer:hud_get_hotbar_itemcount() return 0 end
|
||||
function Vplayer:hud_set_hotbar_image() end
|
||||
function Vplayer:hud_get_hotbar_image() return "" end
|
||||
function Vplayer:hud_set_hotbar_selected_image() end
|
||||
function Vplayer:hud_get_hotbar_selected_image() return "" end
|
||||
function Vplayer:set_sky() end
|
||||
function Vplayer:get_sky() end
|
||||
function Vplayer:set_clouds() end
|
||||
function Vplayer:get_clouds() end
|
||||
function Vplayer:override_day_night_ratio() end
|
||||
function Vplayer:get_day_night_ratio() end
|
||||
function Vplayer:set_local_animation() end
|
||||
function Vplayer:get_local_animation() end
|
||||
function Vplayer:set_eye_offset() end
|
||||
function Vplayer:get_eye_offset() end
|
||||
|
||||
|
||||
-- code for act borrowed from: https://github.com/minetest-mods/pipeworks/blob/fa4817136c8d1e62dafd6ab694821cba255b5206/wielder.lua, line 372
|
||||
|
669
init.lua
@ -2,33 +2,45 @@
|
||||
|
||||
|
||||
basic_robot = {};
|
||||
---- SETTINGS ------
|
||||
basic_robot.call_limit = 48; -- how many execution calls per script run allowed
|
||||
------ SETTINGS --------
|
||||
basic_robot.call_limit = {50,200,1500,10^9}; -- how many execution calls per script run allowed, for auth levels 0,1,2 (normal, robot, puzzle, admin)
|
||||
basic_robot.entry_count = 2 -- how many robots ordinary player can have
|
||||
basic_robot.advanced_count = 16 -- how many robots player with robot privs can have
|
||||
basic_robot.radius = 32; -- divide whole world into blocks of this size - used for managing events like keyboard punches
|
||||
basic_robot.password = "password"; -- IMPORTANT: change it before running mod, password used for authentifications
|
||||
basic_robot.password = "raN___dOM_ p4S"; -- IMPORTANT: change it before running mod, password used for authentifications
|
||||
|
||||
basic_robot.admin_bot_pos = {x=0,y=1,z=0} -- position of admin robot spawner that will be run automatically on server start
|
||||
|
||||
basic_robot.maxoperations = 10; -- how many operations (dig, place,move,...,generate energy,..) available per run, 0 = unlimited
|
||||
basic_robot.dig_require_energy = true; -- does robot require energy to dig stone?
|
||||
|
||||
basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes inventories to prevent player abuses
|
||||
["craft_guide:sign_wall"] = true,
|
||||
["basic_machines:battery_0"] = true,
|
||||
["basic_machines:battery_1"] = true,
|
||||
["basic_machines:battery_2"] = true,
|
||||
["basic_machines:generator"] = true,
|
||||
}
|
||||
basic_robot.maxoperations = 2; -- how many operations (dig, generate energy,..) available per run, 0 = unlimited
|
||||
basic_robot.dig_require_energy = true; -- does robot require energy to dig?
|
||||
----------------------
|
||||
----- END OF SETTINGS ------
|
||||
|
||||
basic_robot.http_api = minetest.request_http_api();
|
||||
|
||||
basic_robot.version = "2017/12/18a";
|
||||
basic_robot.version = "2019/01/13a";
|
||||
|
||||
basic_robot.gui = {}; local robogui = basic_robot.gui -- gui management
|
||||
basic_robot.data = {}; -- stores all robot related data
|
||||
--[[
|
||||
[name] = { sandbox= .., bytecode = ..., ram = ..., obj = robot object, spawnpos= ..., authlevel = ...}
|
||||
[name] = { sandbox= .., bytecode = ..., ram = ..., obj = robot object, spawnpos= ..., authlevel = ... , t = code execution time}
|
||||
robot object = object of entity, used to manipulate movements and more
|
||||
--]]
|
||||
basic_robot.ids = {}; -- stores maxid for each player
|
||||
--[name] = {id = .., maxid = .. }, current id for robot controller, how many robot ids player can use
|
||||
|
||||
basic_robot.virtual_players = {}; -- this way robot can interact with the world as "player" TODO
|
||||
|
||||
basic_robot.data.listening = {}; -- which robots listen to chat
|
||||
|
||||
dofile(minetest.get_modpath("basic_robot").."/robogui.lua") -- gui stuff
|
||||
dofile(minetest.get_modpath("basic_robot").."/commands.lua")
|
||||
|
||||
local check_code, preprocess_code,is_inside_string;
|
||||
@ -47,6 +59,7 @@ function getSandboxEnv (name)
|
||||
left_up = 11, right_up = 12, forward_up = 13, backward_up = 14
|
||||
}
|
||||
|
||||
if not basic_robot.data[name].rom then basic_robot.data[name].rom = {} end -- create rom if not yet existing
|
||||
local env =
|
||||
{
|
||||
pcall=pcall,
|
||||
@ -73,14 +86,20 @@ function getSandboxEnv (name)
|
||||
return commands.pickup(r, name);
|
||||
end,
|
||||
|
||||
craft = function(item, idx,mode)
|
||||
return commands.craft(item, mode, idx, name)
|
||||
craft = function(item, idx,mode, amount)
|
||||
return commands.craft(item, mode, idx, amount, name)
|
||||
end,
|
||||
|
||||
pause = function() -- pause coroutine
|
||||
if not basic_robot.data[name].cor then error("you must start program with '--coroutine' to use pause()") return end
|
||||
coroutine.yield()
|
||||
end,
|
||||
|
||||
self = {
|
||||
pos = function() return basic_robot.data[name].obj:getpos() end,
|
||||
spawnpos = function() local pos = basic_robot.data[name].spawnpos; return {x=pos.x,y=pos.y,z=pos.z} end,
|
||||
name = function() return name end,
|
||||
operations = function() return basic_robot.data[name].operations end,
|
||||
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)
|
||||
@ -152,7 +171,7 @@ function getSandboxEnv (name)
|
||||
fire = function(speed, pitch,gravity, texture, is_entity) -- experimental: fires an projectile
|
||||
local obj = basic_robot.data[name].obj;
|
||||
local pos = obj:getpos();
|
||||
local yaw = obj:getyaw();
|
||||
local yaw = obj:getyaw()+ math.pi/2;
|
||||
pitch = pitch*math.pi/180
|
||||
local velocity = {x=speed*math.cos(yaw)*math.cos(pitch), y=speed*math.sin(pitch),z=speed*math.sin(yaw)*math.cos(pitch)};
|
||||
-- fire particle
|
||||
@ -174,6 +193,7 @@ function getSandboxEnv (name)
|
||||
local obj = minetest.add_entity(pos, "basic_robot:projectile");
|
||||
if not obj then return end
|
||||
obj:setvelocity(velocity);
|
||||
obj:set_properties({textures = {texture or "default_furnace_fire_fg.png"}})
|
||||
obj:setacceleration({x=0,y=-gravity,z=0});
|
||||
local luaent = obj:get_luaentity();
|
||||
luaent.name = name;
|
||||
@ -189,7 +209,7 @@ function getSandboxEnv (name)
|
||||
|
||||
label = function(text)
|
||||
local obj = basic_robot.data[name].obj;
|
||||
obj:set_properties({nametag = text}); -- "[" .. name .. "] " ..
|
||||
obj:set_properties({nametag = text or ""}); -- "[" .. name .. "] " ..
|
||||
end,
|
||||
|
||||
display_text = function(text,linesize,size)
|
||||
@ -290,14 +310,15 @@ function getSandboxEnv (name)
|
||||
grab = function(target) return basic_robot.commands.grab(name,target) end,
|
||||
|
||||
|
||||
say = function(text, owneronly)
|
||||
if not basic_robot.data[name].quiet_mode and not owneronly then
|
||||
say = function(text, pname)
|
||||
if not basic_robot.data[name].quiet_mode and not pname then
|
||||
minetest.chat_send_all("<robot ".. name .. "> " .. text)
|
||||
if not basic_robot.data[name].allow_spam then
|
||||
basic_robot.data[name].quiet_mode=true
|
||||
end
|
||||
else
|
||||
minetest.chat_send_player(basic_robot.data[name].owner,"<robot ".. name .. "> " .. text)
|
||||
if not pname then pname = basic_robot.data[name].owner end
|
||||
minetest.chat_send_player(pname,"<robot ".. name .. "> " .. text) -- send chat only to player pname
|
||||
end
|
||||
end,
|
||||
|
||||
@ -336,44 +357,39 @@ function getSandboxEnv (name)
|
||||
return
|
||||
end
|
||||
end,
|
||||
|
||||
run = function(script)
|
||||
if basic_robot.data[name].authlevel < 3 then
|
||||
local err = check_code(script);
|
||||
script = preprocess_code(script);
|
||||
if err then
|
||||
minetest.chat_send_player(name,"#ROBOT CODE CHECK ERROR : " .. err)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local ScriptFunc, CompileError = loadstring( script )
|
||||
if CompileError then
|
||||
minetest.chat_send_player(name, "#code.run: compile error " .. CompileError )
|
||||
return false
|
||||
end
|
||||
|
||||
setfenv( ScriptFunc, basic_robot.data[name].sandbox )
|
||||
|
||||
local Result, RuntimeError = pcall( ScriptFunc );
|
||||
if RuntimeError then
|
||||
minetest.chat_send_player(name, "#code.run: run error " .. RuntimeError )
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
},
|
||||
|
||||
|
||||
rom = basic_robot.data[name].rom,
|
||||
|
||||
string = {
|
||||
byte = string.byte, char = string.char,
|
||||
find = string.find,
|
||||
format = string.format, gsub = string.gsub,
|
||||
gsub = string.gsub,
|
||||
gmatch = string.gmatch,
|
||||
len = string.len, lower = string.lower,
|
||||
upper = string.upper, rep = string.rep,
|
||||
reverse = string.reverse, sub = string.sub,
|
||||
|
||||
format = function(...)
|
||||
local out = string.format(...)
|
||||
if string.len(out) > 1024 then
|
||||
error("result string longer than 1024")
|
||||
return
|
||||
end
|
||||
return out
|
||||
end,
|
||||
|
||||
concat = function(strings, sep)
|
||||
local length = 0;
|
||||
for i = 1,#strings do
|
||||
length = length + string.len(strings[i])
|
||||
if length > 1024 then
|
||||
error("result string longer than 1024")
|
||||
return
|
||||
end
|
||||
end
|
||||
return table.concat(strings,sep or "")
|
||||
end,
|
||||
},
|
||||
math = {
|
||||
abs = math.abs, acos = math.acos,
|
||||
@ -392,13 +408,6 @@ function getSandboxEnv (name)
|
||||
sqrt = math.sqrt, tan = math.tan,
|
||||
tanh = math.tanh,
|
||||
},
|
||||
table = {
|
||||
concat = table.concat,
|
||||
insert = table.insert,
|
||||
maxn = table.maxn,
|
||||
remove = table.remove,
|
||||
sort = table.sort,
|
||||
},
|
||||
os = {
|
||||
clock = os.clock,
|
||||
difftime = os.difftime,
|
||||
@ -412,16 +421,6 @@ function getSandboxEnv (name)
|
||||
tonumber = tonumber, pairs = pairs,
|
||||
ipairs = ipairs, error = error, type=type,
|
||||
|
||||
--_ccounter = basic_robot.data[name].ccounter, -- counts how many executions of critical spots in script
|
||||
|
||||
increase_ccounter =
|
||||
function()
|
||||
local _ccounter = basic_robot.data[name].ccounter;
|
||||
if _ccounter > basic_robot.call_limit then
|
||||
error("Execution limit " .. basic_robot.call_limit .. " exceeded");
|
||||
end
|
||||
basic_robot.data[name].ccounter = _ccounter + 1;
|
||||
end,
|
||||
};
|
||||
|
||||
-- ROBOT FUNCTIONS: move,dig, place,insert,take,check_inventory,activate,read_node,read_text,write_text
|
||||
@ -478,6 +477,43 @@ function getSandboxEnv (name)
|
||||
end
|
||||
|
||||
if authlevel>=1 then -- robot privs
|
||||
|
||||
|
||||
env.table = {
|
||||
concat = table.concat,
|
||||
insert = table.insert,
|
||||
maxn = table.maxn,
|
||||
remove = table.remove,
|
||||
sort = table.sort,
|
||||
}
|
||||
|
||||
env.code.run = function(script)
|
||||
if basic_robot.data[name].authlevel < 3 then
|
||||
local err = check_code(script);
|
||||
script = preprocess_code(script, basic_robot.call_limit[basic_robot.data[name].authlevel+1]);
|
||||
if err then
|
||||
minetest.chat_send_player(name,"#ROBOT CODE CHECK ERROR : " .. err)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local ScriptFunc, CompileError = loadstring( script )
|
||||
if CompileError then
|
||||
minetest.chat_send_player(name, "#code.run: compile error " .. CompileError )
|
||||
return false
|
||||
end
|
||||
|
||||
setfenv( ScriptFunc, basic_robot.data[name].sandbox )
|
||||
|
||||
local Result, RuntimeError = pcall( ScriptFunc );
|
||||
if RuntimeError then
|
||||
minetest.chat_send_player(name, "#code.run: run error " .. RuntimeError )
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
env.self.read_form = function()
|
||||
local fields = basic_robot.data[name].read_form;
|
||||
local sender = basic_robot.data[name].form_sender;
|
||||
@ -524,8 +560,6 @@ function getSandboxEnv (name)
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--special sandbox for admin
|
||||
if authlevel<3 then -- is admin?
|
||||
env._G = env;
|
||||
@ -541,131 +575,162 @@ end
|
||||
-- code checker
|
||||
|
||||
check_code = function(code)
|
||||
--"while ", "for ", "do ","goto ",
|
||||
local bad_code = {"repeat", "until", "_ccounter", "_G", "while%(", "while{", "pcall","\\\""}
|
||||
|
||||
--"while ", "for ", "do ","goto ",
|
||||
local bad_code = {"repeat", "until", "_c_", "_G", "while%(", "while{", "pcall","%.%."} --,"\\\"", "%[=*%[","--[["}
|
||||
for _, v in pairs(bad_code) do
|
||||
if string.find(code, v) then
|
||||
return v .. " is not allowed!";
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
is_inside_string = function(pos,script)
|
||||
local i1=string.find (script, "\"", 1);
|
||||
if not i1 then
|
||||
return false
|
||||
end
|
||||
local i2=0;
|
||||
local par = 1;
|
||||
local identify_strings = function(code) -- returns list of positions {start,end} of literal strings in lua code
|
||||
|
||||
if pos<i1 then
|
||||
return false
|
||||
end
|
||||
while i1 do
|
||||
i2=string.find(script,"\"",i1+1);
|
||||
if i2 then
|
||||
par = 1 - par;
|
||||
local i = 0; local j; local _; local length = string.len(code);
|
||||
local mode = 0; -- 0: not in string, 1: in '...' string, 2: in "..." string, 3. in [==[ ... ]==] string
|
||||
local modes = {
|
||||
{"'","'"},
|
||||
{"\"","\""},
|
||||
{"%[=*%[","%]=*%]"}
|
||||
}
|
||||
local ret = {}
|
||||
while i < length do
|
||||
i=i+1
|
||||
|
||||
local jmin = length+1;
|
||||
if mode == 0 then -- not yet inside string
|
||||
for k=1,#modes do
|
||||
j = string.find(code,modes[k][1],i);
|
||||
if j and j<jmin then -- pick closest one
|
||||
jmin = j
|
||||
mode = k
|
||||
end
|
||||
end
|
||||
if mode ~= 0 then -- found something
|
||||
j=jmin
|
||||
ret[#ret+1] = {jmin}
|
||||
end
|
||||
if not j then break end -- found nothing
|
||||
else
|
||||
return false
|
||||
end
|
||||
if par == 0 then
|
||||
if i1<pos and pos<i2 then
|
||||
return true
|
||||
_,j = string.find(code,modes[mode][2],i); -- search for closing pair
|
||||
if not j then break end
|
||||
if (mode~=2 or string.sub(code,j-1,j-1) ~= "\\") then -- not (" and \")
|
||||
ret[#ret][2] = j
|
||||
mode = 0
|
||||
end
|
||||
end
|
||||
i1=i2;
|
||||
i=j -- move to next position
|
||||
end
|
||||
return false;
|
||||
if mode~= 0 then ret[#ret][2] = length end
|
||||
return ret
|
||||
end
|
||||
|
||||
is_inside_string = function(strings,pos) -- is position inside one of the strings?
|
||||
local low = 1; local high = #strings;
|
||||
if high == 0 then return false end
|
||||
local mid = high;
|
||||
while high>low+1 do
|
||||
mid = math.floor((low+high)/2)
|
||||
if pos<strings[mid][1] then high = mid else low = mid end
|
||||
end
|
||||
if pos>strings[low][2] then mid = high else mid = low end
|
||||
return strings[mid][1]<=pos and pos<=strings[mid][2]
|
||||
end
|
||||
|
||||
local find_outside_string = function(script, pattern, pos, strings)
|
||||
local length = string.len(script)
|
||||
local found = true;
|
||||
local i1 = pos;
|
||||
while found do
|
||||
found = false
|
||||
local i2 = string.find(script,pattern,i1);
|
||||
if i2 then
|
||||
if not is_inside_string(strings,i2) then return i2 end
|
||||
found = true;
|
||||
i1 = i2+1;
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- COMPILATION
|
||||
|
||||
preprocess_code = function(script)
|
||||
--todo: 2018/12 this suddenly stopped working, wtf??
|
||||
|
||||
preprocess_code = function(script, call_limit) -- version 07/24/2018
|
||||
|
||||
--[[ idea: in each local a = function (args) ... end insert counter like:
|
||||
local a = function (args) counter() ... end
|
||||
local a = function (args) counter_check_code ... end
|
||||
when counter exceeds limit exit with error
|
||||
--]]
|
||||
|
||||
script="_ccounter = 0; " .. script;
|
||||
script = script:gsub("%-%-%[%[.*%-%-%]%]",""):gsub("%-%-[^\n]*\n","\n") -- strip comments
|
||||
script="_c_ = 0; " .. script;
|
||||
|
||||
-- process script to insert call counter in every function
|
||||
local _increase_ccounter = " _c_ = _c_ + 1; if _c_ > " .. call_limit ..
|
||||
" then _G.error(\"Execution count \".. _c_ .. \" exceeded ".. call_limit .. "\") end; "
|
||||
|
||||
local i1 -- process script to insert call counter in every function
|
||||
local _increase_ccounter = " if _ccounter > " .. basic_robot.call_limit ..
|
||||
" then error(\"Execution count \".. _ccounter .. \" exceeded ".. basic_robot.call_limit .. "\") end _ccounter = _ccounter + 1; "
|
||||
|
||||
|
||||
local i1=0; local i2 = 0;
|
||||
local i1=0; local i2 = 0;
|
||||
local found = true;
|
||||
|
||||
while (found) do -- PROCESS SCRIPT AND INSERT COUNTER AT PROBLEMATIC SPOTS
|
||||
|
||||
found = false;
|
||||
i2 = nil;
|
||||
local strings = identify_strings(script);
|
||||
|
||||
-- i1 = where its looking
|
||||
local inserts = {};
|
||||
|
||||
local constructs = {
|
||||
{"while%s", "%sdo%s", 2, 6}, -- numbers: insertion pos = i2+2, after skip to i1 = i12+6
|
||||
{"function", ")", 0, 8},
|
||||
{"for%s", "%sdo%s", 2, 4},
|
||||
{"goto%s", nil , -1, 5},
|
||||
}
|
||||
|
||||
for i = 1,#constructs do
|
||||
i1 = 0; found = true
|
||||
while (found) do -- PROCESS SCRIPT AND INSERT COUNTER AT PROBLEMATIC SPOTS
|
||||
|
||||
i2=string.find (script, "while ", i1) -- fix while OK
|
||||
if i2 then
|
||||
if not is_inside_string(i2,script) then
|
||||
found = false;
|
||||
|
||||
i2=find_outside_string(script, constructs[i][1], i1, strings) -- first part of construct
|
||||
if i2 then
|
||||
local i21 = i2;
|
||||
i2=string.find(script, "do", i2);
|
||||
if i2 then
|
||||
script = script.sub(script,1, i2+1) .. _increase_ccounter .. script.sub(script, i2+2);
|
||||
i1=i21+6; -- after while
|
||||
found = true;
|
||||
if constructs[i][2] then
|
||||
i2 = find_outside_string(script, constructs[i][2], i2, strings); -- second part of construct ( if any )
|
||||
if i2 then
|
||||
inserts[#inserts+1]= i2+constructs[i][3]; -- move to last position of construct[i][2]
|
||||
found = true;
|
||||
end
|
||||
else
|
||||
inserts[#inserts+1]= i2+constructs[i][3]
|
||||
found = true -- 1 part construct
|
||||
end
|
||||
|
||||
if found then
|
||||
i1=i21+constructs[i][4]; -- skip to after constructs[i][1]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
i2=string.find (script, "function", i1) -- fix functions
|
||||
if i2 then
|
||||
--minetest.chat_send_all("func0")
|
||||
if not is_inside_string(i2,script) then
|
||||
i2=string.find(script, ")", i2);
|
||||
if i2 then
|
||||
script = script.sub(script,1, i2) .. _increase_ccounter .. script.sub(script, i2+1);
|
||||
i1=i2+string.len(_increase_ccounter);
|
||||
found = true;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
i2=string.find (script, "for ", i1) -- fix for OK
|
||||
if i2 then
|
||||
if not is_inside_string(i2,script) then
|
||||
i2=string.find(script, "do", i2);
|
||||
if i2 then
|
||||
script = script.sub(script,1, i2+1) .. _increase_ccounter .. script.sub(script, i2+2);
|
||||
i1=i2+string.len(_increase_ccounter);
|
||||
found = true;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
i2=string.find (script, "goto ", i1) -- fix goto OK
|
||||
if i2 then
|
||||
if not is_inside_string(i2,script) then
|
||||
script = script.sub(script,1, i2-1) .. _increase_ccounter .. script.sub(script, i2);
|
||||
i1=i2+string.len(_increase_ccounter)+5; -- insert + skip goto
|
||||
found = true;
|
||||
end
|
||||
end
|
||||
--minetest.chat_send_all("code rem " .. string.sub(script,i1))
|
||||
|
||||
end
|
||||
|
||||
return script
|
||||
table.sort(inserts)
|
||||
|
||||
-- add inserts
|
||||
local ret = {}; i1=1;
|
||||
for i = 1, #inserts do
|
||||
i2 = inserts[i];
|
||||
ret[#ret+1] = string.sub(script,i1,i2);
|
||||
i1 = i2+1;
|
||||
end
|
||||
ret[#ret+1] = string.sub(script,i1);
|
||||
|
||||
script = table.concat(ret,_increase_ccounter)
|
||||
return script:gsub("pause%(%)", "_c_ = 0; pause()") -- reset ccounter at pause
|
||||
end
|
||||
|
||||
|
||||
local function CompileCode ( script )
|
||||
|
||||
--minetest.chat_send_all(script)
|
||||
--if true then return nil, "" end
|
||||
|
||||
local ScriptFunc, CompileError = loadstring( script )
|
||||
if CompileError then
|
||||
return nil, CompileError
|
||||
@ -678,17 +743,30 @@ local function initSandbox (name)
|
||||
end
|
||||
|
||||
local function setCode( name, script ) -- to run script: 1. initSandbox 2. setCode 3. runSandbox
|
||||
|
||||
local err;
|
||||
if basic_robot.data[name].authlevel<3 then -- not admin
|
||||
local cor = false;
|
||||
if string.sub(script,1,11) == "--coroutine" then cor = true end
|
||||
|
||||
local authlevel = basic_robot.data[name].authlevel;
|
||||
|
||||
if authlevel<3 then -- not admin
|
||||
err = check_code(script);
|
||||
script = preprocess_code(script);
|
||||
script = preprocess_code(script,basic_robot.call_limit[authlevel+1]);
|
||||
elseif cor then
|
||||
script = preprocess_code(script, basic_robot.call_limit[authlevel+1]); -- coroutines need ccounter reset or 'infinite loops' fail after limit
|
||||
end
|
||||
if err then return err end
|
||||
|
||||
|
||||
local bytecode, err = CompileCode ( script );
|
||||
if err then return err end
|
||||
basic_robot.data[name].bytecode = bytecode;
|
||||
|
||||
if cor then -- create coroutine if requested
|
||||
basic_robot.data[name].cor = coroutine.create(bytecode)
|
||||
else
|
||||
basic_robot.data[name].cor = nil
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
@ -702,16 +780,26 @@ local function runSandbox( name)
|
||||
return "Bytecode missing."
|
||||
end
|
||||
|
||||
data.ccounter = 0;
|
||||
data.operations = basic_robot.maxoperations;
|
||||
data.t = os.clock()
|
||||
|
||||
setfenv( ScriptFunc, data.sandbox )
|
||||
|
||||
local cor = data.cor;
|
||||
if cor then -- coroutine!
|
||||
local err,ret
|
||||
ret,err = coroutine.resume(cor)
|
||||
data.t = os.clock()-data.t
|
||||
if err then return err end
|
||||
return nil
|
||||
end
|
||||
|
||||
local Result, RuntimeError = pcall( ScriptFunc )
|
||||
data.t = os.clock()-data.t
|
||||
if RuntimeError then
|
||||
return RuntimeError
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@ -720,7 +808,7 @@ end
|
||||
local function setupid(owner)
|
||||
local privs = minetest.get_player_privs(owner); if not privs then return end
|
||||
local maxid = basic_robot.entry_count;
|
||||
if privs.robot then maxid = basic_robot.advanced_count end -- max id's per user
|
||||
if privs.robot or privs.puzzle then maxid = basic_robot.advanced_count end -- max id's per user
|
||||
basic_robot.ids[owner] = {id = 1, maxid = maxid}; --active id for remove control
|
||||
end
|
||||
|
||||
@ -836,7 +924,7 @@ minetest.register_entity("basic_robot:robot",{
|
||||
--textures={"character.png"},
|
||||
|
||||
visual="cube",
|
||||
textures={"arrow.png","basic_machine_side.png","face.png","basic_machine_side.png","basic_machine_side.png","basic_machine_side.png"},
|
||||
textures={"topface.png","legs.png","left-hand.png","right-hand.png","face.png","face-back.png"},
|
||||
|
||||
visual_size={x=1,y=1},
|
||||
running = 0, -- does it run code or is it idle?
|
||||
@ -904,16 +992,13 @@ minetest.register_entity("basic_robot:robot",{
|
||||
end
|
||||
self.running = 0; -- stop execution
|
||||
|
||||
if string.find(err,"stack overflow") then -- remove stupid player privs and spawner, ban player ip
|
||||
if string.find(err,"stack overflow") then
|
||||
local name = self.name;
|
||||
local pos = basic_robot.data[name].spawnpos;
|
||||
minetest.set_node(pos, {name = "air"});
|
||||
|
||||
local privs = core.get_player_privs(self.owner);privs.interact = false;
|
||||
|
||||
core.set_player_privs(self.owner, privs); minetest.auth_reload()
|
||||
minetest.ban_player(self.owner)
|
||||
|
||||
--local privs = core.get_player_privs(self.owner);privs.interact = false;
|
||||
--core.set_player_privs(self.owner, privs); minetest.auth_reload()
|
||||
minetest.kick_player(self.owner, "#basic_robot: stack overflow")
|
||||
end
|
||||
|
||||
local name = self.name;
|
||||
@ -994,7 +1079,7 @@ local spawn_robot = function(pos,node,ttl)
|
||||
|
||||
local sec_hash = minetest.get_password_hash("",data.authlevel.. owner .. basic_robot.password)
|
||||
if meta:get_string("sec_hash")~= sec_hash then
|
||||
minetest.chat_send_all("#ROBOT: " .. name .. " is using fake auth level. dig and place again.")
|
||||
minetest.chat_send_player(owner,"#ROBOT: " .. name .. " is using fake auth level. dig and place again.")
|
||||
return
|
||||
end
|
||||
|
||||
@ -1021,7 +1106,7 @@ local spawn_robot = function(pos,node,ttl)
|
||||
|
||||
if data.authlevel<3 then -- not admin
|
||||
err = check_code(script);
|
||||
script = preprocess_code(script);
|
||||
script = preprocess_code(script, basic_robot.call_limit[data.authlevel+1]);
|
||||
end
|
||||
if err then
|
||||
meta:set_string("infotext","#CODE CHECK ERROR : " .. err);
|
||||
@ -1040,7 +1125,7 @@ local spawn_robot = function(pos,node,ttl)
|
||||
if not data.sandbox then data.sandbox = getSandboxEnv (name) end
|
||||
|
||||
-- actual code run process
|
||||
data.ccounter = 0;data.operations = basic_robot.maxoperations;
|
||||
data.operations = basic_robot.maxoperations;
|
||||
|
||||
setfenv(data.bytecode, data.sandbox )
|
||||
|
||||
@ -1074,7 +1159,7 @@ local spawn_robot = function(pos,node,ttl)
|
||||
|
||||
local sec_hash = minetest.get_password_hash("",luaent.authlevel.. owner .. basic_robot.password)
|
||||
if meta:get_string("sec_hash")~= sec_hash then
|
||||
minetest.chat_send_all("#ROBOT: " .. name .. " is using fake auth level. dig and place again.")
|
||||
minetest.chat_send_player(owner,"#ROBOT: " .. name .. " is using fake auth level. dig and place again.")
|
||||
obj:remove();
|
||||
return
|
||||
end
|
||||
@ -1083,7 +1168,7 @@ local spawn_robot = function(pos,node,ttl)
|
||||
if data == nil then
|
||||
basic_robot.data[name] = {};
|
||||
data = basic_robot.data[name];
|
||||
data.rom = {};
|
||||
--data.rom = {};
|
||||
end
|
||||
|
||||
data.owner = owner;
|
||||
@ -1105,6 +1190,16 @@ local spawn_robot = function(pos,node,ttl)
|
||||
self.running = 1
|
||||
end
|
||||
|
||||
|
||||
--admin robot that starts automatically after server start
|
||||
minetest.after(10, function()
|
||||
local admin_bot_pos = basic_robot.admin_bot_pos;
|
||||
minetest.forceload_block(admin_bot_pos,true) -- load map position
|
||||
spawn_robot(admin_bot_pos,nil,1)
|
||||
print("[BASIC_ROBOT] admin robot at " .. admin_bot_pos.x .. " " .. admin_bot_pos.y .. " " .. admin_bot_pos.z .. " started.")
|
||||
end)
|
||||
|
||||
|
||||
local despawn_robot = function(pos)
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
@ -1150,113 +1245,6 @@ local despawn_robot = function(pos)
|
||||
|
||||
end
|
||||
|
||||
-- GUI
|
||||
|
||||
-- robogui GUI START ==================================================
|
||||
robogui = {}; -- a simple table of entries: [guiName] = {getForm = ... , show = ... , response = ... , guidata = ...}
|
||||
robogui.register = function(def)
|
||||
robogui[def.guiName] = {getForm = def.getForm, show = def.show, response = def.response, guidata = def.guidata or {}}
|
||||
end
|
||||
minetest.register_on_player_receive_fields(
|
||||
function(player, formname, fields)
|
||||
local gui = robogui[formname];
|
||||
if gui then gui.response(player,formname,fields) end
|
||||
end
|
||||
)
|
||||
-- robogui GUI END ====================================================
|
||||
|
||||
|
||||
|
||||
--- DEMO of simple form registration, all in one place, clean and tidy
|
||||
-- adapted for use with basic_robot
|
||||
|
||||
|
||||
-- if not basic_gui then
|
||||
-- basic_gui = _G.basic_gui; minetest = _G.minetest;
|
||||
-- basic_gui.register({
|
||||
-- guiName = "mainWindow", -- formname
|
||||
|
||||
-- getForm = function(form_id, update) -- actual form design
|
||||
-- local gui = basic_gui["mainWindow"];
|
||||
-- local formdata = gui.guidata[form_id]
|
||||
|
||||
-- if not formdata then -- init
|
||||
-- gui.guidata[form_id] = {}; formdata = gui.guidata[form_id]
|
||||
-- formdata.sel_tab = 1;
|
||||
-- formdata.text = "default";
|
||||
-- formdata.form = "";
|
||||
-- end
|
||||
-- if not update then return formdata.form end
|
||||
|
||||
-- local sel_tab = formdata.sel_tab;
|
||||
-- local text = formdata.text;
|
||||
|
||||
-- formdata.form = "size[8,9]"..
|
||||
-- "label[0,0;basic_gui_DEMO, form_id " .. form_id .. ", tab " .. sel_tab .. "]"..
|
||||
-- "button[0,1;2,1;gui_button;CLICK ME]"..
|
||||
-- "textarea[0.25,2;2,1;gui_textarea;text;" .. text .. "]"..
|
||||
-- "tabheader[0,0;tabs;tab1,table demo,tab3;".. sel_tab .. ";true;true]"..
|
||||
-- "list[current_player;main;0,5;8,4;]";
|
||||
|
||||
-- if sel_tab == 2 then
|
||||
-- formdata.form = "size[12,6.5;true]" ..
|
||||
-- "tablecolumns[color;tree;text,width=32;text]" ..
|
||||
-- "tableoptions[background=#00000000;border=false]" ..
|
||||
-- "field[0.3,0.1;10.2,1;search_string;;" .. minetest.formspec_escape(text) .. "]" ..
|
||||
-- "field_close_on_enter[search_string;false]" ..
|
||||
-- "button[10.2,-0.2;2,1;search;" .. "Search" .. "]" ..
|
||||
-- "table[0,0.8;12,4.5;list_settings;"..
|
||||
-- "#FFFF00,1,TEST A,,"..
|
||||
-- "#FFFF00,2,TEST A,,"..
|
||||
-- ",3,test a,value A,"..
|
||||
-- "#FFFF00,1,TEST B,,"..
|
||||
-- ",2,test b,value B,"
|
||||
-- end
|
||||
|
||||
-- formdata.info = "This information comes with the form post"; -- extra data set
|
||||
-- end,
|
||||
|
||||
-- show = function(player_name,update) -- this is used to show form to user
|
||||
-- local formname = "mainWindow";
|
||||
-- local form_id = player_name; -- each player has his own window!
|
||||
-- local gui = basic_gui[formname];
|
||||
-- local formdata = gui.guidata[form_id]; -- all form data for this id gets stored here
|
||||
-- if update then gui.getForm(form_id,true); formdata = gui.guidata[form_id]; end
|
||||
-- minetest.show_formspec(player_name, "mainWindow", formdata.form)
|
||||
-- end,
|
||||
|
||||
-- response = function(player,formname, fields) -- this handles response
|
||||
|
||||
-- local player_name = player:get_player_name();
|
||||
-- local form_id = player_name;
|
||||
-- local gui = basic_gui[formname];
|
||||
-- local formdata = gui.guidata[form_id]; --gui.guidata[form_id];
|
||||
-- if not formdata then say("err") return end --error!
|
||||
|
||||
-- if fields.gui_textarea then
|
||||
-- formdata.text = fields.gui_textarea or ""
|
||||
-- end
|
||||
|
||||
|
||||
-- if fields.tabs then
|
||||
-- formdata.sel_tab = tonumber(fields.tabs) or 1;
|
||||
|
||||
-- gui.show(player_name,true) -- update and show form
|
||||
-- else
|
||||
|
||||
-- local form = "size[5,5]" ..
|
||||
-- "label[0,0;you interacted with demo form, fields : " ..
|
||||
-- _G.minetest.formspec_escape(_G.dump(fields)) .. "]"..
|
||||
-- "label[0,4;" .. formdata.info .. "]"
|
||||
-- _G.minetest.show_formspec(player_name,"basic_response", form);
|
||||
-- end
|
||||
-- end,
|
||||
|
||||
|
||||
-- })
|
||||
-- end
|
||||
|
||||
|
||||
|
||||
--process forms from spawner
|
||||
local on_receive_robot_form = function(pos, formname, fields, sender)
|
||||
@ -1265,13 +1253,15 @@ local on_receive_robot_form = function(pos, formname, fields, sender)
|
||||
if minetest.is_protected(pos,name) then return end
|
||||
|
||||
if fields.OK then
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
if fields.code then
|
||||
local code = fields.code or "";
|
||||
if string.len(code) > 64000 then
|
||||
minetest.chat_send_all("#ROBOT: " .. name .. " is spamming with long text.") return
|
||||
end
|
||||
|
||||
if meta:get_int("admin") == 1 then
|
||||
if meta:get_int("authlevel") > 1 and name ~= meta:get_string("owner")then
|
||||
local privs = minetest.get_player_privs(name); -- only admin can edit admin robot code
|
||||
if not privs.privs then
|
||||
return
|
||||
@ -1317,103 +1307,7 @@ local on_receive_robot_form = function(pos, formname, fields, sender)
|
||||
end
|
||||
|
||||
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"..
|
||||
"\n arrays: myTable1 = {1,2,3}, myTable2 = {[\"entry1\"]=5, [\"entry2\"]=1}\n"..
|
||||
" 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"..
|
||||
" 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"..
|
||||
" read_node.direction() tells you names of nodes\n"..
|
||||
" insert.direction(item, inventory) inserts item from robot inventory to target inventory\n"..
|
||||
" check_inventory.direction(itemname, inventory,index) looks at node and returns false/true, direction can be self,\n"..
|
||||
" 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,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"..
|
||||
"**BOOKS/CODE\n title,text=book.read(i) returns title,contents of book at i-th position in library \n book.write(i,title,text) writes book at i-th position at spawner library\n"..
|
||||
" code.run(text) compiles and runs the code in sandbox\n"..
|
||||
" 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,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"..
|
||||
"**ROBOT\n"..
|
||||
" say(\"hello\") will speak\n"..
|
||||
" self.listen(0/1) (de)attaches chat listener to robot\n"..
|
||||
" speaker, msg = self.listen_msg() retrieves last chat message if robot listens\n"..
|
||||
" self.send_mail(target,mail) sends mail to target robot\n"..
|
||||
" sender,mail = self.read_mail() reads mail, if any\n" ..
|
||||
" self.pos() returns table {x=pos.x,y=pos.y,z=pos.z}\n"..
|
||||
" self.name() returns robot name\n"..
|
||||
" self.set_properties({textures=.., visual=..,visual_size=.., , ) sets visual appearance\n"..
|
||||
" set_animation(anim_start,anim_end,anim_speed,anim_stand_start) set mesh animation \n"..
|
||||
" self.spam(0/1) (dis)enable message repeat to all\n"..
|
||||
" self.remove() stops program and removes robot object\n"..
|
||||
" self.reset() resets robot position\n"..
|
||||
" self.spawnpos() returns position of spawner block\n"..
|
||||
" self.viewdir() returns vector of view for robot\n"..
|
||||
" self.fire(speed, pitch,gravity, texture, is_entity) fires a projectile from robot\n"..
|
||||
" 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, 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,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"..
|
||||
" generate_power(fuel, amount) = energy, attempt to generate power from fuel material,\n" ..
|
||||
" if amount>0 try generate amount of power using builtin generator - this requires\n" ..
|
||||
" 40 gold/mese/diamonblock upgrades for each 1 amount\n"..
|
||||
" smelt(input,amount) = progress/true. works as a furnace, if amount>0 try to\n" ..
|
||||
" use power to smelt - requires 10 upgrades for each 1 amount, energy cost is:\n"..
|
||||
" 1/40*(1+amount)\n"..
|
||||
" grind(input) - grinds input material, requires upgrades for harder material\n"..
|
||||
" compress(input) - requires upgrades - energy intensive process\n" ..
|
||||
" transfer_power(amount,target_robot_name)\n"..
|
||||
"**CRYPTOGRAPHY: namespace 'crypto'\n"..
|
||||
" 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"..
|
||||
"**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);
|
||||
|
||||
--local form = "size [8,7] textarea[0,0;8.5,8.5;help;HELP;".. text.."]"
|
||||
|
||||
--textlist[X,Y;W,H;name;listelem 1,listelem 2,...,listelem n]
|
||||
local list = "";
|
||||
for word in string.gmatch(text, "(.-)\r?\n+") do list = list .. word .. ", " end
|
||||
local form = "size [10,8] textlist[-0.25,-0.25;10.25,8.5;help;" .. list .. "]"
|
||||
minetest.show_formspec(sender:get_player_name(), "robot_help", form);
|
||||
|
||||
robogui["robot_help"].show(name)
|
||||
return
|
||||
end
|
||||
|
||||
@ -1505,8 +1399,10 @@ local on_receive_robot_form = function(pos, formname, fields, sender)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- handle form: when rightclicking robot entity, remote controller
|
||||
|
||||
|
||||
minetest.register_on_player_receive_fields(
|
||||
function(player, formname, fields)
|
||||
|
||||
@ -1537,6 +1433,9 @@ minetest.register_on_player_receive_fields(
|
||||
local name = string.sub(formname, string.len(robot_formname)+1); -- robot name
|
||||
if fields.OK and fields.code then
|
||||
local item = player:get_wielded_item(); --set_wielded_item(item)
|
||||
if string.len(fields.code) > 1000 then
|
||||
minetest.chat_send_player(player:get_player_name(),"#ROBOT: text too long") return
|
||||
end
|
||||
item:set_metadata(fields.code);
|
||||
player:set_wielded_item(item);
|
||||
if fields.id then
|
||||
@ -1678,6 +1577,10 @@ minetest.register_on_player_receive_fields(
|
||||
local data = itemstack:get_meta():to_table().fields -- 0.4.16, old minetest.deserialize(itemstack:get_metadata())
|
||||
if not data then data = {} end
|
||||
local text = fields.book or "";
|
||||
if string.len(text) > 64000 then
|
||||
local sender = player:get_player_name();
|
||||
minetest.chat_send_all("#ROBOT: " .. sender .. " is spamming with long text.") return
|
||||
end
|
||||
data.text = text or ""
|
||||
data.title = fields.title or ""
|
||||
data.text_len = #data.text
|
||||
@ -1715,13 +1618,15 @@ end
|
||||
-- handle chats
|
||||
minetest.register_on_chat_message(
|
||||
function(name, message)
|
||||
local hidden = false;
|
||||
if string.sub(message,1,1) == "\\" then hidden = true; message = string.sub(message,2) end
|
||||
local listeners = basic_robot.data.listening;
|
||||
for pname,_ in pairs(listeners) do
|
||||
local data = basic_robot.data[pname];
|
||||
data.listen_msg = message;
|
||||
data.listen_speaker = name;
|
||||
end
|
||||
return false
|
||||
return hidden
|
||||
end
|
||||
)
|
||||
|
||||
@ -1834,7 +1739,7 @@ end
|
||||
minetest.register_craftitem("basic_robot:control", {
|
||||
description = "Robot remote control",
|
||||
inventory_image = "control.png",
|
||||
groups = {book = 1, not_in_creative_inventory = 1},
|
||||
groups = {book = 1}, --not_in_creative_inventory = 1
|
||||
stack_max = 1,
|
||||
|
||||
on_secondary_use = function(itemstack, user, pointed_thing)
|
||||
@ -1951,7 +1856,7 @@ minetest.register_entity(
|
||||
|
||||
on_step = function(self, dtime)
|
||||
local vel = self.object:getvelocity();
|
||||
if (self.oldvel.x~=0 and vel.x==0) or (self.oldvel.y~=0 and vel.y==0) or (self.oldvel.z~=0 and vel.z==0) then
|
||||
if (self.oldvel.x~=0 and vel.x==0) or (self.oldvel.y~=0 and vel.y==0) or (self.oldvel.z~=0 and vel.z==0) then -- hit
|
||||
local data = basic_robot.data[self.name];
|
||||
if data then
|
||||
data.fire_pos = self.object:getpos();
|
||||
@ -1997,4 +1902,6 @@ minetest.register_craft({
|
||||
|
||||
|
||||
minetest.register_privilege("robot", "increased number of allowed active robots")
|
||||
minetest.register_privilege("puzzle", "allow player to use puzzle. namespace in robots")
|
||||
minetest.register_privilege("puzzle", "allow player to use puzzle. namespace in robots")
|
||||
|
||||
print('[MOD]'.. " basic_robot " .. basic_robot.version .. " loaded.")
|
350
robogui.lua
Normal file
@ -0,0 +1,350 @@
|
||||
local robogui = basic_robot.gui;
|
||||
|
||||
-- GUI
|
||||
|
||||
-- robogui GUI START ==================================================
|
||||
-- a simple table of entries: [guiName] = {getForm = ... , show = ... , response = ... , guidata = ...}
|
||||
robogui.register = function(def)
|
||||
robogui[def.guiName] = {getForm = def.getForm, show = def.show, response = def.response, guidata = def.guidata or {}}
|
||||
end
|
||||
minetest.register_on_player_receive_fields(
|
||||
function(player, formname, fields)
|
||||
local gui = robogui[formname];
|
||||
if gui then gui.response(player,formname,fields) end
|
||||
end
|
||||
)
|
||||
-- robogui GUI END ====================================================
|
||||
|
||||
|
||||
--- DEMO of simple form registration, all in one place, clean and tidy
|
||||
-- adapted for use with basic_robot
|
||||
|
||||
|
||||
-- if not basic_gui then
|
||||
-- basic_gui = _G.basic_gui; minetest = _G.minetest;
|
||||
-- basic_gui.register({
|
||||
-- guiName = "mainWindow", -- formname
|
||||
|
||||
-- getForm = function(form_id, update) -- actual form design
|
||||
-- local gui = basic_gui["mainWindow"];
|
||||
-- local formdata = gui.guidata[form_id]
|
||||
|
||||
-- if not formdata then -- init
|
||||
-- gui.guidata[form_id] = {}; formdata = gui.guidata[form_id]
|
||||
-- formdata.sel_tab = 1;
|
||||
-- formdata.text = "default";
|
||||
-- formdata.form = "";
|
||||
-- end
|
||||
-- if not update then return formdata.form end
|
||||
|
||||
-- local sel_tab = formdata.sel_tab;
|
||||
-- local text = formdata.text;
|
||||
|
||||
-- formdata.form = "size[8,9]"..
|
||||
-- "label[0,0;basic_gui_DEMO, form_id " .. form_id .. ", tab " .. sel_tab .. "]"..
|
||||
-- "button[0,1;2,1;gui_button;CLICK ME]"..
|
||||
-- "textarea[0.25,2;2,1;gui_textarea;text;" .. text .. "]"..
|
||||
-- "tabheader[0,0;tabs;tab1,table demo,tab3;".. sel_tab .. ";true;true]"..
|
||||
-- "list[current_player;main;0,5;8,4;]";
|
||||
|
||||
-- if sel_tab == 2 then
|
||||
-- formdata.form = "size[12,6.5;true]" ..
|
||||
-- "tablecolumns[color;tree;text,width=32;text]" ..
|
||||
-- "tableoptions[background=#00000000;border=false]" ..
|
||||
-- "field[0.3,0.1;10.2,1;search_string;;" .. minetest.formspec_escape(text) .. "]" ..
|
||||
-- "field_close_on_enter[search_string;false]" ..
|
||||
-- "button[10.2,-0.2;2,1;search;" .. "Search" .. "]" ..
|
||||
-- "table[0,0.8;12,4.5;list_settings;"..
|
||||
-- "#FFFF00,1,TEST A,,"..
|
||||
-- "#FFFF00,2,TEST A,,"..
|
||||
-- ",3,test a,value A,"..
|
||||
-- "#FFFF00,1,TEST B,,"..
|
||||
-- ",2,test b,value B,"
|
||||
-- end
|
||||
|
||||
-- formdata.info = "This information comes with the form post"; -- extra data set
|
||||
-- end,
|
||||
|
||||
-- show = function(player_name,update) -- this is used to show form to user
|
||||
-- local formname = "mainWindow";
|
||||
-- local form_id = player_name; -- each player has his own window!
|
||||
-- local gui = basic_gui[formname];
|
||||
-- local formdata = gui.guidata[form_id]; -- all form data for this id gets stored here
|
||||
-- if update then gui.getForm(form_id,true); formdata = gui.guidata[form_id]; end
|
||||
-- minetest.show_formspec(player_name, "mainWindow", formdata.form)
|
||||
-- end,
|
||||
|
||||
-- response = function(player,formname, fields) -- this handles response
|
||||
|
||||
-- local player_name = player:get_player_name();
|
||||
-- local form_id = player_name;
|
||||
-- local gui = basic_gui[formname];
|
||||
-- local formdata = gui.guidata[form_id]; --gui.guidata[form_id];
|
||||
-- if not formdata then say("err") return end --error!
|
||||
|
||||
-- if fields.gui_textarea then
|
||||
-- formdata.text = fields.gui_textarea or ""
|
||||
-- end
|
||||
|
||||
|
||||
-- if fields.tabs then
|
||||
-- formdata.sel_tab = tonumber(fields.tabs) or 1;
|
||||
|
||||
-- gui.show(player_name,true) -- update and show form
|
||||
-- else
|
||||
|
||||
-- local form = "size[5,5]" ..
|
||||
-- "label[0,0;you interacted with demo form, fields : " ..
|
||||
-- _G.minetest.formspec_escape(_G.dump(fields)) .. "]"..
|
||||
-- "label[0,4;" .. formdata.info .. "]"
|
||||
-- _G.minetest.show_formspec(player_name,"basic_response", form);
|
||||
-- end
|
||||
-- end,
|
||||
|
||||
|
||||
-- })
|
||||
-- end
|
||||
|
||||
local help_address = {}; -- array containing current page name for player
|
||||
local help_pages = {
|
||||
["main"] = {
|
||||
" === ROBOT HELP - MAIN SCREEN === ","",
|
||||
"[Commands reference] display list of robot commands",
|
||||
"[Lua basics] short introduction to lua","",
|
||||
"INSTRUCTIONS: double click links marked with []",
|
||||
"------------------------------------------","",
|
||||
"basic_robot version " .. basic_robot.version,
|
||||
"(c) 2016 rnd",
|
||||
},
|
||||
|
||||
["Lua basics"] = {
|
||||
"back to [main] menu",
|
||||
"BASIC LUA SYNTAX","",
|
||||
" IF CONDITIONAL: if x==1 then A else B end",
|
||||
" FOR LOOP: for i = 1, 5 do something end",
|
||||
" WHILE LOOP: while i<6 do A; i=i+1; end",
|
||||
' ARRAYS: myTable1 = {1,2,3}, myTable2 = {["entry1"]=5, ["entry2"]=1}',
|
||||
' access table entries with myTable1[1] or myTable2.entry1 or',
|
||||
' myTable2["entry1"]',
|
||||
" FUNCTIONS: f = function(x) return 2*x end, call like f(3)",
|
||||
" STRINGS: name = \"rnd\" or name = [[rnd ]] (multiline string)",
|
||||
" string.concat({string1,string2,...}, separator) returns",
|
||||
" concatenated string with maxlength is 1024",
|
||||
},
|
||||
|
||||
["Commands reference"] = {
|
||||
"back to [main] menu",
|
||||
|
||||
"ROBOT COMMANDS","",
|
||||
" 1. [MOVEMENT DIGGING PLACING NODE SENSING]",
|
||||
" 2. [TAKE INSERT AND INVENTORY]",
|
||||
" 3. [BOOKS CODE TEXT WRITE OR READ]",
|
||||
" 4. [PLAYERS]",
|
||||
" 5. [ROBOT SPEAK LABEL APPEARANCE OTHER]",
|
||||
" 6. [KEYBOARD AND USER INTERACTIONS]",
|
||||
" 7. [TECHNIC FUNCTIONALITY]",
|
||||
" 8. [CRYPTOGRAPHY]",
|
||||
" 9. [PUZZLE]",
|
||||
},
|
||||
|
||||
["MOVEMENT DIGGING PLACING NODE SENSING"] = {
|
||||
"back to [Commands reference]",
|
||||
"MOVEMENT,DIGGING, PLACING, NODE SENSING","",
|
||||
" move.direction(), where direction is: left, right, forward, backward,",
|
||||
" up, down, left_down, right_down, forward_down, backward_down,",
|
||||
" left_up, right_up, forward_up, backward_up",
|
||||
" boost(v) sets robot velocity, -6<v<6, if v = 0 then stop",
|
||||
" turn.left(), turn.right(), turn.angle(45)",
|
||||
" dig.direction()",
|
||||
" place.direction(\"default:dirt\", optional orientation param)",
|
||||
" read_node.direction() tells you names of nodes",
|
||||
},
|
||||
|
||||
["TAKE INSERT AND INVENTORY"] = {
|
||||
"back to [Commands reference]",
|
||||
"TAKE INSERT AND INVENTORY","",
|
||||
" insert.direction(item, inventory) inserts item from robot inventory to",
|
||||
" target inventory",
|
||||
" check_inventory.direction(itemname, inventory,index) looks at node and ",
|
||||
" returns false/true, direction can be self, if index>0 it returns itemname.",
|
||||
" if itemname == \"\" it checks if inventory empty",
|
||||
" activate.direction(mode) activates target block",
|
||||
" pickup(r) picks up all items around robot in radius r<8 and returns list",
|
||||
" or nil",
|
||||
" craft(item,idx,mode) crafts item if required materials are present in",
|
||||
" inventory, mode = 1 returns recipe, optional recipe idx",
|
||||
" take.direction(item, inventory) takes item from target inventory into",
|
||||
" robot inventory",
|
||||
},
|
||||
|
||||
["BOOKS CODE TEXT WRITE OR READ"] = {
|
||||
"back to [Commands reference]",
|
||||
"BOOKS CODE TEXT WRITE OR READ","",
|
||||
" title,text=book.read(i) returns title,contents of book at i-th position in",
|
||||
" library",
|
||||
" book.write(i,title,text) writes book at i-th position at spawner library",
|
||||
" code.run(text) compiles and runs the code in sandbox (privs only)",
|
||||
" code.set(text) replaces current bytecode of robot",
|
||||
" find_nodes(\"default:dirt\",3) returns distance to node in radius 3 around",
|
||||
" robot, or false if none found",
|
||||
" read_text.direction(stringname,mode) reads text of signs, chests and",
|
||||
" other blocks, optional stringname for other meta, mode 1 to read number",
|
||||
" write_text.direction(text,mode) writes text to target block as infotext",
|
||||
},
|
||||
|
||||
["PLAYERS"] = {
|
||||
"back to [Commands reference]",
|
||||
"PLAYERS","",
|
||||
" find_player(3,pos) finds players in radius 3 around robot(position) and",
|
||||
" returns list of found player names, if none returns nil",
|
||||
" attack(target) attempts to attack target player if nearby",
|
||||
" grab(target) attempt to grab target player if nearby and returns",
|
||||
" true if succesful",
|
||||
" player.getpos(name) return position of player, player.connected()",
|
||||
" returns list of connected players names",
|
||||
},
|
||||
|
||||
["ROBOT SPEAK LABEL APPEARANCE OTHER"] = {
|
||||
"back to [Commands reference]",
|
||||
"ROBOT","",
|
||||
" say(\"hello\") will speak",
|
||||
" self.listen(0/1) (de)attaches chat listener to robot",
|
||||
" speaker, msg = self.listen_msg() retrieves last chat message if robot",
|
||||
" has listener attached",
|
||||
" self.send_mail(target,mail) sends mail to target robot",
|
||||
" sender,mail = self.read_mail() reads mail, if any",
|
||||
" self.pos() returns table {x=pos.x,y=pos.y,z=pos.z}",
|
||||
" self.name() returns robot name",
|
||||
" self.operations() returns remaining robot operations",
|
||||
" self.set_properties({textures=.., visual=..,visual_size=.., , ) sets visual",
|
||||
" appearance",
|
||||
" self.set_animation(anim_start,anim_end,anim_speed,anim_stand_start)",
|
||||
" set mesh,animation",
|
||||
" self.spam(0/1) (dis)enable message repeat to all",
|
||||
" self.remove() stops program and removes robot object",
|
||||
" self.reset() resets robot position",
|
||||
" self.spawnpos() returns position of spawner block",
|
||||
" self.viewdir() returns vector of view for robot",
|
||||
" self.fire(speed, pitch,gravity, texture, is_entity) fires a projectile",
|
||||
" from robot. if is_entity false (default) it fires particle.",
|
||||
" self.fire_pos() returns last hit position",
|
||||
" self.label(text) changes robot label",
|
||||
" self.display_text(text,linesize,size) displays text instead of robot face,",
|
||||
" if no size just return texture string",
|
||||
" self.sound(sample,volume, opt. pos) plays sound named 'sample' at",
|
||||
" robot, location (optional pos)",
|
||||
" rom is aditional table that can store persistent data, like rom.x=1",
|
||||
},
|
||||
|
||||
["KEYBOARD AND USER INTERACTIONS"] = {
|
||||
"back to [Commands reference]",
|
||||
"KEYBOARD","",
|
||||
" EVENTS : place spawner at coordinates (r*i,2*r*j+1,r*k) to monitor",
|
||||
" events. value of r is ".. basic_robot.radius,
|
||||
" keyboard.get() returns table {x=..,y=..,z=..,puncher = .. , type = .. }",
|
||||
" for keyboard event",
|
||||
" keyboard.set(pos,type) set key at pos of type 0=air,1-6,7-15,16-271,",
|
||||
" limited to range 10 around spawner",
|
||||
" keyboard.read(pos) return node name at pos",
|
||||
},
|
||||
|
||||
["TECHNIC FUNCTIONALITY"] = {
|
||||
"back to [Commands reference]",
|
||||
"TECHNIC FUNCTIONALITY","",
|
||||
" All commands are in namespace 'machine', for example machine.energy()",
|
||||
" most functions return: ok, error = true or nil, error",
|
||||
" To use some commands fully robot must be upgraded. 1 upgrade is",
|
||||
" goldblock+meseblock+diamonblock.",
|
||||
" energy() displays available energy",
|
||||
" generate_power(fuel, amount) = energy, attempt to generate power",
|
||||
" from fuel material. If amount>0 try generate amount of power",
|
||||
" using builtin generator - this requires 40 upgrades for each",
|
||||
" 1 amount",
|
||||
" smelt(input,amount) = progress/true. works as a furnace, if amount>0",
|
||||
" try to use power to smelt - requires 10 upgrades for each 1 amount,",
|
||||
" energy cost of smelt is: 1/40*(1+amount)",
|
||||
" grind(input) - grinds input material, requires upgrades for harder",
|
||||
" materials",
|
||||
" compress(input) - requires upgrades - energy intensive process",
|
||||
" transfer_power(amount,target_robot_name)",
|
||||
},
|
||||
|
||||
["CRYPTOGRAPHY"] = {
|
||||
"back to [Commands reference]",
|
||||
"CRYPTOGRAPHY","",
|
||||
" namespace 'crypto'",
|
||||
" encrypt(input,password) returns encrypted text, password is any string",
|
||||
" decrypt(input,password) attempts to decrypt encrypted text",
|
||||
" scramble(input,randomseed,sgn) (de)permutes text randomly according",
|
||||
" to sgn = -1,1",
|
||||
" basic_hash(input,n) returns simple mod hash from string input within",
|
||||
" range 0...n-1",
|
||||
},
|
||||
|
||||
["PUZZLE"] = {
|
||||
"back to [Commands reference]",
|
||||
"PUZZLE","",
|
||||
" namespace 'puzzle' - need puzzle priv",
|
||||
" set_triggers({trigger1, trigger2,...}) sets and initializes spatial triggers",
|
||||
" check_triggers(pname) check if player is close to any trigger and run",
|
||||
" that trigger",
|
||||
" set_node(pos,node) - set any node, limited to current protector",
|
||||
" region",
|
||||
" get_player(pname) return player objRef in current protector region",
|
||||
" chat_send_player(pname, text)",
|
||||
" get_node_inv(pos) / get_player_inv(pname) - return inventories of nodes",
|
||||
" /players in current mapblock",
|
||||
" get_meta(pos) - return meta of target position",
|
||||
" get_gametime() - return current gametime",
|
||||
" ItemStack(itemname) returns ItemRef to be used with inventory",
|
||||
" count_objects(pos,radius)",
|
||||
" pdata contains puzzle data like .triggers and .gamedata",
|
||||
" add_particle(def)"
|
||||
}
|
||||
|
||||
}
|
||||
for k,v in pairs(help_pages) do
|
||||
local pages = help_pages[k]; for i = 1,#pages do pages[i] = minetest.formspec_escape(pages[i]) end
|
||||
end
|
||||
|
||||
|
||||
local robot_show_help = function(pname) --formname: robot_help
|
||||
local address = help_address[pname] or "main";
|
||||
|
||||
--minetest.chat_send_all("D DISPLAY HELP for ".. address )
|
||||
local pages = help_pages[address];
|
||||
|
||||
local content = table.concat(pages,",")
|
||||
local size = 9; local vsize = 8.75;
|
||||
|
||||
local form = "size[" .. size .. "," .. size .. "] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]";
|
||||
--minetest.chat_send_all("D " .. form)
|
||||
minetest.show_formspec(pname, "robot_help", form)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
robogui["robot_help"] = {
|
||||
response = function(player,formname,fields)
|
||||
local name = player:get_player_name()
|
||||
|
||||
local fsel = fields.wiki;
|
||||
if fsel and string.sub(fsel,1,3) == "DCL" then
|
||||
local sel = tonumber(string.sub(fsel,5)) or 1; -- selected line
|
||||
local address = help_address[name] or "main";
|
||||
local pages = help_pages[address];
|
||||
|
||||
local link = string.match(pages[sel] or "", "\\%[([%w%s]+)\\%]")
|
||||
if help_pages[link] then
|
||||
help_address[name] = link;
|
||||
robot_show_help(name)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
getForm = function(player_name)
|
||||
|
||||
end,
|
||||
|
||||
show = robot_show_help,
|
||||
};
|
242
scripts/command_robot.lua
Normal file
@ -0,0 +1,242 @@
|
||||
--COMMAND ROBOT by rnd, v2, adapted for skyblock
|
||||
if not s then
|
||||
self.listen(1)
|
||||
s=1;_G.minetest.forceload_block(self.pos(),true)
|
||||
self.spam(1)
|
||||
users = {["rnd"]=3,["rnd1"]=3,["Giorge"]=1,["quater"]=1,["UltimateNoob"]=1,["reandh3"]=1,["karo"]=1,["Fedon"]=1,["DS"]=2,
|
||||
["Arcelmi"]=1,["Gregorro"]=1,Mokk = 1, Evandro010 = 1}
|
||||
cmdlvl = {["kill"]=2,["tp"]=3,["heal"]=1, ["rename"]=1,["jump"]=1,["givediamond"]=3, ["msg"]=1,["calc"]=0, ["acalc"]=3,["run"]=3, ["shutdown"] = 3,["sayhi"]=0, ["day"] = 1};
|
||||
|
||||
tpr = {};
|
||||
|
||||
cmds = {
|
||||
help =
|
||||
{
|
||||
level = 0,
|
||||
run = function(param)
|
||||
local arg = param[2];
|
||||
if not arg then
|
||||
say(colorize("red","OPEN INVENTORY AND READ 'Quests'. YOU GET REWARD AND BETTER STUFF FOR COMPLETING QUESTS. Say /spawn to get to spawn area."))
|
||||
return
|
||||
end
|
||||
|
||||
if arg and cmds[arg] then
|
||||
local text = cmds[arg]["docs"];
|
||||
if text then say(text) end
|
||||
else
|
||||
--say(colorize("red","commands") .. colorize("LawnGreen",": 0 status, 2 kill $name, 3 tp $name1 $name2, 1 heal $name, 1 day, 1 rename $name $newname, 3 givediamond $name, 1 msg $name $message, 0 sayhi, 0 calc $formula, 3 acalc $formula, 3 run $expr"))
|
||||
local msg = ""
|
||||
for k,v in pairs(cmds) do
|
||||
msg = msg .. v.level .. " " .. k .. ", "
|
||||
end
|
||||
say(colorize("red","commands") .. " " .. colorize("LawnGreen", msg))
|
||||
end
|
||||
end,
|
||||
docs = "Displays available commands. 'help command' shows help about particular command"
|
||||
},
|
||||
|
||||
status =
|
||||
{
|
||||
level = 0,
|
||||
run = function(param)
|
||||
local usr = param[2] or speaker;
|
||||
local id = _G.skyblock.players[usr].id;
|
||||
local pos = _G.skyblock.get_island_pos(id)
|
||||
minetest.chat_send_all(minetest.colorize("yellow",
|
||||
usr .. " data : permission level " .. (users[usr] or 0).. ", island " .. id .." (at " .. pos.x .. " " .. pos.z .. "), skyblock level " .. _G.skyblock.players[usr].level
|
||||
))
|
||||
end,
|
||||
docs = "status name, Show target(speaker if none) level, which determines access privilege."
|
||||
},
|
||||
|
||||
kill =
|
||||
{
|
||||
level = 2,
|
||||
run = function(param)
|
||||
local name = param[2]; if not name then return end
|
||||
local player = _G.minetest.get_player_by_name(name);
|
||||
if player then
|
||||
if (users[name] or 0)<=(users[speaker] or 0) then player:set_hp(0) end
|
||||
end
|
||||
end,
|
||||
docs = "kill name; kills target player",
|
||||
},
|
||||
|
||||
tp =
|
||||
{
|
||||
level = 2,
|
||||
run = function(param)
|
||||
local player1 = _G.minetest.get_player_by_name(param[2] or "");
|
||||
local player2 = _G.minetest.get_player_by_name(param[3] or "");
|
||||
if player1 and player2 then if (users[param[2]] or 0)<=(users[speaker] or 0) then player1:setpos(player2:getpos()) end end
|
||||
end,
|
||||
docs = "tp name1 name2; teleports player name2 to name2",
|
||||
},
|
||||
|
||||
tpr =
|
||||
{
|
||||
level = 0,
|
||||
run = function(param)
|
||||
local name = param[2] or "";
|
||||
local player1 = _G.minetest.get_player_by_name(name);
|
||||
if player1 then tpr = {speaker, name} else return end
|
||||
_G.minetest.chat_send_player(name,minetest.colorize("yellow","#TELEPORT REQUEST: say tpy to teleport " .. speaker .. " to you."))
|
||||
|
||||
end,
|
||||
docs = "tpr name; request teleport to target player",
|
||||
},
|
||||
|
||||
tpy =
|
||||
{
|
||||
level = 0,
|
||||
run = function(param)
|
||||
if speaker == tpr[2] then
|
||||
local player1 = _G.minetest.get_player_by_name(tpr[1] or "");
|
||||
local player2 = _G.minetest.get_player_by_name(tpr[2] or "");
|
||||
if player1 and player2 then else return end
|
||||
player1:setpos(player2:getpos())
|
||||
_G.minetest.chat_send_player(tpr[2],minetest.colorize("yellow","#teleporting " .. tpr[1] .. " to you."))
|
||||
tpr = {}
|
||||
end
|
||||
|
||||
end,
|
||||
docs = "tpy; allow player who sent teleport request to teleport to you.",
|
||||
},
|
||||
|
||||
calc =
|
||||
{
|
||||
level = 0,
|
||||
run = function(param)
|
||||
|
||||
local formula = param[2] or "";
|
||||
if not string.find(formula,"%a") then
|
||||
result = 0;
|
||||
code.run("result = "..formula);
|
||||
result = tonumber(result)
|
||||
if result then say(result) else say("error in formula") end
|
||||
else
|
||||
say("dont use any letters in formula")
|
||||
end
|
||||
end,
|
||||
docs = "calculate expression",
|
||||
},
|
||||
|
||||
day =
|
||||
{
|
||||
level = 1,
|
||||
run = function() minetest.set_timeofday(0.25) end,
|
||||
docs = "set time to day"
|
||||
},
|
||||
|
||||
sayhi =
|
||||
{
|
||||
level = 0,
|
||||
run = function()
|
||||
local players = _G.minetest.get_connected_players();local msg = "";
|
||||
for _,player in pairs(players) do
|
||||
local name = player:get_player_name();
|
||||
local color = string.format("#%x",math.random(2^24)-1)
|
||||
if name~=speaker then msg = msg..colorize(color , " hi " .. name) .."," end
|
||||
end
|
||||
_G.minetest.chat_send_all("<"..speaker..">" .. string.sub(msg,1,-2))
|
||||
end,
|
||||
docs = "say hi to all the other players"
|
||||
},
|
||||
|
||||
msg = {
|
||||
level = 2,
|
||||
run = function(param)
|
||||
local text = string.sub(msg, string.len(param[1] or "")+string.len(param[2] or "") + 3)
|
||||
local form = "size [8,2] textarea[0.,0;8.75,3.75;book;MESSAGE from " .. speaker .. ";" .. _G.minetest.formspec_escape(text or "") .. "]"
|
||||
_G.minetest.show_formspec(param[2], "robot_msg", form);
|
||||
end,
|
||||
docs = "msg name message, displays message to target player",
|
||||
|
||||
},
|
||||
|
||||
plist = {
|
||||
level = 0,
|
||||
run = function()
|
||||
local p = {};
|
||||
for k,v in pairs(minetest.get_connected_players()) do
|
||||
local name = v:get_player_name()
|
||||
local pdata = _G.skyblock.players[name]
|
||||
p[#p+1] = name..", level " .. pdata.level .. "+" .. pdata.completed .. "/" .. pdata.total
|
||||
end
|
||||
local text = table.concat(p,"\n")
|
||||
local form = "size [8,5] textarea[0.,0;8.75,6.75;book;PLAYERS;" .. _G.minetest.formspec_escape(text or "") .. "]"
|
||||
_G.minetest.show_formspec(speaker, "robot_msg", form);
|
||||
end,
|
||||
docs = "plist, displays player list and their levels",
|
||||
|
||||
},
|
||||
|
||||
run = {
|
||||
level = 3,
|
||||
run = function(param)
|
||||
local expr = string.sub(msg,5);
|
||||
--say("running " .. expr)
|
||||
code.run(expr);
|
||||
end,
|
||||
docs = "run lua code",
|
||||
},
|
||||
}
|
||||
|
||||
self.label(colorize("red","\nCMD ROBOT"))
|
||||
end
|
||||
speaker, msg = self.listen_msg();
|
||||
|
||||
if msg then
|
||||
local words = {};
|
||||
for word in string.gmatch(msg,"%S+") do words[#words+1]=word end -- extract words
|
||||
|
||||
local level = users[speaker] or 0;
|
||||
local cmd = words[1];
|
||||
|
||||
cmdlevel = cmdlvl[cmd] or 0;
|
||||
if level < cmdlevel then
|
||||
say("You need to be level " .. cmdlevel .. " to use " .. words[1])
|
||||
else
|
||||
if cmds[cmd] then
|
||||
cmds[cmd].run(words)
|
||||
elseif words[1]=="heal" and words[2] then
|
||||
local player = _G.minetest.get_player_by_name(words[2]);
|
||||
if player then player:set_hp(20) end
|
||||
elseif words[1]=="rename" then
|
||||
local player = _G.minetest.get_player_by_name(words[2]);
|
||||
if player then if ((users[words[2]] or 0)<=level) and (level>=3 or words[2]~=speaker) then player:set_nametag_attributes({text = words[3] or words[2]}) end end
|
||||
elseif words[1]=="robot"then
|
||||
local player = _G.minetest.get_player_by_name(words[2])
|
||||
if player then
|
||||
player:set_properties({visual = "cube"});
|
||||
player:set_properties({textures={"arrow.png^[transformR90","basic_machine_side.png","basic_machine_side.png","basic_machine_side.png","face.png","basic_machine_side.png"}})
|
||||
player:set_properties({collisionbox={-0.5,-0.5,-0.5,0.5,0.5,0.5}})
|
||||
end
|
||||
elseif words[1] == "mese" then
|
||||
local player = _G.minetest.get_player_by_name(words[2])
|
||||
if player then
|
||||
player:set_properties({visual = "mesh",textures = {"zmobs_mese_monster.png"},mesh = "zmobs_mese_monster.x",visual_size = {x=1,y=1}})
|
||||
end
|
||||
|
||||
elseif words[1] == "givediamond" then
|
||||
local player = _G.minetest.get_player_by_name(words[2])
|
||||
local pos = player:getpos();
|
||||
_G.minetest.add_item(pos,_G.ItemStack("default:diamond"))
|
||||
elseif words[1] == "acalc" then
|
||||
local formula = words[2] or "";
|
||||
if not string.find(formula,"_G") then
|
||||
result = 0;
|
||||
code.run("result = "..formula);
|
||||
result = tonumber(result)
|
||||
if result then say(result) else say("error in formula") end
|
||||
end
|
||||
elseif words[1] == "shutdown" then
|
||||
_G.minetest.request_shutdown("maintenance, come back",true)
|
||||
elseif words[1] == "web" then
|
||||
local text = string.sub(msg,5);
|
||||
local f = _G.io.open("H:\\sfk\\files\\index.html", "w")
|
||||
f:write(text);f:close()
|
||||
|
||||
end
|
||||
end
|
||||
end
|
90
scripts/copy_paste.lua
Normal file
@ -0,0 +1,90 @@
|
||||
-- COPY PASTE ROBOT by rnd: c1 c2 r = markers, c = copy p = paste
|
||||
|
||||
if not paste then
|
||||
_G.minetest.forceload_block(self.pos(),true)
|
||||
paste = {};
|
||||
round = function(x)
|
||||
if x>0 then
|
||||
return math.floor(x+0.5)
|
||||
else
|
||||
return -math.floor(-x+0.5)
|
||||
end
|
||||
end
|
||||
data = {};
|
||||
|
||||
display_marker = function(pos,label)
|
||||
minetest.add_particle(
|
||||
{
|
||||
pos = pos,
|
||||
expirationtime = 10,
|
||||
velocity = {x=0, y=0,z=0},
|
||||
size = 18,
|
||||
texture = label..".png",
|
||||
acceleration = {x=0,y=0,z=0},
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
})
|
||||
|
||||
end
|
||||
|
||||
self.listen(1)
|
||||
self.label("COPY-PASTE MASTER v1.2 gold edition. commands: c1 c2 r c p")
|
||||
end
|
||||
|
||||
|
||||
speaker, msg = self.listen_msg()
|
||||
|
||||
if 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 p.y<0 then p.y = p.y +1 end -- needed cause of minetest bug
|
||||
if msg == "c1" then
|
||||
paste.src1 = {x=p.x,y=p.y,z=p.z};say("marker 1 set at " .. p.x .. " " .. p.y .. " " .. p.z)
|
||||
display_marker(p,"099") -- c
|
||||
elseif msg == "c2" then
|
||||
paste.src2 = {x=p.x,y=p.y,z=p.z};say("marker 2 set at " .. p.x .. " " .. p.y .. " " .. p.z)
|
||||
display_marker(p,"099") -- c
|
||||
elseif msg == "r" then
|
||||
paste.ref = {x=p.x,y=p.y,z=p.z};say("reference set at " .. p.x .. " " .. p.y .. " " .. p.z)
|
||||
display_marker(p,"114") -- r
|
||||
elseif msg == "c" then -- copy
|
||||
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
|
||||
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
|
||||
local count = 0; data = {};
|
||||
for i = x1,x2 do
|
||||
for j = y1,y2 do
|
||||
for k = z1,z2 do
|
||||
local node = _G.minetest.get_node({x=i,y=j,z=k});
|
||||
if node.name ~= "air" then
|
||||
if not data[i] then data[i]= {} end
|
||||
if not data[i][j] then data[i][j]= {} end
|
||||
data[i][j][k] = {node, _G.minetest.get_meta({x=i,y=j,z=k}):to_table()}
|
||||
count = count +1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
say(count .. " nodes copied ");
|
||||
elseif msg == "p" then -- paste
|
||||
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
|
||||
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
|
||||
local count = 0; p.x = p.x-paste.ref.x; p.y = p.y-paste.ref.y; p.z = p.z-paste.ref.z;
|
||||
for i = x1,x2 do
|
||||
for j = y1,y2 do
|
||||
for k = z1,z2 do
|
||||
local pdata;
|
||||
if data[i] and data[i][j] and data[i][j][k] then
|
||||
pdata = data[i][j][k]
|
||||
end
|
||||
if pdata then
|
||||
count = count + 1
|
||||
_G.minetest.set_node({x=i+p.x,y=j+p.y,z=k+p.z}, pdata[1]);
|
||||
_G.minetest.get_meta({x=i+p.x,y=j+p.y,z=k+p.z}):from_table(pdata[2])
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
say(count .. " nodes pasted ");
|
||||
end
|
||||
end
|
108
scripts/craft_guide.lua
Normal file
@ -0,0 +1,108 @@
|
||||
-- ROBOT craft guide by rnd, 2017
|
||||
if not list then
|
||||
|
||||
tname = "rnd";
|
||||
list = {};
|
||||
tmplist = _G.minetest.registered_items;
|
||||
for k,v in pairs(tmplist) do
|
||||
local texture = v.inventory_image or "";
|
||||
if texture=="" and v.tiles then texture = v.tiles[1] or "" end
|
||||
if (not v.groups.not_in_craft_guide or v.groups.not_in_craft_guide == 0) and type(texture)=="string" and texture~="" then
|
||||
list[#list+1] = {_G.minetest.formspec_escape(k),_G.minetest.formspec_escape(v.description),_G.minetest.formspec_escape(texture)}; -- v.inventory_image, k, v.description
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
idx = 1; n = 35; row = 6; size = 1.25;
|
||||
filter = "" item = "" recipeid = 1
|
||||
filterlist = {}; for i = 1,#list do filterlist[i] = i end
|
||||
|
||||
get_texture = function(ritem)
|
||||
local v = _G.minetest.registered_items[ritem]; if not v then return "" end
|
||||
local texture = v.inventory_image or "";
|
||||
if texture=="" and v.tiles then texture = v.tiles[1] or "" end
|
||||
if type(texture)~="string" then return "" end
|
||||
return texture
|
||||
end
|
||||
|
||||
get_form = function()
|
||||
local form = "size[7.5,8.5]";
|
||||
local x,y,i; local idxt = idx+n; if idxt > #filterlist then idxt = #filterlist end
|
||||
for i = idx, idxt do
|
||||
local id = filterlist[i];
|
||||
if list[id] and list[id][3] then
|
||||
x = ((i-idx) % row)
|
||||
y = (i-idx-x)/row;
|
||||
form = form .. "image_button[".. x*size ..",".. y*size+0.75 .. ";"..size.."," .. size .. ";" .. list[id][3] ..";".."item"..";".. list[id][1] .."]"
|
||||
end
|
||||
end
|
||||
form = form .. "textarea[0.25,0;2,0.75;filter;filter;"..filter .. "]" .. "button[2.,0;1,0.5;search;search]"..
|
||||
"button[5.5,0;1,0.5;prev;PREV]" .. "button[6.5,0;1,0.5;next;NEXT]" .. "label[4,0;".. idx .. "-"..idxt .. "/" .. #filterlist.."]";
|
||||
return form
|
||||
end
|
||||
|
||||
get_recipe = function()
|
||||
local form = "size[7.5,8.5]";
|
||||
local recipes = _G.minetest.get_all_craft_recipes(item); if not recipes then return end;
|
||||
local recipe = recipes[recipeid]; if not recipe then return end
|
||||
local items = recipe.items
|
||||
local x,y,i;
|
||||
for i = 0, 8 do
|
||||
local ritem = items[i+1] or ""; local sritem = "";
|
||||
local j = string.find(ritem,":"); if j then sritem = string.sub(ritem,j+1) end; --ritem = _G.minetest.formspec_escape(ritem);
|
||||
x = (i % 3)
|
||||
y = (i-x)/3;
|
||||
form = form .. "image_button[".. x*size ..",".. y*size+0.75 .. ";"..size.."," .. size .. ";" .. get_texture(ritem) ..";".."item"..";".. sritem .."]"
|
||||
end
|
||||
form = form .. "textarea[0.25,0;2,0.75;recipeid;recipeid ".. #recipes .. ";"..recipeid .. "]" .. "button[2.,0;1,0.5;go;go]"..
|
||||
"label[3,0;" .. item .. "]" .. "button[6.5,0;1,0.5;back;BACK]" ;
|
||||
return form
|
||||
end
|
||||
|
||||
s=0
|
||||
end
|
||||
|
||||
if s==0 then
|
||||
local p = find_player(4); s = 1
|
||||
if p then
|
||||
self.show_form(p[1],get_form())
|
||||
else
|
||||
self.remove()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
sender,fields = self.read_form()
|
||||
if sender then
|
||||
|
||||
if fields.search then
|
||||
filter = fields.filter or ""
|
||||
filterlist = {};
|
||||
for i = 1,#list do
|
||||
if string.find(list[i][1],filter) then filterlist[#filterlist+1] = i end
|
||||
end
|
||||
idx=1;self.show_form(sender,get_form())
|
||||
|
||||
elseif fields.prev then
|
||||
idx = idx - n; if idx<1 then idx =#filterlist-n end
|
||||
self.show_form(sender,get_form())
|
||||
elseif fields.next then
|
||||
idx = idx+n; if idx > #filterlist then idx = 1 end
|
||||
self.show_form(sender,get_form())
|
||||
elseif fields.back then
|
||||
self.show_form(sender,get_form())
|
||||
elseif fields.recipeid then
|
||||
recipeid = tonumber(fields.recipeid) or 1;
|
||||
self.show_form(sender,get_recipe())
|
||||
elseif fields.item then
|
||||
item = fields.item;
|
||||
local recipes = _G.minetest.get_all_craft_recipes(item);
|
||||
local count = 0; if recipes then count = #recipes end
|
||||
if count>0 then
|
||||
recipeid = 1
|
||||
self.show_form(sender,get_recipe() or "")
|
||||
end
|
||||
elseif fields.quit then
|
||||
self.remove()
|
||||
end
|
||||
end
|
15
scripts/farm_walker.lua
Normal file
@ -0,0 +1,15 @@
|
||||
if not init then
|
||||
init = true
|
||||
angle = 90
|
||||
walk = {["default:dirt"] = 1}
|
||||
stop = {["wool:white"] = 1}
|
||||
end
|
||||
|
||||
node = read_node.forward_down()
|
||||
if walk[node] then
|
||||
move.forward()
|
||||
elseif stop[node] then
|
||||
self.reset(); angle = 90
|
||||
else
|
||||
turn.angle(angle);move.forward(); turn.angle(angle); angle = - angle
|
||||
end
|
146
scripts/games/CTF_bot.lua
Normal file
@ -0,0 +1,146 @@
|
||||
-- simple ctf robot, rnd
|
||||
--instructions: build game arena and place blue/red buttons as flags. edit flag positions below
|
||||
--you must register 'keyboard' events by placing robot with same 'id' as robot running this code at 'event register' positions - default (32*i,64*j+1, 32*k)
|
||||
|
||||
if not ctf then
|
||||
_G.minetest.forceload_block(self.pos(),true)
|
||||
ctf = {
|
||||
[1] = {state = 1, flagnode = "basic_robot:button8080FF", pos = {x=-18,y=502,z=110}, name = "blue", owner = "", score = 0}, -- team[1]
|
||||
[2] = {state = 1, flagnode = "basic_robot:buttonFF8080", pos = {x=-18,y=502,z=80}, name = "red", owner = "", score = 0}, -- team[2]
|
||||
}
|
||||
|
||||
teams = {} -- example : {["rnd"] = {1,0, player, health points at start}}; -- team, ownership of flag
|
||||
maxscore = 3;
|
||||
t = 0
|
||||
teamid = 1; -- team selector when joining
|
||||
|
||||
gamestate = 0;
|
||||
self.listen(1)
|
||||
self.spam(1)
|
||||
|
||||
get_id = function(pos)
|
||||
local range = 1000;
|
||||
return pos.x + range*pos.y+range^2*pos.z
|
||||
end
|
||||
|
||||
flag_id = {}; for i = 1,#ctf do flag_id[get_id(ctf[i].pos)] = i end
|
||||
|
||||
render_flags = function()
|
||||
for i = 1,#ctf do minetest.set_node(ctf[i].pos, {name = ctf[i].flagnode}) end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if gamestate == 0 then -- welcome
|
||||
say(colorize("red","#CAPTURE THE FLAG GAME. say '\\join' to join game. to start game one of joined players says '\\start'"))
|
||||
gamestate = 1
|
||||
elseif gamestate == 1 then
|
||||
speaker,msg = self.listen_msg()
|
||||
if msg == "join" then
|
||||
local pl = minetest.get_player_by_name(speaker);
|
||||
teams[speaker] = {teamid, 0, pl,20};pl:set_hp(20)
|
||||
local msg1 = ""; local msg2 = ""
|
||||
for k,v in pairs(teams) do
|
||||
if v[1] == 1 then msg1 = msg1 .. k .. " " elseif v[1] == 2 then msg2 = msg2 .. k .. " " end
|
||||
end
|
||||
|
||||
say(colorize("yellow","#CTF : " .. speaker .. " joined team " .. ctf[teamid].name .. ". TEAM " .. ctf[1].name .. ": " .. msg1 .. ", TEAM " .. ctf[2].name .. ": " .. msg2))
|
||||
teamid = 3-teamid; -- 1,2
|
||||
elseif msg == "start" then -- game start
|
||||
if teams[speaker] then
|
||||
gamestate = 2
|
||||
keyboard.get() -- clear keyboard buffer
|
||||
say(colorize("red","#CTF GAME STARTED. GET ENEMY FLAG AND BRING IT BACK TO YOUR FLAG. DONT LET YOUR HEALTH GO BELOW 3 HEARTS OR YOU ARE OUT."))
|
||||
for k,_ in pairs(teams) do -- teleport players
|
||||
local data = teams[k];data[3]:setpos( ctf[data[1]].pos )
|
||||
end
|
||||
render_flags()
|
||||
end
|
||||
end
|
||||
|
||||
elseif gamestate == 2 then
|
||||
speaker,msg = self.listen_msg()
|
||||
if msg == "score" then
|
||||
local msg1 = ""; local msg2 = ""
|
||||
for k,v in pairs(teams) do
|
||||
if v[1] == 1 then msg1 = msg1 .. k .. " " elseif v[1] == 2 then msg2 = msg2 .. k .. " " end
|
||||
end
|
||||
say(colorize("yellow","SCORE " .. ctf[1].score .. "/" .. ctf[2].score) .."\n" .. colorize("yellow","TEAM " .. ctf[1].name .. ": " .. msg1 .. ", TEAM " .. ctf[2].name .. ": " .. msg2))
|
||||
end
|
||||
|
||||
-- check player health
|
||||
for k,v in pairs(teams) do
|
||||
local hp = teams[k][3]:get_hp();
|
||||
if not hp or hp<6 then -- teams[k][4]
|
||||
|
||||
local cflag = teams[k][2];
|
||||
if cflag>0 then -- drop flag
|
||||
ctf[cflag].state = 1
|
||||
ctf[cflag].owner = ""
|
||||
minetest.set_node(ctf[cflag].pos, {name = ctf[cflag].flagnode})
|
||||
say(colorize("red", "#CTF " .. k .. " dropped " .. ctf[cflag].name .. " flag!"))
|
||||
end
|
||||
if not hp then -- player left
|
||||
say(colorize("yellow", "#CTF " .. k .. " left the game!"))
|
||||
teams[k] = nil
|
||||
else -- reset player
|
||||
say(colorize("yellow", "#CTF " .. k .. " resetted!"))
|
||||
v[2] = 0 -- player has no flag
|
||||
v[3]:set_hp(20)
|
||||
v[3]:setpos( ctf[v[1]].pos )
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
event = keyboard.get()
|
||||
if event and teams[event.puncher] then
|
||||
--say(serialize(event))
|
||||
local punch_id = get_id({x=event.x,y=event.y,z=event.z});
|
||||
local flag = flag_id[punch_id];
|
||||
if flag then
|
||||
local state = ctf[flag].state
|
||||
local puncher = event.puncher;
|
||||
if state == 1 then -- flag is here, ready to be taken or capture of enemy flag
|
||||
if teams[puncher][1] ~= flag then -- take
|
||||
say(colorize("red","#CTF " .. puncher .. " has taken " .. ctf[flag].name .. " flag !"))
|
||||
ctf[flag].state = 2;
|
||||
ctf[flag].owner = puncher;
|
||||
teams[puncher][2] = flag;
|
||||
minetest.set_node(ctf[flag].pos, {name = "basic_robot:buttonFFFF80"})
|
||||
else -- capture?
|
||||
if teams[puncher][2] > 0 then
|
||||
local cflag = teams[puncher][2] -- puncher has this flag
|
||||
local data = ctf[cflag];
|
||||
|
||||
local team = teams[puncher][1];
|
||||
ctf[team].score = ctf[team].score + 1
|
||||
ctf[team].owner = ""
|
||||
ctf[cflag].state = 1; -- reset captured flag state
|
||||
minetest.set_node(ctf[cflag].pos, {name = ctf[cflag].flagnode})
|
||||
teams[puncher][2] = 0
|
||||
say(colorize("orange","#CTF " .. puncher .. " has captured " .. data.name .. " flag! Team " .. ctf[team].name .. " has score " .. ctf[team].score ))
|
||||
if ctf[team].score == maxscore then
|
||||
say(colorize("yellow","#CTF: TEAM " .. ctf[team].name .. " WINS! "))
|
||||
gamestate = 3;t=5; -- intermission, duration 5
|
||||
|
||||
--reset
|
||||
teams = {}
|
||||
for i=1,#ctf do ctf[i].state = 1 ctf[i].score = 0 ctf[i].owner = "" end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
--say(serialize(event))
|
||||
end
|
||||
elseif gamestate == 3 then -- intermission
|
||||
if t>0 then t=t-1 else gamestate = 0 end
|
||||
end
|
91
scripts/games/battle_bot_arena.lua
Normal file
@ -0,0 +1,91 @@
|
||||
if not s then
|
||||
-- init
|
||||
bots = {[4] = {}, [5] = {}}; -- [type] = {{1,1,10}, {3,2,10}}; -- {x,y,hp}
|
||||
arena = {}; --[x][z] = {type, idx}
|
||||
for i = -10,10 do arena[i] = {} for j=-10,10 do arena[i][j] = {0,0} end end
|
||||
centerpos = self.spawnpos(); centerpos.y = centerpos.y+2
|
||||
TYPE = 4; -- 4,5 defines which bots are on the move/attack
|
||||
DIR = 1
|
||||
s=0
|
||||
t=0
|
||||
-- load user progs
|
||||
_,script1 = book.read(1);_,script2 = book.read(2);
|
||||
prog1, _ = _G.loadstring( script1 ); prog2, _ = _G.loadstring( script2 );
|
||||
|
||||
spawn_bot = function (x,z,type)
|
||||
if arena[x] and arena[x][z] and arena[x][z][1] == 0 then
|
||||
keyboard.set({x=centerpos.x+x,y=centerpos.y,z=centerpos.z+z},type)
|
||||
table.insert(bots[type],{x,z,10})
|
||||
arena[x][z] = {type,#bots[type]}
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
move_bot = function (i,dx,dz)
|
||||
local bot = bots[TYPE][i];if not bot then return false end
|
||||
if math.abs(dx)>1 or math.abs(dz)>1 then return false end
|
||||
local x1=bot[1]+dx; local z1=bot[2]+dz;
|
||||
if math.abs(x1)>10 or math.abs(z1)>10 then return false end
|
||||
if arena[x1] and arena[x1][z1] and arena[x1][z1][1] == 0 then else return false end
|
||||
|
||||
keyboard.set({x=centerpos.x+bot[1],y=centerpos.y,z=centerpos.z+bot[2]},0);
|
||||
keyboard.set({x=centerpos.x+x1,y=centerpos.y,z=centerpos.z+z1},TYPE);
|
||||
arena[bot[1]][bot[2]] = {0,0}
|
||||
arena[x1][z1] = {TYPE,i}
|
||||
|
||||
bot[1]=x1;bot[2]=z1;
|
||||
end
|
||||
|
||||
attack_bot = function(i,dx,dz)
|
||||
local bot = bots[TYPE][i];if not bot then return false end
|
||||
if math.abs(dx)>1 or math.abs(dz)>1 then return false end
|
||||
local x1=bot[1]+dx; local z1=bot[2]+dz;
|
||||
if math.abs(x1)>10 or math.abs(z1)>10 then return false end
|
||||
if arena[x1] and arena[x1][z1] and arena[x1][z1][1] == 0 then return false end
|
||||
local type = arena[x1][z1][1]; local idx = arena[x1][z1][2];
|
||||
local tbot = bots[type][idx];
|
||||
if not tbot then return false end
|
||||
tbot[3]=tbot[3]-5;
|
||||
if tbot[3]<=0 then
|
||||
keyboard.set({x=centerpos.x+tbot[1],y=centerpos.y,z=centerpos.z+tbot[2]},0);
|
||||
table.remove(bots[type],idx);
|
||||
arena[x1][z1] = {0,0}
|
||||
end
|
||||
end
|
||||
|
||||
read_arena = function(x,z)
|
||||
local data = arena[x][z];
|
||||
if not data then return end
|
||||
return {data[1],data[2]};
|
||||
end
|
||||
|
||||
read_bots = function (type, idx)
|
||||
local data = bots[type][idx];
|
||||
if not data then return end
|
||||
return {data[1],data[2],data[3]}
|
||||
end
|
||||
end
|
||||
|
||||
if t%10 == 0 then
|
||||
spawn_bot(0,-10,4)
|
||||
spawn_bot(0,10,5)
|
||||
end
|
||||
t=t+1
|
||||
self.label(#bots[4] .. " " .. #bots[5])
|
||||
|
||||
-- PROGRAM RULES:
|
||||
-- not allowed to modify api code: TYPE, bots,t,s, spawn_bot, move_bot, attack_bot, read_arena, read_bots
|
||||
-- only allowed to move bot or attack, but not to dig/place
|
||||
|
||||
TYPE = 4+(t%2);
|
||||
DIR = - DIR
|
||||
|
||||
if TYPE == 5 then
|
||||
_G.setfenv(prog1, _G.basic_robot.data[self.name()].sandbox )
|
||||
_,err = pcall(prog1)
|
||||
else
|
||||
_G.setfenv(prog2, _G.basic_robot.data[self.name()].sandbox )
|
||||
_,err = pcall(prog2)
|
||||
end
|
||||
if err then say(err) self.remove() end
|
189
scripts/games/battle_minesweeper_game.lua
Normal file
@ -0,0 +1,189 @@
|
||||
if not data then
|
||||
m=10;n=10;
|
||||
players = {};
|
||||
paused = true
|
||||
|
||||
turn = 2;
|
||||
turntimeout = 5
|
||||
shorttimeout = 1
|
||||
shortround = false
|
||||
turnmessage = ""
|
||||
t = 0;
|
||||
SIGNUP = 0; GAME = 1; INTERMISSION = 2
|
||||
state = SIGNUP
|
||||
|
||||
t0 = _G.minetest.get_gametime();spawnpos = self.spawnpos() -- place mines
|
||||
data = {};
|
||||
|
||||
init_game = function()
|
||||
abilitypoints = {0,0}; -- points to use ability, step outside ring to use them in following round
|
||||
shortround = false
|
||||
data = {}; minescount = 32
|
||||
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
|
||||
if not data[1] then data[1] = {} end if not data[2] then data[2] = {} end -- create 2x2 safe area
|
||||
data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0;
|
||||
|
||||
minescount = 0;
|
||||
for i = 1,m do for j = 1,n do -- render game
|
||||
if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
|
||||
end
|
||||
end end
|
||||
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},4) -- safe start spot
|
||||
end
|
||||
|
||||
get_mine_count = function(i,j)
|
||||
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
|
||||
for k = -1,1 do for l = -1,1 do
|
||||
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
chk_mines = function()
|
||||
local count = minescount;
|
||||
for i=1,m do for j=1,n do
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})=="basic_robot:buttonFF8080" and data[i] and data[i][j]==1 then
|
||||
count=count-1
|
||||
end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
|
||||
near_chat = function(msg)
|
||||
|
||||
end
|
||||
|
||||
greeting = function()
|
||||
_G.minetest.chat_send_all(colorize("red",
|
||||
"#BATTLE MINESWEEPER : two player battle in minesweeper. say join to play.\nRules: "..
|
||||
"1. each player has 5 second turn to make a move 2. if you dont make move you lose\n"..
|
||||
"3. if you make move in other player turn you lose. 4. if you hit bomb or mark bomb falsely you lose\n"..
|
||||
"5. by playing fast you get ability charge. when you collect 10 you can step out of ring when your round ends and opponent will only have 2s to play his turn."
|
||||
))
|
||||
end
|
||||
|
||||
player_lost = function ()
|
||||
for i=1,#players do
|
||||
local player = _G.minetest.get_player_by_name(players[i]);
|
||||
if player then player:setpos({x=spawnpos.x,y=spawnpos.y+10,z=spawnpos.z}) end
|
||||
end
|
||||
_G.minetest.sound_play("electric_zap",{pos=spawnpos, max_hear_distance = 100})
|
||||
state = INTERMISSION; t = 0
|
||||
end
|
||||
|
||||
function change_turn()
|
||||
shortround = false -- reset ability if activated
|
||||
if turn == 1 then
|
||||
_G.minetest.sound_play("default_break_glass",{pos=spawnpos, max_hear_distance = 100})
|
||||
else
|
||||
_G.minetest.sound_play("default_cool_lava",{pos=spawnpos, max_hear_distance = 100})
|
||||
end
|
||||
|
||||
if paused == false then
|
||||
say(players[turn] .. " lost : didn't make a move fast enough ");
|
||||
player_lost()
|
||||
else
|
||||
local player = minetest.get_player_by_name(players[turn])
|
||||
local ppos = player:getpos()
|
||||
local x = ppos.x - spawnpos.x;local y = ppos.y - spawnpos.y;local z = ppos.z - spawnpos.z;
|
||||
local points = abilitypoints[turn]
|
||||
if x<1 or x>m or z<1 or z>n then -- outside area
|
||||
local points = abilitypoints[turn]
|
||||
if points>=10 then -- do we have enough points?
|
||||
shortround = true -- next round will be shorter, 2s
|
||||
_G.minetest.sound_play("grinder",{pos=spawnpos, max_hear_distance = 100})
|
||||
abilitypoints[turn] = abilitypoints[turn]-10
|
||||
end
|
||||
end
|
||||
|
||||
if turn == 1 then turn = 2 else turn = 1 end
|
||||
turnmessage = "turn " .. turn .. " " .. players[turn] .. ",charge " .. abilitypoints[turn] .. "\n"
|
||||
self.label(turnmessage)
|
||||
t=0
|
||||
paused = false
|
||||
end
|
||||
end
|
||||
|
||||
init_game()
|
||||
greeting()
|
||||
self.listen(1)
|
||||
end
|
||||
|
||||
if state == SIGNUP then
|
||||
speaker,msg = self.listen_msg()
|
||||
if speaker then
|
||||
if msg == "join" then
|
||||
players[#players+1] = speaker;
|
||||
local plist = ""; for i=1,#players do plist = plist .. players[i] .. ", " end
|
||||
_G.minetest.chat_send_all("BATTLE MINESWEEPER, current players : " .. plist)
|
||||
|
||||
if #players >= 2 then
|
||||
state = GAME
|
||||
change_turn();
|
||||
keyboard.get(); t=0;
|
||||
for i = 1, #players do
|
||||
local player = _G.minetest.get_player_by_name(players[i]);
|
||||
if player then player:setpos({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z}) end
|
||||
end
|
||||
_G.minetest.chat_send_all(colorize("red","BATTLE MINESWEEPER " .. m .. "x" ..n .. " with " .. minescount .. " mines.\n" .. players[turn] .. " its your move!"))
|
||||
init_game()
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
elseif state == GAME then
|
||||
|
||||
t = t + 1;
|
||||
if (t>turntimeout) or (shortround and t>shorttimeout) then -- change of turn
|
||||
change_turn()
|
||||
else
|
||||
self.label(turnmessage .. " " .. (turntimeout-t+1))
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event and event.type == 2 and not paused then
|
||||
if event.puncher == players[turn] then
|
||||
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
|
||||
local points = abilitypoints[turn]
|
||||
if x<1 or x>m or z<1 or z>n then -- outside area
|
||||
else
|
||||
local ppos = player.getpos(event.puncher)
|
||||
|
||||
points = points + math.max(turntimeout-t-2,0); if points>40 then points = 40 end
|
||||
abilitypoints[turn] = points
|
||||
|
||||
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 data[x] and data[x][z] == 1 then
|
||||
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)
|
||||
else
|
||||
keyboard.set({x=event.x,y=event.y,z=event.z},3)
|
||||
end
|
||||
else
|
||||
say(event.puncher .. " lost : marked a bomb where it was none! ");
|
||||
player_lost()
|
||||
end
|
||||
else
|
||||
if data[x] and data[x][z]==1 then
|
||||
_G.minetest.sound_play("tnt_boom",{pos=spawnpos, max_hear_distance = 100})
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3)
|
||||
say(event.puncher .. " lost : punched a bomb! ");
|
||||
player_lost()
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
paused = true
|
||||
else
|
||||
say(event.puncher .. " lost : played out of his/her turn"); player_lost()
|
||||
end
|
||||
end
|
||||
|
||||
elseif state == INTERMISSION then
|
||||
t=t+1; if t> 15 then state = SIGNUP;players = {}; paused = true; greeting() end
|
||||
end
|
208
scripts/games/blackbox_game.lua
Normal file
@ -0,0 +1,208 @@
|
||||
--black box by rnd, 03/18/2017
|
||||
--https://en.wikipedia.org/wiki/Black_Box_(game)
|
||||
|
||||
if not data then
|
||||
m=16;n=16;
|
||||
atoms = 32
|
||||
attempts = 1;turn = 0;
|
||||
spawnpos = self.spawnpos(); spawnpos.x = spawnpos.x-m/2; spawnpos.y = spawnpos.y+2; spawnpos.z = spawnpos.z-n/2
|
||||
|
||||
local players = find_player(5,spawnpos);
|
||||
if not player then self.remove() else pname = players[1] end
|
||||
|
||||
self.spam(1);t0 = _G.minetest.get_gametime();
|
||||
data = {};
|
||||
for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end
|
||||
|
||||
for i=1,atoms do -- put in atoms randomly
|
||||
data[math.random(m)][math.random(n)] = 1
|
||||
end
|
||||
|
||||
atoms = 0
|
||||
for i = 1,m do for j = 1,n do if data[i][j]==1 then atoms = atoms + 1 end end end
|
||||
|
||||
render_board = function(mode) -- mode 0 : render without solution, 1: render solution
|
||||
for i = 1,m do for j = 1,n do -- render game
|
||||
if mode == 0 or data[i][j] == 0 then
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
|
||||
end
|
||||
else
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},3)
|
||||
end
|
||||
end end
|
||||
end
|
||||
|
||||
get_dirl = function(dir)
|
||||
local dirl; -- direction left
|
||||
if dir[1] > 0.5 then dirl = {0,-1}
|
||||
elseif dir[1] < -0.5 then dirl = {0,1}
|
||||
elseif dir[2] > 0.5 then dirl = {-1,0}
|
||||
elseif dir[2] < -0.5 then dirl = {1,0}
|
||||
end
|
||||
return dirl
|
||||
end
|
||||
|
||||
read_pos = function(x,z)
|
||||
if x<1 or x>m or z<1 or z>n then return nil end
|
||||
return data[x][z]
|
||||
end
|
||||
|
||||
newdir = function(x,z,dir) -- where will ray go next
|
||||
local retdir = {dir[1],dir[2]};
|
||||
local xf = x+dir[1]; local zf = z+dir[2] -- forward
|
||||
local dirl = get_dirl(dir)
|
||||
|
||||
local nodef = read_pos(xf,zf)
|
||||
local nodel = read_pos(xf + dirl[1],zf + dirl[2])
|
||||
local noder = read_pos(xf - dirl[1],zf - dirl[2])
|
||||
if nodef == 1 then
|
||||
retdir = {0,0} -- ray hit something
|
||||
elseif nodel == 1 and noder ~= 1 then
|
||||
retdir = {-dirl[1],-dirl[2]}
|
||||
elseif nodel ~= 1 and noder == 1 then
|
||||
retdir = {dirl[1],dirl[2]}
|
||||
elseif nodel == 1 and noder == 1 then
|
||||
retdir = {-dir[1],-dir[2]}
|
||||
end
|
||||
return retdir
|
||||
end
|
||||
|
||||
shootray = function(x,z,dir)
|
||||
--say("ray starts " .. x .. " " .. z .. " dir " .. dir[1] .. " " .. dir[2])
|
||||
local xp = x; local zp = z;
|
||||
local dirp = {dir[1],dir[2]};
|
||||
local maxstep = m*n;
|
||||
|
||||
for i = 1,maxstep do
|
||||
dirp = newdir(xp,zp,dirp);
|
||||
if dirp[1]==0 and dirp[2]==0 then return -i end -- hit
|
||||
xp=xp+dirp[1];zp=zp+dirp[2];
|
||||
if xp<1 or xp>m or zp<1 or zp>n then return i,{xp,zp} end -- out
|
||||
end
|
||||
return 0 -- hit
|
||||
end
|
||||
|
||||
count = 0; -- how many letters were used up
|
||||
border_start_ray = function(x,z)
|
||||
local rdir
|
||||
if x==0 then rdir = {1,0}
|
||||
elseif x == m+1 then rdir = {-1,0}
|
||||
elseif z == 0 then rdir = {0,1}
|
||||
elseif z == n+1 then rdir = {0,-1}
|
||||
end
|
||||
if rdir then
|
||||
local result,out = shootray(x,z,rdir);
|
||||
if result >= 0 then
|
||||
|
||||
if out then
|
||||
if out[1]==x and out[2]==z then -- got back where it originated, reflection
|
||||
keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},1);
|
||||
else
|
||||
if result<=1 then
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},6); -- immediate bounce off
|
||||
else
|
||||
local nodename = "basic_robot:button_"..(65+count);
|
||||
_G.minetest.set_node(
|
||||
{x=spawnpos.x+out[1],y=spawnpos.y+1,z=spawnpos.z+out[2]},
|
||||
{name = nodename, param2 = 1})
|
||||
_G.minetest.set_node(
|
||||
{x=spawnpos.x+x,y=spawnpos.y+1,z=spawnpos.z+z},
|
||||
{name = nodename, param2 = 1})
|
||||
count = count + 1;
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4);
|
||||
keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},4);
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif result<0 then
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3); -- hit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- initial border loop and marking
|
||||
|
||||
--render blue border
|
||||
for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+0},5) keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+n+1},5) end
|
||||
for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y,z=spawnpos.z+j},5) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y,z=spawnpos.z+j},5) end
|
||||
|
||||
for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+0},0) keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+n+1},0) end
|
||||
for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y+1,z=spawnpos.z+j},0) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y+1,z=spawnpos.z+j},0) end
|
||||
|
||||
|
||||
z=0 -- bottom
|
||||
for x = 1,m do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
x=m+1 -- right
|
||||
for z = 1,n do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
z=n+1 -- top
|
||||
for x = m,1,-1 do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
x=0 -- left
|
||||
for z = n,1,-1 do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
check_solution = function()
|
||||
for i = 1,m do
|
||||
for j = 1,n do
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}) == "basic_robot:buttonFF8080" then -- red
|
||||
if data[i][j]~=1 then return false end
|
||||
else
|
||||
if data[i][j]~=0 then return false end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--render board
|
||||
render_board(0)
|
||||
keyboard.set({x=spawnpos.x,y=spawnpos.y,z=spawnpos.z-1},4)
|
||||
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z-1},5)
|
||||
self.label("BLACKBOX with " .. atoms .. " atoms")
|
||||
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event then
|
||||
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
|
||||
if x<1 or x>m or z<1 or z>n then
|
||||
if event.type == 4 then
|
||||
if check_solution() then
|
||||
say("#BLACKBOX : CONGRATULATIONS! " .. event.puncher .. " found all atoms after " .. attempts .. " tries."); self.remove()
|
||||
else
|
||||
say("#BLACKBOX : " .. event.puncher .. " failed to detect atoms after " .. attempts .. " attempts.")
|
||||
attempts = attempts+1
|
||||
end
|
||||
elseif event.type == 5 then
|
||||
say("#BLACKBOX : DISPLAYING SOLUTION",pname)
|
||||
render_board(1)
|
||||
self.remove()
|
||||
end
|
||||
else -- interior punch
|
||||
nodetype = 2;
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button808080" then
|
||||
nodetype = 3
|
||||
end
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},nodetype);
|
||||
end
|
||||
|
||||
end
|
||||
::END::
|
38
scripts/games/casino_bot.lua
Normal file
@ -0,0 +1,38 @@
|
||||
-- rnd 2017
|
||||
if not s then
|
||||
s=0
|
||||
player0 ="";
|
||||
reward = "default:gold_ingot 6"
|
||||
price = "default:gold_ingot";
|
||||
self.spam(1)
|
||||
end
|
||||
if s==0 then
|
||||
local player = find_player(5);
|
||||
if player then
|
||||
player=player[1]
|
||||
if player~=player0 then
|
||||
self.label("Hello " .. player .. ". Please insert one gold ingot in chest to play.\nYou need to roll 6 on dice to win 6 gold.")
|
||||
player0 = player
|
||||
end
|
||||
else
|
||||
self.label(colorize("red","Come and win 6 gold!"))
|
||||
end
|
||||
if check_inventory.forward(price) then
|
||||
take.forward("default:gold_ingot");
|
||||
self.label("Thank you for your gold. rolling the dice!")
|
||||
s=1
|
||||
end
|
||||
elseif s==1 then
|
||||
roll = math.random(6);
|
||||
if roll == 6 then
|
||||
self.label("#YOU WIN!")
|
||||
say("#WE HAVE A WINNER! get 6 gold in chest!")
|
||||
insert.forward(reward)
|
||||
s=2
|
||||
else
|
||||
self.label(":( you rolled " .. roll.. ". Put gold in to try again.")
|
||||
s=0
|
||||
end
|
||||
elseif s==2 then
|
||||
if not check_inventory.forward(reward) then s=0 self.label("Please insert one gold to continue playing") end
|
||||
end
|
60
scripts/games/connect4.lua
Normal file
@ -0,0 +1,60 @@
|
||||
-- CONNECT, coded in 20 minutes by rnd
|
||||
if not data then
|
||||
m=8;n=8;turn = 0; num = 4;
|
||||
self.spam(1);t0 = _G.minetest.get_gametime();
|
||||
spawnpos = self.spawnpos() -- place mines
|
||||
state = 0; -- 0 signup 1 game
|
||||
players = {};
|
||||
data = {};
|
||||
for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end
|
||||
for i = 1,m do for j = 1,n do -- render game
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
|
||||
end
|
||||
end end
|
||||
|
||||
get_count_in_dir = function(dir,x,y)
|
||||
local r = num; -- num=4? in a row
|
||||
local snode = data[x][y];local count = 1;
|
||||
for j = 1,2 do
|
||||
for i = 1,r-1 do
|
||||
local x1 = x + dir[1]*i;local y1 = y + dir[2]*i;
|
||||
if not data[x1] or not data[x1][y1] then break end; if data[x1][y1]~= snode then break end
|
||||
count = count +1
|
||||
end
|
||||
dir[1]=-dir[1];dir[2]=-dir[2];
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
get_count = function(x,y)
|
||||
local c1 = get_count_in_dir({0,1},x,y); local c2 = get_count_in_dir({1,0},x,y)
|
||||
local c3 = get_count_in_dir({1,1},x,y); local c4 = get_count_in_dir({1,-1},x,y)
|
||||
if c2>c1 then c1 = c2 end; if c3>c1 then c1 = c3 end; if c4>c1 then c1 = c4 end
|
||||
return c1
|
||||
end
|
||||
|
||||
self.label("CONNECT 4 : GREEN starts play. 2 players punch to join game.")
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event then
|
||||
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
|
||||
if x<1 or x>m or z<1 or z>n then
|
||||
elseif event.type == 2 then --if event.type == 2 then
|
||||
if state == 0 then
|
||||
if #players<2 then players[#players+1] = event.puncher
|
||||
else state = 1 end
|
||||
if #players==2 then state = 1 end
|
||||
end
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4+turn);
|
||||
data[x][z] = 4+turn;
|
||||
if get_count(x,z) == num then say("CONGRATULATIONS! " .. event.puncher .. " has "..num .. " in a row"); self.remove(); goto END end
|
||||
turn = 1-turn
|
||||
if state == 1 then
|
||||
local msg = ""; if turn == 0 then msg = "GREEN " else msg = "BLUE" end
|
||||
self.label(msg .. " : " .. players[turn+1])
|
||||
end
|
||||
end
|
||||
end
|
||||
::END::
|
95
scripts/games/fallout_hacking.lua
Normal file
@ -0,0 +1,95 @@
|
||||
if not init then
|
||||
init = true
|
||||
|
||||
generate_random_string = function(n,m)
|
||||
local ret = {};
|
||||
for i = 1,n do ret[i]=string.char(math.random(m)+96) end --24
|
||||
return table.concat(ret)
|
||||
end
|
||||
|
||||
get_similiarity = function(text1,text2)
|
||||
local n = string.len(text1);
|
||||
if string.len(text2)~=n then return 0 end
|
||||
local ret = 0;
|
||||
for i = 1,n do
|
||||
if string.sub(text1,i,i) == string.sub(text2,i,i) then ret = ret + 1 end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
get_form = function()
|
||||
|
||||
local n = #passlist;
|
||||
|
||||
local frm = "label[0,0;" .. intro_msg .. "] " .. "label[0,8.5;" .. msg .. "] "
|
||||
|
||||
for i = 1,10 do
|
||||
frm = frm .. "button[0,".. (i)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] "
|
||||
end
|
||||
|
||||
for i = 11,n do
|
||||
frm = frm .. "button[2,".. (i-11+1)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] "
|
||||
end
|
||||
|
||||
local form = "size[6," .. 9 .. "]" .. frm;
|
||||
return form
|
||||
end
|
||||
|
||||
_G.math.randomseed(os.time())
|
||||
intro_msg = minetest.colorize("lawngreen","FALLOUT PASSWORD HACKING GAME\nmatch is both position and character.")
|
||||
msg = "" --TEST\nTEST\nTEST";
|
||||
passlist = {}; passdict = {}
|
||||
|
||||
n = 20; -- how many options
|
||||
count = 0;
|
||||
while count< n do
|
||||
local pass = generate_random_string(5,5); -- password length, charset size
|
||||
if not passdict[pass] then passlist[#passlist+1] = pass; passdict[pass] = true; count = count + 1 end
|
||||
end
|
||||
correct = math.random(n)
|
||||
guesses = 0
|
||||
max_guesses = 4
|
||||
|
||||
rom.data = {};
|
||||
if not rom.data then rom.data = {} end
|
||||
self.spam(1)
|
||||
|
||||
local players = find_player(4);
|
||||
if not players then say("#fallout hacking game: no players") self.remove() end
|
||||
pname = players[1];
|
||||
say("#fallout hacking game, player " .. pname)
|
||||
|
||||
--if rom.data[pname] then say("password is locked out!") self.remove() end
|
||||
|
||||
self.show_form(pname,get_form())
|
||||
self.read_form()
|
||||
|
||||
end
|
||||
|
||||
sender,fields = self.read_form()
|
||||
if sender and sender == pname then -- form event
|
||||
local pl = _G.minetest.get_player_by_name(pname);
|
||||
if pl then
|
||||
local selected = 0
|
||||
for k,_ in pairs(fields) do if k~="quit" then selected = tonumber(k) break end end
|
||||
|
||||
if selected>0 then
|
||||
guesses = guesses + 1
|
||||
if selected == correct then
|
||||
say("password " .. passlist[correct] .. " is correct! " .. guesses .. " guesses.")
|
||||
self.show_form(pname, "size[1,1] label[0,0.5;" .. minetest.colorize("lawngreen", "ACCESS GRANTED") .. "]")
|
||||
self.remove()
|
||||
--correct: do something with player
|
||||
else
|
||||
msg = msg .. " " .. minetest.colorize("yellow",guesses .. ". " .. passlist[selected]) .. " (" .. get_similiarity(passlist[correct], passlist[selected]) .. " match)"
|
||||
self.show_form(pname, get_form())
|
||||
end
|
||||
if guesses>=max_guesses then
|
||||
msg = minetest.colorize("red","A C C E S S D E N I E D!")
|
||||
self.show_form(pname, get_form())
|
||||
say("too many false guesses. password locked out!") rom.data[pname] = 1; self.remove()
|
||||
end
|
||||
end
|
||||
if fields.quit then self.remove() end
|
||||
end
|
||||
end
|
97
scripts/games/hacking_game.lua
Normal file
@ -0,0 +1,97 @@
|
||||
-- 'hacking' game from Fallout, by rnd
|
||||
if not init then
|
||||
init = true
|
||||
|
||||
generate_random_string = function(n,m)
|
||||
local ret = {};
|
||||
for i = 1,n do ret[i]=string.char(math.random(m)+96) end --24
|
||||
return table.concat(ret)
|
||||
end
|
||||
|
||||
get_similiarity = function(text1,text2)
|
||||
local n = string.len(text1);
|
||||
if string.len(text2)~=n then return 0 end
|
||||
local ret = 0;
|
||||
for i = 1,n do
|
||||
if string.sub(text1,i,i) == string.sub(text2,i,i) then ret = ret + 1 end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
get_form = function()
|
||||
|
||||
local n = #passlist;
|
||||
|
||||
local frm = "label[0,0;" .. intro_msg .. "] " .. "label[0,8.5;" .. msg .. "] "
|
||||
|
||||
for i = 1,10 do
|
||||
frm = frm .. "button[0,".. (i)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] "
|
||||
end
|
||||
|
||||
for i = 11,n do
|
||||
frm = frm .. "button[2,".. (i-11+1)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] "
|
||||
end
|
||||
|
||||
local form = "size[4," .. 9 .. "]" .. frm;
|
||||
return form
|
||||
end
|
||||
|
||||
_G.math.randomseed(os.time())
|
||||
intro_msg = minetest.colorize("lawngreen","FALLOUT PASSWORD HACKING GAME\nmatch is both position and character.")
|
||||
msg = "" --TEST\nTEST\nTEST";
|
||||
passlist = {}; passdict = {}
|
||||
|
||||
n = 20; -- how many options
|
||||
count = 0;
|
||||
while count< n do
|
||||
local pass = generate_random_string(4,5); -- password length, charset size
|
||||
if not passdict[pass] then passlist[#passlist+1] = pass; passdict[pass] = true; count = count + 1 end
|
||||
end
|
||||
correct = math.random(n)
|
||||
guesses = 0
|
||||
max_guesses = 4
|
||||
|
||||
rom.data = {};
|
||||
if not rom.data then rom.data = {} end
|
||||
self.spam(1)
|
||||
|
||||
local players = find_player(4);
|
||||
if not players then say("#fallout hacking game: no players") self.remove() end
|
||||
pname = players[1];
|
||||
minetest.chat_send_player(pname,"#fallout hacking game, player " .. pname)
|
||||
|
||||
--if rom.data[pname] then say("password is locked out!") self.remove() end
|
||||
|
||||
self.show_form(pname,get_form())
|
||||
self.read_form()
|
||||
|
||||
end
|
||||
|
||||
sender,fields = self.read_form()
|
||||
if sender and sender == pname then -- form event
|
||||
local pl = _G.minetest.get_player_by_name(pname);
|
||||
if pl then
|
||||
local selected = 0
|
||||
for k,_ in pairs(fields) do if k~="quit" then selected = tonumber(k) break end end
|
||||
|
||||
if selected>0 then
|
||||
guesses = guesses + 1
|
||||
if selected == correct then
|
||||
minetest.chat_send_player(pname,"password " .. passlist[correct] .. " is correct! " .. guesses .. " guesses.")
|
||||
self.show_form(pname, "size[2,1] label[0,0.5;" .. minetest.colorize("lawngreen", "ACCESS GRANTED") .. "]")
|
||||
self.remove()
|
||||
--correct: do something with player
|
||||
else
|
||||
if guesses == 3 then msg = msg .. "\n" end
|
||||
msg = msg .. " " .. minetest.colorize("yellow",guesses .. ". " .. passlist[selected]) .. " (" .. get_similiarity(passlist[correct], passlist[selected]) .. " match)"
|
||||
self.show_form(pname, get_form())
|
||||
end
|
||||
if guesses>=max_guesses then
|
||||
msg = minetest.colorize("red","A C C E S S D E N I E D!")
|
||||
self.show_form(pname, get_form())
|
||||
minetest.chat_send_player(pname,"too many false guesses. password locked out!") rom.data[pname] = 1; self.remove()
|
||||
end
|
||||
end
|
||||
if fields.quit then self.remove() end
|
||||
end
|
||||
end
|
106
scripts/games/hide_and_seek.lua
Normal file
@ -0,0 +1,106 @@
|
||||
--HIDE AND SEEK game robot, by rnd
|
||||
if not gamemaster then
|
||||
timeout = 10;
|
||||
gamemaster = "rnd"
|
||||
player_list = {};
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK .. say #hide to join play")
|
||||
s=0;t=0; count = 0;
|
||||
_G.minetest.forceload_block(self.pos(),true)
|
||||
self.listen(1); self.label(colorize("yellow","HIDE&SEEK"))
|
||||
end
|
||||
|
||||
speaker,msg = self.listen_msg();
|
||||
|
||||
if s==0 then
|
||||
if msg =="#hide" then
|
||||
player_list[speaker]={};
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: " .. speaker .. " joined the game")
|
||||
local player = _G.minetest.get_player_by_name(speaker);
|
||||
if player then
|
||||
player:setpos({x=0,y=5,z=0});player:set_properties({nametag_color = "0x0"})
|
||||
end
|
||||
|
||||
end
|
||||
if msg == "#start" and speaker == gamemaster then s = 0.5 _G.minetest.chat_send_all("# HIDE AND SEEK STARTS in " .. timeout .. " SECONDS!") end
|
||||
|
||||
elseif s==0.5 then
|
||||
t=t+1
|
||||
if t==timeout then
|
||||
t=0;s = 1; count = 0;
|
||||
for pname,_ in pairs(player_list) do
|
||||
local player = _G.minetest.get_player_by_name(pname);
|
||||
if player then
|
||||
player_list[pname].hp = player:get_hp();
|
||||
player_list[pname].pos = player:getpos()
|
||||
player_list[pname].t = 0;
|
||||
count = count+1
|
||||
end
|
||||
end
|
||||
if count == 1 then
|
||||
gamemaster = false; _G.minetest.chat_send_all("# HIDE AND SEEK only 1 player, aborting.")
|
||||
else
|
||||
_G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK STARTS NOW WITH " .. count .. " PLAYERS. You are out if: 1.your health changes, 2. leave spawn. If stay in same area for too long or you will be exposed."))
|
||||
end
|
||||
|
||||
end
|
||||
elseif s==1 then
|
||||
players = _G.minetest.get_connected_players();
|
||||
count = 0;
|
||||
for _,player in pairs(players) do
|
||||
local name = player:get_player_name();
|
||||
local data = player_list[name];
|
||||
if data then
|
||||
count=count+1
|
||||
local pos = player:getpos();
|
||||
local dist = math.max(math.abs(pos.x),math.abs(pos.y),math.abs(pos.z));
|
||||
if dist>50 or (not _G.minetest.get_player_by_name(name)) then
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! went too far away " )
|
||||
player:set_properties({nametag_color = "white"})
|
||||
player_list[name] = nil;
|
||||
end
|
||||
if data.hp ~= player:get_hp() then
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! his health changed!" )
|
||||
player:set_properties({nametag_color = "white"})
|
||||
player_list[name] = nil;
|
||||
end
|
||||
|
||||
--expose campers
|
||||
local p = data.pos;
|
||||
dist = math.max(math.abs(pos.x-p.x),math.abs(pos.y-p.y),math.abs(pos.z-p.z));
|
||||
--say( name .. " dist " .. dist .. " t " .. data.t)
|
||||
if dist<8 then
|
||||
data.t = data.t+1;
|
||||
if not data.camp then
|
||||
if data.t>15 and not data.camp then
|
||||
_G.minetest.chat_send_player(name, "# HIDE AND SEEK: move in 5s or be exposed")
|
||||
data.camp = true
|
||||
end
|
||||
elseif data.t>=20 then
|
||||
pos.x=math.ceil(pos.x);pos.y=math.ceil(pos.y);pos.z=math.ceil(pos.z);
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: " .. name .. " is camping at " .. pos.x .. " " .. pos.z)
|
||||
data.camp = false; data.t = 0
|
||||
end
|
||||
else
|
||||
data.t = 0; data.pos = player:getpos(); data.camp = false
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
self.label(count)
|
||||
|
||||
if count<=1 then
|
||||
if count==1 then
|
||||
for name,_ in pairs(player_list) do
|
||||
player0=_G.minetest.get_player_by_name(name)
|
||||
_G.minetest.chat_send_all(colorize("red","****** HIDE AND SEEK: ".. name .. " wins ******"))
|
||||
player0:set_properties({nametag_color = "white"})
|
||||
gamemaster = false;
|
||||
end
|
||||
else
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: no players left")
|
||||
gamemaster = false;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
151
scripts/games/hide_and_seek_1.lua
Normal file
@ -0,0 +1,151 @@
|
||||
--HIDE AND SEEK game robot
|
||||
if not gamemaster then
|
||||
timeout = 30;
|
||||
gamemaster = "rnd"
|
||||
gamepos = {x=0,y=5,z=0}
|
||||
player_list = {};
|
||||
s=0;t=0; count = 0;
|
||||
prize = ""
|
||||
|
||||
get_players = function()
|
||||
local msg = "";
|
||||
for name,_ in pairs(player_list) do
|
||||
msg = msg .. " " .. name
|
||||
end
|
||||
return msg
|
||||
end
|
||||
|
||||
init_game = function()
|
||||
|
||||
local msg = get_players();
|
||||
_G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK : hide from everyone else who is playing. Winner gets DIAMONDS\nsay join to join play. say #start to start game."..
|
||||
" players: " .. msg))
|
||||
s=0;t=0;
|
||||
end
|
||||
|
||||
init_game()
|
||||
_G.minetest.forceload_block(self.pos(),true)
|
||||
self.listen(1); self.label(colorize("yellow","HIDE&SEEK"))
|
||||
end
|
||||
|
||||
speaker,msg = self.listen_msg();
|
||||
|
||||
if s==0 then
|
||||
|
||||
t = t +1
|
||||
if t%30 == 0 then
|
||||
init_game();
|
||||
end
|
||||
|
||||
if msg =="join" then
|
||||
player_list[speaker]={};
|
||||
_G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK: " .. speaker .. " joined the game"));
|
||||
_G.minetest.chat_send_all("players: " .. get_players())
|
||||
|
||||
local player = _G.minetest.get_player_by_name(speaker);
|
||||
count = count + 1
|
||||
if player then
|
||||
player:setpos(gamepos);player:set_properties({nametag_color = "0x0"})
|
||||
player:set_hp(20)
|
||||
local inv = player:get_inventory();inv:set_list("main",{})
|
||||
end
|
||||
|
||||
end
|
||||
if msg == "#start" and count>1 then s = 0.5 _G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK STARTS in " .. timeout .. " SECONDS!")) end
|
||||
|
||||
elseif s==0.5 then
|
||||
t=t+1
|
||||
if t==timeout then
|
||||
t=0;s = 1; count = 0;
|
||||
for pname,_ in pairs(player_list) do
|
||||
local player = _G.minetest.get_player_by_name(pname);
|
||||
if player then
|
||||
player_list[pname].hp = player:get_hp();
|
||||
player_list[pname].pos = player:getpos()
|
||||
player_list[pname].t = 0;
|
||||
count = count+1
|
||||
end
|
||||
end
|
||||
if count == 1 then
|
||||
gamemaster = false; _G.minetest.chat_send_all("# HIDE AND SEEK only 1 player, aborting.")
|
||||
else
|
||||
prize = "default:diamond " .. (count-1);
|
||||
_G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK STARTS NOW WITH " .. count .. " PLAYERS."..
|
||||
"You are out if: 1.your health changes, 2. leave game area. If stay in same area for too long or you will be exposed."))
|
||||
_G.minetest.chat_send_all(colorize("red","# WINNER WILL GET " .. prize))
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
elseif s==1 then
|
||||
players = _G.minetest.get_connected_players();
|
||||
count = 0;
|
||||
for _,player in pairs(players) do
|
||||
local name = player:get_player_name();
|
||||
local data = player_list[name];
|
||||
if data then
|
||||
count=count+1
|
||||
local pos = player:getpos();
|
||||
local dist = math.max(math.abs(pos.x-gamepos.x),math.abs(pos.y-gamepos.y),math.abs(pos.z-gamepos.z));
|
||||
if dist>100 or (not _G.minetest.get_player_by_name(name)) then
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! went too far away " )
|
||||
player:set_properties({nametag_color = "white"})
|
||||
player_list[name] = nil;
|
||||
_G.minetest.chat_send_all("remaining players: " .. get_players())
|
||||
end
|
||||
if data.hp ~= player:get_hp() then
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! his health changed!" )
|
||||
player:set_properties({nametag_color = "white"})
|
||||
player_list[name] = nil;
|
||||
_G.minetest.chat_send_all("remaining players: " .. get_players())
|
||||
player:setpos({x=0,y=5,z=0})
|
||||
end
|
||||
|
||||
--expose campers
|
||||
local p = data.pos;
|
||||
dist = math.max(math.abs(pos.x-p.x),math.abs(pos.y-p.y),math.abs(pos.z-p.z));
|
||||
--say( name .. " dist " .. dist .. " t " .. data.t)
|
||||
if dist<8 then
|
||||
data.t = data.t+1;
|
||||
if not data.camp then
|
||||
if data.t>25 and not data.camp then
|
||||
_G.minetest.chat_send_player(name, "# HIDE AND SEEK: move in 5s or be exposed")
|
||||
data.camp = true
|
||||
end
|
||||
elseif data.t>=30 then
|
||||
pos.x=math.ceil(pos.x);pos.y=math.ceil(pos.y);pos.z=math.ceil(pos.z);
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: " .. name .. " is camping at " .. pos.x .. " " .. pos.z)
|
||||
data.camp = false; data.t = 0
|
||||
end
|
||||
else
|
||||
data.t = 0; data.pos = player:getpos(); data.camp = false
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
self.label(count)
|
||||
|
||||
if count<=1 then
|
||||
if count==1 then
|
||||
for name,_ in pairs(player_list) do
|
||||
local player0=_G.minetest.get_player_by_name(name)
|
||||
if player0 then
|
||||
_G.minetest.chat_send_all(colorize("red","****** HIDE AND SEEK: ".. name .. " wins ******"))
|
||||
local inv = player0:get_inventory();
|
||||
inv:add_item("main",_G.ItemStack(prize))
|
||||
player0:set_properties({nametag_color = "white"})
|
||||
player0:setpos({x=0,y=5,z=0})
|
||||
end
|
||||
s=2
|
||||
end
|
||||
else
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: no players left")
|
||||
s=2
|
||||
end
|
||||
end
|
||||
|
||||
elseif s==2 then
|
||||
player_list = {}
|
||||
init_game()
|
||||
end
|
138
scripts/games/maze_generator.lua
Normal file
@ -0,0 +1,138 @@
|
||||
-- maze generation by rnd
|
||||
|
||||
-- http://en.wikipedia.org/wiki/Maze_generation_algorithm#Depth-first_search, recursive backtracker
|
||||
-- representation of node coordinate (row,coloumn)=(i,j) -> (i-1)*n+j, i=1..n, j=1...m
|
||||
-- representation of walls: below node k --> k, left of node k --> k+m.n
|
||||
|
||||
-- good overview of maze generation algorithms using javascript/html5
|
||||
-- http://www.jamisbuck.org/presentations/rubyconf2011/index.html#recursive-backtracker
|
||||
|
||||
-- helper functions
|
||||
--stack in lua
|
||||
local stack={};
|
||||
function stack.push(s,e) s[#s+1]=e end
|
||||
function stack.pop(s) local r = s[#s];s[#s]=nil;return r end
|
||||
--function table2string(s) local r = ""; for i,v in pairs(s) do r = r.. " ["..i.."]=".. v ; end return r end
|
||||
|
||||
function maze_deep_first_search(m,n,start,seed) -- returns a table of strings representing line renders
|
||||
|
||||
local steps,maxsteps; steps= 0; maxsteps = 999999;
|
||||
local maze = {}
|
||||
maze.m = m; maze.n = n;
|
||||
maze.unvisited = {};maze.stack = {}; maze.walls = {};
|
||||
maze.free = maze.m*maze.n;
|
||||
local i,j,k
|
||||
local nb,wall -- unvisited neighbbors, walls
|
||||
|
||||
--init structures
|
||||
for i=1,maze.m do
|
||||
for j =1,maze.n do
|
||||
k=(i-1)*maze.n+j;maze.unvisited[k]=true -- initially all cells unvisited
|
||||
maze.walls[k]=true;maze.walls[k+maze.n*maze.m]=true; -- walls are there
|
||||
end
|
||||
end
|
||||
|
||||
_G.math.randomseed(seed)
|
||||
maze.current = start
|
||||
maze.unvisited [ maze.current ] = false;
|
||||
maze.free = maze.free-1; maze.stack[1+#maze.stack] = maze.current
|
||||
|
||||
while maze.free>0 and steps<maxsteps do -- main loop
|
||||
steps=steps+1
|
||||
-- check current node neighbors
|
||||
k=maze.current
|
||||
j = k % maze.n;i=math.ceil(k/maze.n); -- get coords back from index
|
||||
if j==0 then j = maze.n end
|
||||
--print("coords current node "..k .. " = " .. i .. " " ..j)
|
||||
|
||||
nb={};wall={}-- check unvisited neighbors & wall removals
|
||||
|
||||
if i>1 then -- down
|
||||
k=(i-2)*maze.n+j; if maze.unvisited[k] then wall[#wall+1]=k+maze.n;nb[#nb+1]=k end
|
||||
end
|
||||
if i<maze.m then -- up
|
||||
k=(i)*maze.n+j; if maze.unvisited[k] then wall[#wall+1]=k;nb[#nb+1]=k end
|
||||
end
|
||||
if j<maze.n then --right
|
||||
k=(i-1)*maze.n+j+1; if maze.unvisited[k] then wall[#wall+1]=k+maze.n*maze.m; nb[#nb+1]=k end
|
||||
end
|
||||
if j>1 then --left
|
||||
k=(i-1)*maze.n+j-1; if maze.unvisited[k] then wall[#wall+1]=k+1+maze.n*maze.m;nb[#nb+1]=k end
|
||||
end
|
||||
|
||||
--print(" unvisited neighbors " .. table2string(nb))
|
||||
if (#nb)>0 then -- if unvisited neighbors, choose random one as next current node
|
||||
stack.push(maze.stack,maze.current) -- remember previous current node
|
||||
k=math.random(#nb); -- pick random unvisited neighbor
|
||||
maze.walls[wall[k]]=false; -- remove wall
|
||||
--print(" removed wall ".. wall[k])
|
||||
k=nb[k];
|
||||
maze.current = k; -- new current cell
|
||||
maze.unvisited[k]=false; maze.free = maze.free-1 -- one less unvisited
|
||||
--print("new explore " .. k);
|
||||
|
||||
elseif (#maze.stack)~=0 then -- no unvisited neighbors, backtrack using stack
|
||||
|
||||
maze.current = stack.pop(maze.stack)
|
||||
--print("backtrack to "..maze.current)
|
||||
|
||||
else -- even stack is empty, just pick random unvisited cell
|
||||
k = math.random(maze.free); j=1;
|
||||
for i =1,maze.m*maze.n do
|
||||
if maze.unvisited[i] then
|
||||
if j==k then k=i; break end -- pick node
|
||||
j=j+1
|
||||
end
|
||||
end
|
||||
--print(" stack empty, random pick " ..k)
|
||||
maze.current=k;maze.unvisited[k]=false; maze.free = maze.free -1;
|
||||
end
|
||||
end -- of do
|
||||
|
||||
-- render maze with chars, row by row
|
||||
maze.ret = {};
|
||||
local hor;local vert;
|
||||
local wall = "1"
|
||||
|
||||
for i=1,maze.m do
|
||||
hor="";vert="";
|
||||
k= (i-1)*maze.n;
|
||||
-- horizontal
|
||||
for j = 1, maze.n do
|
||||
k=k+1;
|
||||
-- if maze.walls[k+maze.n*maze.m] then vert=vert.."X." else vert=vert.. "0." end
|
||||
-- if maze.walls[k] then hor=hor.."XX" else hor=hor.."X0" end
|
||||
if maze.walls[k+maze.n*maze.m] then vert=vert..wall.."0" else vert=vert.. "00" end
|
||||
if maze.walls[k] then hor=hor..wall..wall else hor=hor..wall.."0" end
|
||||
end
|
||||
maze.ret[1+#maze.ret]=hor..wall;maze.ret[1+#maze.ret]=vert..wall;
|
||||
end
|
||||
maze.ret[1+#maze.ret] = string.rep(wall,2*maze.n+1)
|
||||
return maze.ret
|
||||
end
|
||||
|
||||
-- RUN PROGRAM
|
||||
local maze=maze_deep_first_search(10,30,1,2015)
|
||||
--for _,v in pairs(maze) do print(v) end
|
||||
|
||||
|
||||
|
||||
make_maze = function(m,n,start,seed)
|
||||
local pos = self.spawnpos();pos.y=pos.y+1
|
||||
local p
|
||||
local maze=maze_deep_first_search(m,n,start,seed) -- m,n,start,seed
|
||||
local i,j,k;local p = {x=pos.x,y=pos.y,z=pos.z};
|
||||
for i,v in pairs(maze) do
|
||||
p.x = pos.x+i
|
||||
for k = 1,string.len(v) do
|
||||
p.z=pos.z+k
|
||||
if string.sub(v,k,k)=="1" then
|
||||
minetest.set_node(p,{name="default:brick"})
|
||||
else minetest.set_node(p,{name="air"})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
make_maze(10,10,1,1)
|
||||
self.remove()
|
89
scripts/games/mensch_argere_dich_nicht.lua
Normal file
@ -0,0 +1,89 @@
|
||||
|
||||
if not init then
|
||||
|
||||
msg =
|
||||
"'Mensch argere Dich nicht' is a German board game (but not a German-style board game), developed by Josef Friedrich Schmidt in 1907/1908.\n"..
|
||||
" The players throw a die in turn and can advance any of their pieces in the game by the thrown number of dots on the dice.\n" ..
|
||||
"Throwing a six means bringing a piece into the game (by placing one from the 'out' area onto the 'start' field) and throwing the dice again. If\n" ..
|
||||
"a piece is on the 'start' field and there are still pieces in the 'out' area, it must be moved as soon as possible. If a piece cannot be\n".. "brought into the game then any other piece in the game must be moved by the thrown number, if that is possible. Pay attention that throwing\n".." dice continuously without moving is forbidden and by each dice throw you have to make a move.\n" ..
|
||||
"Pieces can jump over other pieces, and throw out pieces from other players (into that player's 'out' area) if they land on them. A player\n".. "cannot throw out his own pieces though, he can advance further than the last field in the 'home' row. A player can be thrown out if he is on\n"..
|
||||
"his 'start' field.\n" ..
|
||||
"Variation which is played by most players: A player who has no pieces in the game has 3 tries to throw a six"
|
||||
self.label(msg)
|
||||
|
||||
init = true;
|
||||
state = 1; -- game on
|
||||
step = 0;
|
||||
punchstate = 1; -- first punch
|
||||
punchpos = {}
|
||||
pos = self.spawnpos()
|
||||
dice = 0
|
||||
spawns = {{2,2,"basic_robot:buttonFF8080"},{2,11,"basic_robot:button8080FF"},{11,11,"basic_robot:button80FF80"},{11,2,"basic_robot:buttonFFFF80"}}
|
||||
|
||||
for i = 1,12 do for j = 1,12 do
|
||||
minetest.swap_node({x=pos.x+i,y=pos.y+1,z=pos.z+j},{name = "air"})
|
||||
end end
|
||||
|
||||
for k = 1,#spawns do
|
||||
for i = 0,1 do for j = 0,1 do
|
||||
minetest.swap_node({x=pos.x+i+spawns[k][1],y=pos.y+1,z=pos.z+j+spawns[k][2]},{name = spawns[k][3]})
|
||||
end end
|
||||
end
|
||||
|
||||
keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7)
|
||||
|
||||
end
|
||||
|
||||
if state == 0 then
|
||||
elseif state == 1 then
|
||||
event = keyboard.get();
|
||||
if event then
|
||||
x = event.x-pos.x; y = event.y-pos.y; z = event.z-pos.z
|
||||
--say("type " .. event.type .. " pos " .. x .. " " .. y .. " " .. z)
|
||||
if x == 7 and y == 1 and z == 7 then
|
||||
_G.math.randomseed(os.time())
|
||||
dice = math.random(6);
|
||||
keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7+dice)
|
||||
step = step + 1;
|
||||
msg = colorize("red","<Mensch argere dich nicht>") .. " STEP " .. step .. ": " ..event.puncher .. " threw dice = " .. dice;
|
||||
minetest.chat_send_all(msg)
|
||||
self.label(msg)
|
||||
punchstate = 1
|
||||
elseif punchstate == 1 then
|
||||
if y == 1 and event.type ~= 2 and event.type<7 then
|
||||
punchpos = 2; minetest.chat_send_player(event.puncher,colorize("red","<Mensch argere dich nicht>") .. " punch place on board where to move ")
|
||||
punchpos = {x=event.x,y=event.y,z=event.z}
|
||||
punchstate = 2
|
||||
end
|
||||
elseif punchstate == 2 then
|
||||
if y == 0 and event.type ~= 2 then
|
||||
if x<2 or x>12 or z<2 or z>12 then
|
||||
else
|
||||
local nodename = minetest.get_node(punchpos).name;
|
||||
minetest.swap_node({x=event.x, y = event.y+1, z=event.z},{name = nodename})
|
||||
minetest.swap_node(punchpos,{name = "air"})
|
||||
punchstate = 1; dice = 0
|
||||
minetest.add_particle(
|
||||
{
|
||||
pos = punchpos,
|
||||
expirationtime = 15,
|
||||
velocity = {x=0, y=0,z=0},
|
||||
size = 18,
|
||||
texture = "default_apple.png",
|
||||
acceleration = {x=0,y=0,z=0},
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
}
|
||||
)
|
||||
msg = colorize("red","<Mensch argere dich nicht>") .. " " .. event.puncher .. " moved.";
|
||||
minetest.chat_send_all(msg)
|
||||
self.label(msg)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
83
scripts/games/minesweeper_game.lua
Normal file
@ -0,0 +1,83 @@
|
||||
-- minesweeper
|
||||
if not data then
|
||||
m=24;n=22; minescount = m*n/5;
|
||||
reward = 30;
|
||||
|
||||
if not find_player(4) then error("minesweeper: no players near") end
|
||||
|
||||
self.spam(1)
|
||||
t0 = _G.minetest.get_gametime();
|
||||
data = {}; spawnpos = self.spawnpos() -- place mines
|
||||
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
|
||||
if not data[1] then data[1] = {} end if not data[2] then data[2] = {} end -- create 2x2 safe area
|
||||
data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0;
|
||||
|
||||
minescount = 0;
|
||||
for i = 1,m do for j = 1,n do -- render game
|
||||
if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
|
||||
puzzle.set_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},{name = "basic_robot:button808080"})
|
||||
end
|
||||
end end
|
||||
puzzle.set_node({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},{name = "basic_robot:button80FF80"})
|
||||
|
||||
get_mine_count = function(i,j)
|
||||
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
|
||||
for k = -1,1 do for l = -1,1 do
|
||||
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
chk_mines = function()
|
||||
local count = minescount;
|
||||
for i=1,m do for j=1,n do
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})=="basic_robot:buttonFF8080" and data[i] and data[i][j]==1 then
|
||||
count=count-1
|
||||
end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
say("minesweeper " .. m .. "x" ..n .. " with " .. minescount .. " mines ")
|
||||
self.label("find all hidden mines! mark mine by standing on top of block and punch,\notherwise it will uncover the block (and possibly explode).")
|
||||
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event then
|
||||
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
|
||||
if x<1 or x>m or z<1 or z>n then
|
||||
if x == 0 and z == 1 then
|
||||
local count = chk_mines();
|
||||
if count == 0 then
|
||||
t0 = _G.minetest.get_gametime() - t0;
|
||||
say("congratulations! " .. event.puncher .. " discovered all mines in " .. t0 .. " s")
|
||||
_G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward
|
||||
else
|
||||
reward = reward*(1-(count/minescount))^(1.5); reward = math.floor(reward);
|
||||
say("FAIL! " .. count .. " mines remaining. You get " .. reward .. " gold for found mines")
|
||||
_G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward
|
||||
end
|
||||
self.remove()
|
||||
end
|
||||
else --if event.type == 2 then
|
||||
local ppos = player.getpos(event.puncher)
|
||||
if ppos and math.abs(ppos.x-event.x)<0.5 and math.abs(ppos.z-event.z)<0.5 then -- just mark mine
|
||||
if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:button808080" then
|
||||
puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:button808080"})
|
||||
else
|
||||
puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:buttonFF8080"})
|
||||
end
|
||||
else
|
||||
if data[x] and data[x][z]==1 then
|
||||
say("boom! "..event.puncher .. " is dead ");puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:buttonFF8080"});
|
||||
local player_ = puzzle.get_player(event.puncher);
|
||||
player_:setpos({x=spawnpos.x-1,y=spawnpos.y+1,z=spawnpos.z-1});
|
||||
self.remove()
|
||||
else
|
||||
local count = get_mine_count(x,z);
|
||||
if count == 0 then puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:button80FF80"})
|
||||
else puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:button"..count}) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
264
scripts/games/nonogram.lua
Normal file
@ -0,0 +1,264 @@
|
||||
-- nonogram game, created in 1hr 40 min by rnd
|
||||
|
||||
-- INIT
|
||||
if not grid then
|
||||
n=6
|
||||
solved = false -- do we render solution or blank?
|
||||
-- _G.math.randomseed(3)
|
||||
|
||||
self.spam(1)
|
||||
function get_score_from_string(score)
|
||||
--say(score)
|
||||
local scores = {};
|
||||
local j=1; --j k l
|
||||
for i=0,5 do -- 0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999
|
||||
j = string.find(score," ", j+1);
|
||||
local k = string.find(score," ", j+1);
|
||||
local l = string.find(score," ", k+1);
|
||||
if i==5 then l = string.len(score)+1 end
|
||||
scores[i] = {string.sub(score,j+1,k-1),tonumber(string.sub(score,k+1,l-1))};
|
||||
j=l
|
||||
end
|
||||
return scores
|
||||
end
|
||||
|
||||
if not rom.score then _,rom.score = book.read(1) end
|
||||
if not rom.score then rom.score = "0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999" end
|
||||
highscore = get_score_from_string(rom.score)
|
||||
--self.label(string.gsub(_G.dump(highscore), "\n",""))
|
||||
|
||||
function get_score_string(scores)
|
||||
local out = ""
|
||||
for i = 0,5 do
|
||||
out = out .. i .. " " ..
|
||||
scores[i][1] .. " " ..
|
||||
scores[i][2] .. " "
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
t0 = _G.minetest.get_gametime()
|
||||
local intro ="numbers at beginning of each row (coloumn) tell how many\nred blocks are together in each row ( coloumn )." ..
|
||||
"\npunch gray blocks to toggle them and reveal hidden red blocks.\npunch green to check solution. If you give up punch blue.";
|
||||
self.label(intro)
|
||||
|
||||
grid = {}
|
||||
spawnpos = self.spawnpos();
|
||||
offsetx = 10 - math.ceil(n/2); offsetz = math.floor(n/2);
|
||||
spawnpos.x = spawnpos.x - offsetx; spawnpos.z = spawnpos.z - offsetz;
|
||||
spawnpos.y = spawnpos.y+3
|
||||
|
||||
for i=1,n do
|
||||
grid[i]={};
|
||||
for j=1,n do
|
||||
grid[i][j]=math.random(2)-1
|
||||
end
|
||||
end
|
||||
|
||||
getcounts = function(grid)
|
||||
local rowdata = {};
|
||||
for i=1,n do
|
||||
rowdata[i]={}; local data = rowdata[i];
|
||||
local s=0;local c=0;
|
||||
for j = 1, n do
|
||||
if s == 0 and grid[i][j]==1 then s=1;c=0 end
|
||||
if s == 1 then
|
||||
if grid[i][j]==1 then
|
||||
c=c+1
|
||||
if j == n then data[#data+1]=c end
|
||||
else
|
||||
data[#data+1]=c; s=0
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
local coldata = {};
|
||||
for j=1,n do
|
||||
coldata[j]={}; local data = coldata[j];
|
||||
local s=0;local c=0;
|
||||
for i = 1, n do
|
||||
if s == 0 and grid[i][j]==1 then s=1;c=0 end
|
||||
if s == 1 then
|
||||
if grid[i][j]==1 then
|
||||
c=c+1
|
||||
if i == n then data[#data+1]=c end
|
||||
else
|
||||
data[#data+1]=c; s=0
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
return rowdata,coldata
|
||||
end
|
||||
|
||||
read_field = function()
|
||||
local grid = {};
|
||||
for i = 1, n do
|
||||
grid[i]={};
|
||||
for j = 1,n do
|
||||
local typ = keyboard.read({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+i});
|
||||
if typ == "basic_robot:button808080" then grid[i][j] = 0 else grid[i][j] = 1 end
|
||||
end
|
||||
end
|
||||
return grid
|
||||
end
|
||||
|
||||
rowdata,coldata = getcounts(grid)
|
||||
|
||||
check_solution = function()
|
||||
local rdata,cdata;
|
||||
rdata,cdata = getcounts(read_field())
|
||||
for i = 1,#rdata do
|
||||
if #rdata[i]~=#rowdata[i] then return false end
|
||||
for j = 1, #rdata[i] do
|
||||
if rdata[i][j]~=rowdata[i][j] then return false end
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1,#cdata do
|
||||
if #cdata[i]~=#coldata[i] then return false end
|
||||
for j = 1, #rdata[i] do
|
||||
if cdata[i][j]~=coldata[i][j] then return false end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
get_difficulty = function()
|
||||
local easy = 0;
|
||||
for k = 1, n do
|
||||
local sum=0
|
||||
for i = 1,#rowdata[k]-1 do
|
||||
sum = sum + rowdata[k][i]+1;
|
||||
end
|
||||
if #rowdata[k]>0 then sum = sum + rowdata[k][#rowdata[k]] else sum = n end
|
||||
if sum == n then easy = easy + 1 end
|
||||
end
|
||||
|
||||
for k = 1, n do
|
||||
local sum=0
|
||||
for i = 1,#coldata[k]-1 do
|
||||
sum = sum + coldata[k][i]+1;
|
||||
end
|
||||
if #coldata[k]>0 then sum = sum + coldata[k][#coldata[k]] else sum = n end
|
||||
if sum == n then easy = easy + 1 end
|
||||
end
|
||||
easy = 5-easy;
|
||||
if easy < 0 then easy = 0 end
|
||||
return easy
|
||||
end
|
||||
|
||||
-- render game
|
||||
for i=1,n do
|
||||
for j =1,n do
|
||||
keyboard.set({x=spawnpos.x-n+j,y=spawnpos.y,z=spawnpos.z+i},0) -- clear
|
||||
keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+2*n-i+1},0) -- clear
|
||||
local typ;
|
||||
if grid[j][i]==0 then typ = 2 else typ = 3 end
|
||||
if not solved then typ = 2 end
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ) --board
|
||||
end
|
||||
end
|
||||
|
||||
--render counts rows
|
||||
for i=1,n do
|
||||
length = #rowdata[i]
|
||||
for k = 1,length do
|
||||
keyboard.set({x=spawnpos.x-length+k,y=spawnpos.y,z=spawnpos.z+i},rowdata[i][k]+7)
|
||||
end
|
||||
end
|
||||
--render counts coloumns
|
||||
for j=1,n do
|
||||
length = #coldata[j]
|
||||
for k = 1,length do
|
||||
keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+k+n},coldata[j][k]+7)
|
||||
end
|
||||
end
|
||||
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},4) -- game check button
|
||||
keyboard.set({x=spawnpos.x+2,y=spawnpos.y,z=spawnpos.z},5) -- game check button
|
||||
|
||||
local players = find_player(4,spawnpos)
|
||||
if not players then error("minesweeper: no players near") end
|
||||
local pname = players[1];
|
||||
|
||||
|
||||
--self.label()
|
||||
|
||||
--self.label(string.gsub(_G.dump(read_field()),"\n","") )
|
||||
difficulty = get_difficulty()
|
||||
reward = 0; limit = 0;
|
||||
|
||||
if difficulty == 5 then limit = 120 reward = 10
|
||||
elseif difficulty == 4 then limit = 115 reward = 9 -- 60s
|
||||
elseif difficulty == 3 then limit = 100 reward = 8
|
||||
elseif difficulty == 2 then limit = 80 reward = 7
|
||||
elseif difficulty <= 1 then limit = 70 reward = 6
|
||||
end
|
||||
minetest.chat_send_player(pname, "nonogram difficulty " .. difficulty .. ". you will get " .. reward .. " gold if you solve it in faster than " .. limit .."s" ..
|
||||
". Current record " .. highscore[difficulty][2] .. " by " .. highscore[difficulty][1])
|
||||
|
||||
end
|
||||
|
||||
event = keyboard.get()
|
||||
if event then
|
||||
if event.y == spawnpos.y and event.z == spawnpos.z then
|
||||
if event.x == spawnpos.x+1 then -- check solution
|
||||
if check_solution() then
|
||||
t = _G.minetest.get_gametime(); t = t- t0;
|
||||
local msg = "";
|
||||
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},2)
|
||||
msg = n .. "x" .. n .. " nonogram (difficuly " .. difficulty .. ") solved by " .. event.puncher .. " in " .. t .. " seconds. "
|
||||
|
||||
if t < limit then
|
||||
msg = msg .. " He gets " .. reward .. " gold for quick solve.";
|
||||
else
|
||||
reward = reward*2*(1-2*(t-limit)/limit)/2; if reward<0 then reward = 0 end
|
||||
reward = math.floor(reward);
|
||||
msg = msg .. " Your time was more than " .. limit .. ", you get " .. reward .. " gold ";
|
||||
end
|
||||
|
||||
-- highscore
|
||||
if t<highscore[difficulty][2] then
|
||||
say("nonogram: new record " .. t .. " s ! old record " .. highscore[difficulty][2] .. "s by " .. highscore[difficulty][1])
|
||||
highscore[difficulty] = {event.puncher, t}
|
||||
rom.score = get_score_string(highscore)
|
||||
book.write(1,"scores", rom.score)
|
||||
end
|
||||
|
||||
if reward>0 then
|
||||
local player = _G.minetest.get_player_by_name(event.puncher);
|
||||
if player then
|
||||
local inv = player:get_inventory();
|
||||
inv:add_item("main",_G.ItemStack("default:gold_ingot " .. reward))
|
||||
end
|
||||
end
|
||||
minetest.chat_send_player(event.puncher,msg)
|
||||
|
||||
self.remove()
|
||||
|
||||
else self.label("FAIL") end
|
||||
elseif event.x == spawnpos.x+2 then -- solve
|
||||
minetest.chat_send_player(event.puncher,"you gave up on game, displaying solution")
|
||||
for i=1,n do
|
||||
for j =1,n do
|
||||
local typ;
|
||||
if grid[j][i]==0 then typ = 2 else typ = 3 end
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ)
|
||||
end
|
||||
end
|
||||
self.remove()
|
||||
end
|
||||
else
|
||||
local i = event.x-spawnpos.x;local j = event.z-spawnpos.z;
|
||||
if i>0 and i<=n and j>0 and j<=n then
|
||||
local typ = keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j});
|
||||
local newtyp;
|
||||
if typ == "basic_robot:button808080" then newtyp = 3
|
||||
else newtyp = 2
|
||||
end
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},newtyp);
|
||||
end
|
||||
end
|
||||
end
|
112
scripts/games/paint_game.lua
Normal file
@ -0,0 +1,112 @@
|
||||
-- paint canvas by rnd, 2018
|
||||
if not init then
|
||||
colors = {
|
||||
"black","blue","brown","cyan","dark_green","dark_grey","green","grey",
|
||||
"magenta","orange","pink","red","violet","white","yellow"
|
||||
}
|
||||
invcolors = {}; for i = 1,#colors do invcolors[colors[i]] = i end
|
||||
|
||||
color = 1;
|
||||
size = 16;
|
||||
|
||||
init = true
|
||||
|
||||
local ent = _G.basic_robot.data[self.name()].obj:get_luaentity();
|
||||
ent.timestep = 0.5
|
||||
|
||||
players = find_player(5); if not players then self.remove() end
|
||||
player = _G.minetest.get_player_by_name(players[1])
|
||||
self.label("-> " .. players[1])
|
||||
|
||||
spos = self.spawnpos(); spos.y=spos.y+1;
|
||||
|
||||
canvasn = "wool:white"
|
||||
reset_canvas = function()
|
||||
for i = 1, size do
|
||||
for j = 1, size do
|
||||
minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn})
|
||||
end
|
||||
end
|
||||
end
|
||||
reset_canvas()
|
||||
|
||||
save_image = function()
|
||||
local ret = {};
|
||||
for i = 1, size do
|
||||
for j = 1, size do
|
||||
local nname = string.sub(minetest.get_node({x=spos.x +i , y = spos.y + j, z = spos.z }).name,6)
|
||||
local pcolor = invcolors[nname] or 1;
|
||||
ret[#ret+1]= string.char(96+pcolor)
|
||||
end
|
||||
end
|
||||
return table.concat(ret,"")
|
||||
end
|
||||
|
||||
load_image = function(image)
|
||||
if not image then return end
|
||||
local ret = {}; local k = 0;
|
||||
for i = 1, size do
|
||||
for j = 1, size do
|
||||
k=k+1;
|
||||
local pcolor = colors[string.byte(image,k)-96] or "black";
|
||||
minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = "wool:"..pcolor})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--draw buttons
|
||||
for i = 1,#colors do
|
||||
minetest.set_node({x=spos.x +i , y = spos.y , z = spos.z },{name = "wool:"..colors[i]})
|
||||
end
|
||||
|
||||
minetest.set_node({x=spos.x +1 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_83"})
|
||||
minetest.set_node({x=spos.x +2 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_76"})
|
||||
|
||||
|
||||
vn = {x=0,y=0,z=1};
|
||||
T0 = {x=spos.x+0.5,y=spos.y+0.5,z=spos.z-0.5*vn.z};
|
||||
|
||||
get_intersect = function(vn, T0, p, v)
|
||||
local a = (T0.x-p.x)*vn.x + (T0.y-p.y)*vn.y + (T0.z-p.z)*vn.z;
|
||||
local b = vn.x*v.x + vn.y*v.y + vn.z*v.z
|
||||
if b<=0 then return nil end
|
||||
if a<=0 then return nil end
|
||||
local t = a / b
|
||||
return {x = p.x+v.x*t, y= p.y+v.y*t, z = p.z+v.z*t}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if player:get_player_control().LMB then -- player interacts with 'virtual canvas gui'
|
||||
local v = player:get_look_dir();
|
||||
local p = player:get_pos(); p.y = p.y + 1.5
|
||||
local c = get_intersect(vn,T0,p,v);
|
||||
if c then
|
||||
|
||||
local x = c.x - T0.x; local y = c.y - T0.y
|
||||
if x>0 and x<size and y>-2 and y<size then
|
||||
if y>0 then -- above: painting
|
||||
c.z = c.z+0.5
|
||||
minetest.set_node(c, {name = "wool:" .. colors[color]})
|
||||
elseif y>-1 then -- color selection
|
||||
x = 1+math.floor(x)
|
||||
if colors[x] then
|
||||
color = x;
|
||||
self.label(colors[x])
|
||||
end
|
||||
else -- save,load button row
|
||||
x = 1+math.floor(x)
|
||||
if x==1 then
|
||||
self.label("SAVED.")
|
||||
book.write(1,"ROBOT_IMAGE",save_image())
|
||||
elseif x==2 then
|
||||
local _,image = book.read(1)
|
||||
load_image(image);
|
||||
self.label("LOADED.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
101
scripts/games/sliding_puzzle_game.lua
Normal file
@ -0,0 +1,101 @@
|
||||
-- sliding unscramble game by rnd, made in 20 minutes
|
||||
if not init then
|
||||
reward = "default:gold_ingot"
|
||||
size = 3;
|
||||
|
||||
init = true
|
||||
spos = self.spawnpos(); spos.y = spos.y + 1
|
||||
board = {};
|
||||
local players = find_player(4);
|
||||
if not players then say("#sliding puzzle game: no players") self.remove() end
|
||||
name = players[1];
|
||||
|
||||
minetest.chat_send_player(name, "#SLIDING GAME: try to sort numbers in increasing order, starting from top left")
|
||||
|
||||
create_board = function(n)
|
||||
local k = 0;
|
||||
local ret = scramble(n*n, os.time())
|
||||
for i = 1,n do
|
||||
board[i]={};
|
||||
for j = 1,n do
|
||||
k=k+1
|
||||
board[i][j]=7+ret[k] -- 7 numbers, 82 letters
|
||||
end
|
||||
end
|
||||
board[math.random(n)][math.random(n)] = 0
|
||||
end
|
||||
|
||||
render_board = function()
|
||||
local n = #board;
|
||||
for i = 1,n do
|
||||
for j = 1,n do
|
||||
keyboard.set({x=spos.x+i, y = spos.y, z=spos.z+j}, board[i][j])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
check_score = function() -- check how many places are increasing in order, starting top left
|
||||
local n = #board;
|
||||
local cmax = 0;
|
||||
local score = 0;
|
||||
for j = n,1,-1 do
|
||||
for i = 1,n do
|
||||
local b = board[i][j];
|
||||
if b==0 or b<cmax then return score else score = score +1 cmax = b end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
find_hole = function(i,j)
|
||||
if board[i][j] == 0 then return i,j end
|
||||
if i>1 and board[i-1][j] == 0 then return i-1,j end
|
||||
if i<#board and board[i+1][j] == 0 then return i+1,j end
|
||||
if j>1 and board[i][j-1] == 0 then return i,j-1 end
|
||||
if j<#board and board[i][j+1] == 0 then return i,j+1 end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
scramble = function(n,seed)
|
||||
_G.math.randomseed(seed);
|
||||
local ret = {}; for i = 1,n do ret[i]=i end
|
||||
for j = n,2,-1 do
|
||||
local k = math.random(j);
|
||||
if k~=j then
|
||||
local tmp = ret[k]; ret[k] = ret[j]; ret[j] = tmp
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
create_board(size)
|
||||
render_board()
|
||||
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event and event.y == spos.y then
|
||||
local x = event.x-spos.x;
|
||||
local z = event.z-spos.z;
|
||||
if x<1 or x>size or z<1 or z>size then
|
||||
else
|
||||
local i,j = find_hole(x,z);
|
||||
if i then
|
||||
local tmp = board[x][z];
|
||||
keyboard.set({x=spos.x+x, y = spos.y, z=spos.z+z}, board[i][j])
|
||||
board[x][z] = board[i][j]
|
||||
board[i][j] = tmp;
|
||||
keyboard.set({x=spos.x+i, y = spos.y, z=spos.z+j}, tmp)
|
||||
end
|
||||
local score = check_score()
|
||||
self.label("score : " .. score)
|
||||
if score >= size*size-2 then
|
||||
minetest.chat_send_player(name, "CONGRATULATIONS! YOU SOLVED PUZZLE. REWARD WAS DROPPED ON TOP OF ROBOT.")
|
||||
pos = self.pos(); pos.y = pos.y+2;
|
||||
minetest.add_item(pos, _G.ItemStack(reward))
|
||||
self.remove()
|
||||
end
|
||||
end
|
||||
|
||||
end
|
189
scripts/games/sokoban_game.lua
Normal file
@ -0,0 +1,189 @@
|
||||
-- SOKOBAN GAME, by rnd, robots port
|
||||
|
||||
|
||||
if not sokoban then
|
||||
sokoban = {};
|
||||
local players = find_player(8);
|
||||
if not players then error("sokoban: no player near") end
|
||||
name = players[1];
|
||||
|
||||
-- self.show_form(name,
|
||||
-- "size[2,1.25]"..
|
||||
-- "label[0,0;SELECT LEVEL 1-90]"..
|
||||
-- "field[0.25,1;1,1;LVL;LEVEL;1]"..
|
||||
-- "button_exit[1.25,0.75;1,1;OK;OK]"
|
||||
-- )
|
||||
state = 1 -- will wait for form receive otherwise game play
|
||||
self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks")
|
||||
|
||||
player_ = puzzle.get_player(name); -- get player entity - player must be present in area
|
||||
player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}) -- reset player
|
||||
|
||||
|
||||
self.spam(1)
|
||||
sokoban.push_time = 0
|
||||
sokoban.blocks = 0;sokoban.level = 0; sokoban.moves=0;
|
||||
imax = 0; jmax = 0
|
||||
|
||||
sokoban.load=0;sokoban.playername =""; sokoban.pos = {};
|
||||
SOKOBAN_WALL = "moreblocks:cactus_brick"
|
||||
SOKOBAN_FLOOR = "default:silver_sandstone"
|
||||
SOKOBAN_GOAL = "default:aspen_tree"
|
||||
SOKOBAN_BOX = "basic_robot:buttonFFFFFF"
|
||||
|
||||
load_level = function(lvl)
|
||||
|
||||
local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1;
|
||||
sokoban.pos = pos;
|
||||
sokoban.playername = name
|
||||
|
||||
if lvl == nil then return end
|
||||
if lvl <0 or lvl >89 then return end
|
||||
|
||||
local file = _G.io.open(minetest.get_modpath("basic_robot").."\\scripts\\sokoban.txt","r")
|
||||
if not file then return end
|
||||
local str = ""; local s; local p = {x=pos.x,y=pos.y,z=pos.z}; local i,j;i=0;
|
||||
local lvl_found = false
|
||||
while str~= nil do
|
||||
str = file:read("*line");
|
||||
if str~=nil and str =="; "..lvl then lvl_found=true break end
|
||||
end
|
||||
if not lvl_found then file:close();return end
|
||||
|
||||
sokoban.blocks = 0;sokoban.level = lvl+1; sokoban.moves=0;
|
||||
imax=0; jmax = 0;
|
||||
while str~= nil do
|
||||
str = file:read("*line");
|
||||
if str~=nil then
|
||||
if string.sub(str,1,1)==";" then
|
||||
imax=i;
|
||||
file:close();
|
||||
player_:set_physics_override({jump=0})
|
||||
player_:set_eye_offset({x=0,y=20,z=0},{x=0,y=0,z=0});
|
||||
return
|
||||
end
|
||||
i=i+1;
|
||||
if string.len(str)>jmax then jmax = string.len(str) end -- determine max dimensions
|
||||
for j = 1,string.len(str) do
|
||||
p.x=pos.x+i;p.y=pos.y; p.z=pos.z+j; s=string.sub(str,j,j);
|
||||
p.y=p.y-1;
|
||||
if puzzle.get_node(p).name~=SOKOBAN_FLOOR then puzzle.set_node(p,{name=SOKOBAN_FLOOR}); end -- clear floor
|
||||
p.y=p.y+1;
|
||||
if s==" " and puzzle.get_node(p).name~="air" then puzzle.set_node(p,{name="air"}) end
|
||||
if s=="#" and puzzle.get_node(p).name~=SOKOBAN_WALL then puzzle.set_node(p,{name=SOKOBAN_WALL}) end
|
||||
if s=="$" then puzzle.set_node(p,{name=SOKOBAN_BOX});sokoban.blocks=sokoban.blocks+1 end
|
||||
if s=="." then p.y=p.y-1;puzzle.set_node(p,{name=SOKOBAN_GOAL}); p.y=p.y+1;puzzle.set_node(p,{name="air"}) end
|
||||
--starting position
|
||||
if s=="@" then
|
||||
player_:setpos({x=p.x,y=p.y-0.5,z=p.z}); -- move player to start position
|
||||
--p.y=p.y-1;puzzle.set_node(p,{name="default:glass"});
|
||||
puzzle.set_node(p,{name="air"})
|
||||
p.y=p.y+1;puzzle.set_node(p,{name="air"})
|
||||
--p.y=p.y+2;puzzle.set_node(p,{name="default:ladder"})
|
||||
end
|
||||
if s~="@" then p.y = pos.y+2;puzzle.set_node(p,{name="air"}); -- ceiling default:obsidian_glass
|
||||
else --p.y=pos.y+2;puzzle.set_node(p,{name="default:ladder"})
|
||||
end -- roof above to block jumps
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
file:close();
|
||||
end
|
||||
|
||||
clear_game = function()
|
||||
local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1;
|
||||
for i = 1, 20 do
|
||||
for j = 1,20 do
|
||||
local node = minetest.get_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}).name
|
||||
if node ~= "default:silver_sandstone" then minetest.set_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}, {name = "default:silver_sandstone"}) end
|
||||
node = minetest.get_node({x=pos.x+i,y=pos.y,z=pos.z+j}).name
|
||||
if node ~= "air" then minetest.set_node({x=pos.x+i,y=pos.y,z=pos.z+j}, {name = "air"}) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
if state == 1 then
|
||||
clear_game()
|
||||
load_level(20)
|
||||
state = 0
|
||||
self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks")
|
||||
else
|
||||
|
||||
local ppos = player_:getpos()
|
||||
if math.abs(ppos.y-sokoban.pos.y)~= 0.5 then minetest.chat_send_player(name,colorize("red", "SOKOBAN: " .. name .. " QUITS ! "));
|
||||
player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}); self.remove() end
|
||||
|
||||
event = keyboard.get();
|
||||
|
||||
if event then
|
||||
|
||||
local pname = event.puncher
|
||||
if pname ~= name then goto quit end
|
||||
local pos = {x=event.x, y = event.y, z = event.z};
|
||||
local p=player.getpos(pname);local q={x=pos.x,y=pos.y,z=pos.z}
|
||||
p.x=p.x-q.x;p.y=p.y-q.y;p.z=p.z-q.z
|
||||
if math.abs(p.y+0.5)>0 then goto quit end
|
||||
if math.abs(p.x)>math.abs(p.z) then -- determine push direction
|
||||
if p.z<-0.5 or p.z>0.5 or math.abs(p.x)>1.5 then goto quit end
|
||||
if p.x+q.x>q.x then q.x= q.x-1
|
||||
else q.x = q.x+1
|
||||
end
|
||||
else
|
||||
if p.x<-0.5 or p.x>0.5 or math.abs(p.z)>1.5 then goto quit end
|
||||
if p.z+q.z>q.z then q.z= q.z-1
|
||||
else q.z = q.z+1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if minetest.get_node(q).name=="air" then -- push crate
|
||||
sokoban.moves = sokoban.moves+1
|
||||
local old_infotext = minetest.get_meta(pos):get_string("infotext");
|
||||
minetest.set_node(pos,{name="air"})
|
||||
minetest.set_node(q,{name=SOKOBAN_BOX})
|
||||
minetest.sound_play("default_dig_dig_immediate", {pos=q,gain=1.0,max_hear_distance = 24,}) -- sound of pushing
|
||||
local meta = minetest.get_meta(q);
|
||||
q.y=q.y-1;
|
||||
if minetest.get_node(q).name==SOKOBAN_GOAL then
|
||||
if old_infotext~="GOAL REACHED" then
|
||||
sokoban.blocks = sokoban.blocks -1;
|
||||
end
|
||||
meta:set_string("infotext", "GOAL REACHED")
|
||||
else
|
||||
if old_infotext=="GOAL REACHED" then
|
||||
sokoban.blocks = sokoban.blocks +1
|
||||
end
|
||||
--meta:set_string("infotext", "push crate on top of goal block")
|
||||
end
|
||||
end
|
||||
|
||||
if sokoban.blocks~=0 then -- how many blocks left
|
||||
--say("move " .. sokoban.moves .. " : " ..sokoban.blocks .. " crates left ");
|
||||
else
|
||||
say("games: ".. name .. " just solved sokoban level ".. sokoban.level .. " in " .. sokoban.moves .. " moves.");
|
||||
player_:set_physics_override({jump=1})
|
||||
player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
|
||||
local player = _G.minetest.get_player_by_name(event.puncher);
|
||||
if player then
|
||||
local inv = player:get_inventory();
|
||||
inv:add_item("main",_G.ItemStack("skyblock:sokoban 4 "))
|
||||
end
|
||||
|
||||
local i,j;
|
||||
for i = 1,imax do
|
||||
for j=1,jmax do
|
||||
minetest.set_node({x= sokoban.pos.x+i,y=sokoban.pos.y,z=sokoban.pos.z+j}, {name = "air"}); -- clear level
|
||||
end
|
||||
end
|
||||
|
||||
sokoban.playername = ""; sokoban.level = 1
|
||||
end
|
||||
::quit::
|
||||
end
|
||||
end
|
101
scripts/games/switching_game.lua
Normal file
@ -0,0 +1,101 @@
|
||||
--[[
|
||||
SWITCHING GAME by rnd, 2018
|
||||
|
||||
lights:
|
||||
0110
|
||||
|
||||
switches, each one toggles certain lights like: s1 1001 (toggles light with 1)
|
||||
|
||||
PROBLEM:
|
||||
hit switches in correct order to turn on all lights
|
||||
|
||||
GENERATE RANDOM CHALLENGE:
|
||||
start with all lights on and apply random sequence of switches
|
||||
|
||||
TODO: instead of simply 0/1 switches have ones that advance +1 mod p (p can be say 3 or more)
|
||||
|
||||
|
||||
REMARKS: application of 2 different switches is commutative ( obvious, since just x->x+1 mod p)
|
||||
--]]
|
||||
if not init then
|
||||
|
||||
init = true
|
||||
numlights = 2;
|
||||
numswitches = 2;
|
||||
states = 10;
|
||||
|
||||
lights = {}; -- states of lights, initialy 1,1,...,1
|
||||
for i = 1, numlights do lights[i] = 0 end
|
||||
switches = {}
|
||||
|
||||
--switches = {{1,0,0,1},{1,1,1,1}};
|
||||
make_random_switches = function(lights, switches,count)
|
||||
for i = 1, count do
|
||||
switches[i] = {};
|
||||
local switch = switches[i];
|
||||
for j = 1, #lights do switch[j] = math.random(states)-1 end
|
||||
end
|
||||
end
|
||||
make_random_switches(lights,switches, numswitches)
|
||||
|
||||
|
||||
pos = self.spawnpos(); pos.x = pos.x + 1;-- pos.z = pos.z + 1
|
||||
|
||||
apply_switch = function(switches,lights,idx)
|
||||
local switch = switches[idx];
|
||||
for i = 1, #switch do
|
||||
local state = lights[i] + switch[i];
|
||||
if state >= states then state = state - states end
|
||||
lights[i] = state
|
||||
end
|
||||
end
|
||||
|
||||
randomize = function(switches, lights, steps) -- randomize lights
|
||||
for i = 1, steps do
|
||||
local idx = math.random(#switches);
|
||||
apply_switch(switches,lights,idx);
|
||||
end
|
||||
end
|
||||
|
||||
render_lights = function() for i = 1, #lights do keyboard.set({x=pos.x+i-1,y=pos.y+1, z=pos.z}, 7+lights[i]) end end
|
||||
render_switches = function(mode)
|
||||
if mode then
|
||||
for i = 1, #switches do keyboard.set({x=pos.x+i-1,y=pos.y, z=pos.z}, 1+i) end
|
||||
else
|
||||
for i = 1, #switches do keyboard.set({x=pos.x+i-1,y=pos.y, z=pos.z}, 0) end
|
||||
end
|
||||
end
|
||||
|
||||
check_lights = function()
|
||||
for i = 1, #lights do if lights[i] ~= 0 then return false end end
|
||||
return true
|
||||
end
|
||||
step = 0
|
||||
|
||||
randomize(switches,lights, math.min((#switches)^states,10000))
|
||||
if check_lights() then randomize(switches,lights, #switches + states) end
|
||||
|
||||
render_lights(); render_switches(true)
|
||||
|
||||
|
||||
self.label("GOAL OF GAME: punch buttons with numbers in correct order to turn all blocks to 0")
|
||||
|
||||
--self.label(serialize(switches))
|
||||
end
|
||||
|
||||
|
||||
event = keyboard.get()
|
||||
if event then
|
||||
local idx = event.x-pos.x+1;
|
||||
if event.y==pos.y and idx>=1 and idx <= #switches then
|
||||
apply_switch(switches, lights, idx)
|
||||
render_lights()
|
||||
step = step + 1
|
||||
if check_lights() then
|
||||
self.label("DONE IN " .. step .. " STEPS !")
|
||||
render_switches(false)
|
||||
else
|
||||
self.label("STEP " .. step)
|
||||
end
|
||||
end
|
||||
end
|
85
scripts/games/tank_bot.lua
Normal file
@ -0,0 +1,85 @@
|
||||
-- rnd 2017
|
||||
-- instructions: put 7 buttons around bot(top one left empty)
|
||||
-- clockwise: empty, green, yellow,blue, red, blue,yellow,green.
|
||||
-- those buttons serve as controls
|
||||
|
||||
if not s then
|
||||
name = self.name();
|
||||
direction = 1;
|
||||
s=0;
|
||||
self.label("TANK ROBOT. control with colored buttons")
|
||||
user=find_player(4); if user then user = user[1] end
|
||||
|
||||
speed = 7 + math.random(7);turn.angle(math.random(360));
|
||||
pitch = 0
|
||||
|
||||
gravity = 1+math.random(2);
|
||||
if user then
|
||||
say("TANK ROBOT, ready. ".. user .. " in control")
|
||||
else
|
||||
say("no player found nearby. deactivating"); self.remove()
|
||||
s=-1
|
||||
end
|
||||
pos = self.spawnpos();
|
||||
end
|
||||
|
||||
ppos = player.getpos(user); ppos.x=ppos.x-pos.x;ppos.y=ppos.y-pos.y;ppos.z=ppos.z-pos.z;
|
||||
if ppos.x^2+ppos.y^2+ppos.z^2>10 then
|
||||
local obj = _G.minetest.get_player_by_name(user);
|
||||
if obj then say("deserter " .. user .. " killed for abandoning the tank!") obj:set_hp(0) end
|
||||
self.remove()
|
||||
else
|
||||
local obj = _G.minetest.get_player_by_name(user);
|
||||
if obj then
|
||||
if obj:get_hp() == 0 then
|
||||
say("TANK DESTROYED!")
|
||||
self.remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if s == 0 then
|
||||
event = keyboard.get();
|
||||
if event and event.puncher==user then
|
||||
--self.label(event.x-pos.x .. " " .. event.y-pos.y .. " " .. event.z-pos.z .. " T " .. event.type)
|
||||
event.x = event.x-pos.x;event.y = event.y-pos.y;event.z = event.z-pos.z;
|
||||
if event.x == 0 and event.y == 0 and event.z == 1 then
|
||||
self.fire(speed, pitch,gravity)
|
||||
s=1;self.label("BOOM")
|
||||
_G.minetest.sound_play("tnt_explode",{pos = self.pos(), max_hear_distance = 256, gain = 1})
|
||||
elseif event.x == direction*1 and event.y == 0 and event.z == direction*1 then
|
||||
turn.angle(2)
|
||||
elseif event.x == -1*direction and event.y == 0 and event.z == 1*direction then
|
||||
turn.angle(-2)
|
||||
elseif event.x == 1*direction and event.y == 0 and event.z == 0 then
|
||||
turn.angle(40)
|
||||
elseif event.x == -1*direction and event.y == 0 and event.z == 0 then
|
||||
turn.angle(-40)
|
||||
elseif event.x == 1*direction and event.y == 0 and event.z == -1*direction then
|
||||
pitch = pitch + 5; if pitch> 85 then pitch = 85 end
|
||||
self.label("pitch " .. pitch)
|
||||
elseif event.x == -1*direction and event.y == 0 and event.z == -1*direction then
|
||||
pitch = pitch - 5; if pitch<-10 then pitch = -10 end
|
||||
self.label("pitch " .. pitch)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if s == 1 then
|
||||
local pos = self.fire_pos();
|
||||
if pos then
|
||||
self.label("HIT")
|
||||
msg = "";
|
||||
_G.minetest.sound_play("tnt_explode",{pos = pos, max_hear_distance = 256, gain = 1})
|
||||
|
||||
local objs=_G.minetest.get_objects_inside_radius(pos, 4);
|
||||
for _,obj in pairs(objs) do
|
||||
if obj:is_player() then
|
||||
obj:set_hp(0)
|
||||
msg = msg .. obj:get_player_name() .. " is dead, "
|
||||
end
|
||||
end
|
||||
s = 0
|
||||
if msg~="" then say(msg) end
|
||||
end
|
||||
end
|
112
scripts/graphics/painting.lua
Normal file
@ -0,0 +1,112 @@
|
||||
-- paint canvas by rnd, 2018
|
||||
if not init then
|
||||
colors = {
|
||||
"black","blue","brown","cyan","dark_green","dark_grey","green","grey",
|
||||
"magenta","orange","pink","red","violet","white","yellow"
|
||||
}
|
||||
invcolors = {}; for i = 1,#colors do invcolors[colors[i]] = i end
|
||||
|
||||
color = 1;
|
||||
size = 16;
|
||||
|
||||
init = true
|
||||
|
||||
local ent = _G.basic_robot.data[self.name()].obj:get_luaentity();
|
||||
ent.timestep = 0.5
|
||||
|
||||
players = find_player(5); if not players then self.remove() end
|
||||
player = _G.minetest.get_player_by_name(players[1])
|
||||
self.label("-> " .. players[1])
|
||||
|
||||
spos = self.spawnpos(); spos.y=spos.y+1;
|
||||
|
||||
canvasn = "wool:white"
|
||||
reset_canvas = function()
|
||||
for i = 1, size do
|
||||
for j = 1, size do
|
||||
minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn})
|
||||
end
|
||||
end
|
||||
end
|
||||
reset_canvas()
|
||||
|
||||
save_image = function()
|
||||
local ret = {};
|
||||
for i = 1, size do
|
||||
for j = 1, size do
|
||||
local nname = string.sub(minetest.get_node({x=spos.x +i , y = spos.y + j, z = spos.z }).name,6)
|
||||
local pcolor = invcolors[nname] or 1;
|
||||
ret[#ret+1]= string.char(96+pcolor)
|
||||
end
|
||||
end
|
||||
return table.concat(ret,"")
|
||||
end
|
||||
|
||||
load_image = function(image)
|
||||
if not image then return end
|
||||
local ret = {}; local k = 0;
|
||||
for i = 1, size do
|
||||
for j = 1, size do
|
||||
k=k+1;
|
||||
local pcolor = colors[string.byte(image,k)-96] or "black";
|
||||
minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = "wool:"..pcolor})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--draw buttons
|
||||
for i = 1,#colors do
|
||||
minetest.set_node({x=spos.x +i , y = spos.y , z = spos.z },{name = "wool:"..colors[i]})
|
||||
end
|
||||
|
||||
minetest.set_node({x=spos.x +1 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_83"})
|
||||
minetest.set_node({x=spos.x +2 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_76"})
|
||||
|
||||
|
||||
vn = {x=0,y=0,z=1};
|
||||
T0 = {x=spos.x+0.5,y=spos.y+0.5,z=spos.z-0.5*vn.z};
|
||||
|
||||
get_intersect = function(vn, T0, p, v)
|
||||
local a = (T0.x-p.x)*vn.x + (T0.y-p.y)*vn.y + (T0.z-p.z)*vn.z;
|
||||
local b = vn.x*v.x + vn.y*v.y + vn.z*v.z
|
||||
if b<=0 then return nil end
|
||||
if a<=0 then return nil end
|
||||
local t = a / b
|
||||
return {x = p.x+v.x*t, y= p.y+v.y*t, z = p.z+v.z*t}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if player:get_player_control().LMB then -- player interacts with 'virtual canvas gui'
|
||||
local v = player:get_look_dir();
|
||||
local p = player:get_pos(); p.y = p.y + 1.5
|
||||
local c = get_intersect(vn,T0,p,v);
|
||||
if c then
|
||||
|
||||
local x = c.x - T0.x; local y = c.y - T0.y
|
||||
if x>0 and x<size and y>-2 and y<size then
|
||||
if y>0 then -- above: painting
|
||||
c.z = c.z+0.5
|
||||
minetest.set_node(c, {name = "wool:" .. colors[color]})
|
||||
elseif y>-1 then -- color selection
|
||||
x = 1+math.floor(x)
|
||||
if colors[x] then
|
||||
color = x;
|
||||
self.label(colors[x])
|
||||
end
|
||||
else -- save,load button row
|
||||
x = 1+math.floor(x)
|
||||
if x==1 then
|
||||
self.label("SAVED.")
|
||||
book.write(1,"ROBOT_IMAGE",save_image())
|
||||
elseif x==2 then
|
||||
local _,image = book.read(1)
|
||||
load_image(image);
|
||||
self.label("LOADED.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
58
scripts/graphics/painting_import.lua
Normal file
@ -0,0 +1,58 @@
|
||||
-- painting import from minetest 'painting mod' to robot canvas by rnd
|
||||
-- stand near image and run "get_texture()" command in remote control
|
||||
|
||||
if not init then
|
||||
self.label("PAINTING IMPORTER")
|
||||
pname = "rnd"
|
||||
player = minetest.get_player_by_name(pname)
|
||||
|
||||
get_texture = function()
|
||||
|
||||
|
||||
local pos = player:get_pos(); local radius = 2
|
||||
local objs = minetest.get_objects_inside_radius(pos, radius)
|
||||
local obj = {};
|
||||
|
||||
local ret = {};
|
||||
for i=1,#objs do
|
||||
if not objs[i]:is_player() then obj = objs[i] break end
|
||||
end
|
||||
|
||||
if obj then
|
||||
local tex = obj:get_properties().textures
|
||||
local out = tex[1] or ""
|
||||
if string.sub(out,1,9) == "[combine:" then
|
||||
local pcolors = {"black","blue","brown","cyan","darkgreen","darkgrey","green","grey",
|
||||
"magenta","orange","pink","red","violet","white","yellow"}
|
||||
local ipcolors = {}; for i = 1,#pcolors do ipcolors[pcolors[i]] = i end
|
||||
|
||||
local ret = {};
|
||||
local i =0; local j = 1; local k = 0; local size = 16;
|
||||
--ret[1] = {}
|
||||
for word in out:gmatch("=(%a+)%.png") do
|
||||
ret[#ret+1] = string.char(96 + (ipcolors[word] or 1))
|
||||
end
|
||||
|
||||
local rret = {};
|
||||
for i = 1, size do rret[i] = {} for j = 1,size do rret[i][j] = 0 end end
|
||||
|
||||
k = 0 -- rotate 90 right
|
||||
for j = 1,size do
|
||||
for i = size,1,-1 do
|
||||
k = k + 1
|
||||
rret[size-i+1][size-j+1] = ret[k]
|
||||
end
|
||||
end
|
||||
|
||||
ret = {}; for i = 1, size do for j = 1, size do ret[#ret+1]= rret[i][j] end end -- write back
|
||||
|
||||
out = table.concat(ret,"")
|
||||
book.write(1,"IMPORTED_PAINTING", out)
|
||||
minetest.chat_send_player(pname, "PAINTING FOUND, saved in robot library in book 1.")
|
||||
end
|
||||
else return "empty"
|
||||
end
|
||||
end
|
||||
|
||||
init = true
|
||||
end
|
108
scripts/gui/craft_guide.lua
Normal file
@ -0,0 +1,108 @@
|
||||
-- ROBOT craft guide by rnd, 2017
|
||||
if not list then
|
||||
|
||||
tname = "rnd";
|
||||
list = {};
|
||||
tmplist = _G.minetest.registered_items;
|
||||
for k,v in pairs(tmplist) do
|
||||
local texture = v.inventory_image or "";
|
||||
if texture=="" and v.tiles then texture = v.tiles[1] or "" end
|
||||
if (not v.groups.not_in_craft_guide or v.groups.not_in_craft_guide == 0) and type(texture)=="string" and texture~="" then
|
||||
list[#list+1] = {_G.minetest.formspec_escape(k),_G.minetest.formspec_escape(v.description),_G.minetest.formspec_escape(texture)}; -- v.inventory_image, k, v.description
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
idx = 1; n = 35; row = 6; size = 1.25;
|
||||
filter = "" item = "" recipeid = 1
|
||||
filterlist = {}; for i = 1,#list do filterlist[i] = i end
|
||||
|
||||
get_texture = function(ritem)
|
||||
local v = _G.minetest.registered_items[ritem]; if not v then return "" end
|
||||
local texture = v.inventory_image or "";
|
||||
if texture=="" and v.tiles then texture = v.tiles[1] or "" end
|
||||
if type(texture)~="string" then return "" end
|
||||
return texture
|
||||
end
|
||||
|
||||
get_form = function()
|
||||
local form = "size[7.5,8.5]";
|
||||
local x,y,i; local idxt = idx+n; if idxt > #filterlist then idxt = #filterlist end
|
||||
for i = idx, idxt do
|
||||
local id = filterlist[i];
|
||||
if list[id] and list[id][3] then
|
||||
x = ((i-idx) % row)
|
||||
y = (i-idx-x)/row;
|
||||
form = form .. "image_button[".. x*size ..",".. y*size+0.75 .. ";"..size.."," .. size .. ";" .. list[id][3] ..";".."item"..";".. list[id][1] .."]"
|
||||
end
|
||||
end
|
||||
form = form .. "textarea[0.25,0;2,0.75;filter;filter;"..filter .. "]" .. "button[2.,0;1,0.5;search;search]"..
|
||||
"button[5.5,0;1,0.5;prev;PREV]" .. "button[6.5,0;1,0.5;next;NEXT]" .. "label[4,0;".. idx .. "-"..idxt .. "/" .. #filterlist.."]";
|
||||
return form
|
||||
end
|
||||
|
||||
get_recipe = function()
|
||||
local form = "size[7.5,8.5]";
|
||||
local recipes = _G.minetest.get_all_craft_recipes(item); if not recipes then return end;
|
||||
local recipe = recipes[recipeid]; if not recipe then return end
|
||||
local items = recipe.items
|
||||
local x,y,i;
|
||||
for i = 0, 8 do
|
||||
local ritem = items[i+1] or ""; local sritem = "";
|
||||
local j = string.find(ritem,":"); if j then sritem = string.sub(ritem,j+1) end; --ritem = _G.minetest.formspec_escape(ritem);
|
||||
x = (i % 3)
|
||||
y = (i-x)/3;
|
||||
form = form .. "image_button[".. x*size ..",".. y*size+0.75 .. ";"..size.."," .. size .. ";" .. get_texture(ritem) ..";".."item"..";".. sritem .."]"
|
||||
end
|
||||
form = form .. "textarea[0.25,0;2,0.75;recipeid;recipeid ".. #recipes .. ";"..recipeid .. "]" .. "button[2.,0;1,0.5;go;go]"..
|
||||
"label[3,0;" .. item .. "]" .. "button[6.5,0;1,0.5;back;BACK]" ;
|
||||
return form
|
||||
end
|
||||
|
||||
s=0
|
||||
end
|
||||
|
||||
if s==0 then
|
||||
local p = find_player(4); s = 1
|
||||
if p then
|
||||
self.show_form(p[1],get_form())
|
||||
else
|
||||
self.remove()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
sender,fields = self.read_form()
|
||||
if sender then
|
||||
|
||||
if fields.search then
|
||||
filter = fields.filter or ""
|
||||
filterlist = {};
|
||||
for i = 1,#list do
|
||||
if string.find(list[i][1],filter) then filterlist[#filterlist+1] = i end
|
||||
end
|
||||
idx=1;self.show_form(sender,get_form())
|
||||
|
||||
elseif fields.prev then
|
||||
idx = idx - n; if idx<1 then idx =#filterlist-n end
|
||||
self.show_form(sender,get_form())
|
||||
elseif fields.next then
|
||||
idx = idx+n; if idx > #filterlist then idx = 1 end
|
||||
self.show_form(sender,get_form())
|
||||
elseif fields.back then
|
||||
self.show_form(sender,get_form())
|
||||
elseif fields.recipeid then
|
||||
recipeid = tonumber(fields.recipeid) or 1;
|
||||
self.show_form(sender,get_recipe())
|
||||
elseif fields.item then
|
||||
item = fields.item;
|
||||
local recipes = _G.minetest.get_all_craft_recipes(item);
|
||||
local count = 0; if recipes then count = #recipes end
|
||||
if count>0 then
|
||||
recipeid = 1
|
||||
self.show_form(sender,get_recipe() or "")
|
||||
end
|
||||
elseif fields.quit then
|
||||
self.remove()
|
||||
end
|
||||
end
|
57
scripts/gui/file_manager.lua
Normal file
@ -0,0 +1,57 @@
|
||||
-- file 'manager' by rnd
|
||||
|
||||
if not init then
|
||||
fmver = "2018/12/09"
|
||||
local players = find_player(4);
|
||||
if not players then self.remove() end
|
||||
pname = players[1];
|
||||
size = 8;
|
||||
vsize = 6.5;
|
||||
|
||||
path = "/";
|
||||
pathlist = {}
|
||||
folderlist = {};
|
||||
filelist = {};
|
||||
|
||||
render_page = function()
|
||||
local foldlist = minetest.get_dir_list(path,true) -- only folders
|
||||
if foldlist then folderlist = foldlist else folderlist = {} end
|
||||
for i = 1,#foldlist do foldlist[i] = "*"..foldlist[i] end
|
||||
foldlist[#foldlist+1] = "*.."
|
||||
local fillist = minetest.get_dir_list(path,false)
|
||||
if fillist then filelist = fillist else filelist = {} end
|
||||
local content = table.concat(folderlist,",") .. ",-------------------," .. table.concat(filelist,",")
|
||||
return "size[" .. size .. "," .. size .. "] label[0,-0.25;ROBOT FILE MANAGER " .. fmver .. " by rnd\nPATH " .. minetest.formspec_escape(path) .. "] textlist[-0.25,0.75;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]";
|
||||
end
|
||||
|
||||
page = {}
|
||||
self.show_form(pname,render_page())
|
||||
init = true
|
||||
self.read_form()
|
||||
end
|
||||
|
||||
sender,fields = self.read_form()
|
||||
if sender then
|
||||
local fsel = fields.wiki;
|
||||
if fsel and string.sub(fsel,1,3) == "DCL" then
|
||||
local sel = tonumber(string.sub(fsel,5)) or 1; -- selected line
|
||||
local fold = folderlist[sel];
|
||||
if fold and string.sub(fold,1,1) == "*" then
|
||||
if fold == "*.." then -- go back
|
||||
if #pathlist>0 then
|
||||
local i = string.len(pathlist[#pathlist]);
|
||||
if i>0 then
|
||||
pathlist[#pathlist] = nil
|
||||
path = string.sub(path,1,-i-2);
|
||||
end
|
||||
end
|
||||
else
|
||||
pathlist[#pathlist+1] = string.sub(fold,2)
|
||||
path = path .. "/".. pathlist[#pathlist]
|
||||
end
|
||||
|
||||
self.show_form(pname,render_page())
|
||||
end
|
||||
end
|
||||
--self.label(fsel);
|
||||
end
|
57
scripts/gui/gui_2player_coop_edit.lua
Normal file
@ -0,0 +1,57 @@
|
||||
-- gui demo by rnd
|
||||
-- 2 player cooperative editing of image with 2 colors
|
||||
|
||||
if not init then
|
||||
_G.basic_robot.data[self.name()].obj:get_luaentity().timestep = 0.25
|
||||
init = true
|
||||
name = "rnd"
|
||||
otherrobotname = "rnd2" -- on other robot do name of this robot
|
||||
drawcolor = 3; -- other robot has reversed colors
|
||||
otherdrawcolor = 4
|
||||
|
||||
color = {"black","white","blue","green"}
|
||||
data = {};
|
||||
n = 20;
|
||||
|
||||
for i = 1,n do
|
||||
data[i]={};
|
||||
--local y = math.floor(f(i));
|
||||
for j = 1,n do
|
||||
data[i][j] = 1--(n-j>y) and 2 or 1
|
||||
end
|
||||
end
|
||||
|
||||
get_form = function()
|
||||
local form = "size[10,10] "; ret = {};
|
||||
|
||||
for i = 1,n do
|
||||
for j = 1,n do
|
||||
ret[#ret+1] = "image_button["..((i-1)*0.5)..","..((j-1)*0.5)..";0.7,0.63;wool_"..color[data[i][j]]..".png;"..((i-1)*n+j-1) .. ";] "
|
||||
end
|
||||
end
|
||||
return form .. table.concat(ret,"")
|
||||
end
|
||||
|
||||
self.show_form(name,get_form())
|
||||
self.read_form()
|
||||
end
|
||||
|
||||
sender,mail = self.read_mail()
|
||||
if mail then
|
||||
local x = mail[1]; local y = mail[2];
|
||||
if data[x][y]==1 then data[x][y] = otherdrawcolor else data[x][y] = 1 end
|
||||
self.show_form(name,get_form())
|
||||
end
|
||||
|
||||
sender,fields = self.read_form()
|
||||
if fields then
|
||||
if fields.quit then self.remove() end
|
||||
local sel = 0;
|
||||
for k,v in pairs(fields) do
|
||||
if k ~="quit" then sel = tonumber(k); break end
|
||||
end
|
||||
local x = 1+math.floor(sel/n); local y = 1+sel % n;
|
||||
if data[x][y]==1 then data[x][y] = drawcolor else data[x][y] = 1 end
|
||||
self.send_mail(otherrobotname,{x,y})
|
||||
self.show_form(name,get_form())
|
||||
end
|
94
scripts/gui/gui_deposit_withdraw_demo.lua
Normal file
@ -0,0 +1,94 @@
|
||||
|
||||
if not init then init = true
|
||||
|
||||
deposit = function(pname)
|
||||
_, text = book.read(1);
|
||||
local data = deserialize(text) or {};
|
||||
|
||||
local player = _G.minetest.get_player_by_name(pname)
|
||||
local inv = player:get_inventory();
|
||||
local pstack = inv:get_stack("main", 1);
|
||||
local iname = pstack:to_string()
|
||||
|
||||
|
||||
item, count = _G.string.match(iname,"(%S+)%s?(%d*)");
|
||||
item = item or "";if item == "" then return end
|
||||
|
||||
count = tonumber(count) or 1;
|
||||
|
||||
--say("item " .. item .. ", count " .. count)
|
||||
|
||||
data[pname] = data[pname] or {};
|
||||
local pdata = data[pname];
|
||||
|
||||
pdata[item] = pdata[item] or {};
|
||||
|
||||
local t = minetest.get_gametime()
|
||||
pdata[item][1] = (pdata[item][1] or 0) + count
|
||||
pdata[item][2] = t;
|
||||
|
||||
inv:set_stack("main", 1, _G.ItemStack(""))
|
||||
|
||||
book.write(1,"",serialize(data))
|
||||
local form = "size [5,5] label[0,0; You deposited " .. item .. "( " .. count .. " pieces)]"
|
||||
self.show_form(pname, form)
|
||||
--say(pname .. " deposited " .. item .. "( " .. count .. " pieces) ")
|
||||
end
|
||||
|
||||
check = function(pname)
|
||||
_, text = book.read(1);
|
||||
local data = deserialize(text) or {};
|
||||
data[pname] = data[pname] or {};
|
||||
|
||||
--say(serialize(data[pname]))
|
||||
return serialize(data[pname])
|
||||
end
|
||||
|
||||
withdraw = function(pname)
|
||||
_, text = book.read(1);
|
||||
local data = deserialize(text) or {};
|
||||
data[pname] = data[pname] or {};
|
||||
|
||||
local player = _G.minetest.get_player_by_name(pname)
|
||||
local inv = player:get_inventory();
|
||||
local pdata = data[pname]
|
||||
local t0 = minetest.get_gametime();
|
||||
|
||||
for k,v in pairs(pdata) do
|
||||
local t = t0 - v[2]; -- time since last deposit
|
||||
local a = (1+interests/100)^t;
|
||||
self.label("deposited time " .. t .. ", deposited quantity " .. v[1] .. ", new quantity : " .. math.floor(a*tonumber(v[1])) )
|
||||
inv:add_item("main", _G.ItemStack(k .. " " .. math.floor(a*tonumber(v[1]))) )
|
||||
end
|
||||
data[pname] = nil;
|
||||
book.write(1,"",serialize(data))
|
||||
end
|
||||
|
||||
|
||||
function show(pname)
|
||||
local itemlist = check(pname)
|
||||
local form = "size [5,5] button[0,0;2,1;DEPOSIT;DEPOSIT] button[0,1;2,1;WITHDRAW;WITHDRAW]" ..
|
||||
"textarea[0,2.5;6,3;STORAGE;YOUR DEPOSITS;" .. minetest.formspec_escape(itemlist) .. "]"
|
||||
self.show_form(pname, form)
|
||||
end
|
||||
|
||||
|
||||
interests = 5; -- interest rate per second (demo)
|
||||
local players = find_player(4)
|
||||
if not players then self.remove() end
|
||||
show(players[1])
|
||||
|
||||
end
|
||||
|
||||
sender,fields = self.read_form()
|
||||
if sender then
|
||||
if fields.DEPOSIT then
|
||||
deposit(sender)
|
||||
show(sender)
|
||||
elseif fields.WITHDRAW then
|
||||
withdraw(sender)
|
||||
show(sender)
|
||||
end
|
||||
|
||||
--say(sender .. " clicked " .. serialize(fields))
|
||||
end
|
46
scripts/gui/gui_interact_demo_board.lua
Normal file
@ -0,0 +1,46 @@
|
||||
-- gui demo by rnd
|
||||
|
||||
if not init then
|
||||
init = true
|
||||
name = "rnd"
|
||||
color = {"white","black"}
|
||||
data = {};
|
||||
n = 20;
|
||||
|
||||
f = function(x) return 7*(1+math.sin(x/2)) end
|
||||
|
||||
for i = 1,n do
|
||||
data[i]={};
|
||||
local y = math.floor(f(i));
|
||||
for j = 1,n do
|
||||
data[i][j] = (n-j>y) and 2 or 1
|
||||
end
|
||||
end
|
||||
|
||||
get_form = function()
|
||||
local form = "size[10,10] "; ret = {};
|
||||
|
||||
for i = 1,n do
|
||||
for j = 1,n do
|
||||
ret[#ret+1] = "image_button["..((i-1)*0.5)..","..((j-1)*0.5)..";0.7,0.63;wool_"..color[data[i][j]]..".png;"..((i-1)*n+j-1) .. ";] "
|
||||
end
|
||||
end
|
||||
return form .. table.concat(ret,"")
|
||||
end
|
||||
|
||||
self.show_form(name,get_form())
|
||||
self.read_form()
|
||||
end
|
||||
|
||||
sender,fields = self.read_form()
|
||||
if fields then
|
||||
if fields.quit then self.remove() end
|
||||
local sel = 0;
|
||||
for k,v in pairs(fields) do
|
||||
if k ~="quit" then sel = tonumber(k); break end
|
||||
end
|
||||
local x = 1+math.floor(sel/n); local y = 1+sel % n;
|
||||
data[x][y] = 3 - data[x][y]
|
||||
--self.label(x .. " " .. y)
|
||||
self.show_form(name,get_form())
|
||||
end
|
68
scripts/gui/wiki.lua
Normal file
@ -0,0 +1,68 @@
|
||||
-- ROBOT WIKI by rnd
|
||||
-- to do: ability for multiple links in 1 line
|
||||
|
||||
|
||||
if not init then
|
||||
_G.basic_robot.data[self.name()].obj:get_luaentity().timestep = 0.1
|
||||
local players = find_player(4);
|
||||
if not players then self.remove() end
|
||||
pname = players[1];
|
||||
size = 8;
|
||||
vsize = 8;
|
||||
linesize = 60; -- break up longer lines
|
||||
|
||||
wiki = { -- example of wiki pages
|
||||
["MAIN PAGE"] =
|
||||
{
|
||||
"-- WIKI CONTENTS -- ", "",
|
||||
"double click link marked with [] or press enter while selected.","",
|
||||
"[Viewing wiki]",
|
||||
"[Editing wiki]"
|
||||
},
|
||||
|
||||
["Viewing wiki"] = {
|
||||
"back to [MAIN PAGE]","",
|
||||
" ** Viewing wiki",
|
||||
"double click link marked with [] or press enter while selected."
|
||||
},
|
||||
|
||||
["Editing wiki"] = {
|
||||
"back to [MAIN PAGE]","",
|
||||
" ** Editing wiki",
|
||||
"Edit wiki table and write in entries"
|
||||
}
|
||||
}
|
||||
|
||||
for k,v in pairs(wiki) do
|
||||
local pages = wiki[k]; for i = 1,#pages do pages[i] = minetest.formspec_escape(pages[i]) end
|
||||
end
|
||||
|
||||
|
||||
current = "MAIN PAGE";
|
||||
|
||||
render_page = function()
|
||||
local content = table.concat(wiki[current],",")
|
||||
return "size[" .. size .. "," .. size .. "] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]";
|
||||
end
|
||||
|
||||
page = {}
|
||||
self.show_form(pname,render_page())
|
||||
init = true
|
||||
end
|
||||
|
||||
sender,fields = self.read_form()
|
||||
if sender then
|
||||
local fsel = fields.wiki;
|
||||
if fsel and string.sub(fsel,1,3) == "DCL" then
|
||||
local sel = tonumber(string.sub(fsel,5)) or 1; -- selected line
|
||||
local address = current or "main";
|
||||
local pages = wiki[address];
|
||||
|
||||
local link = _G.string.match(pages[sel] or "", "\\%[([%w%s]+)\\%]")
|
||||
if wiki[link] then
|
||||
current = link;
|
||||
self.show_form(pname,render_page())
|
||||
--robot_show_help(name)
|
||||
end
|
||||
end
|
||||
end
|
44
scripts/http/http_demo.lua
Normal file
@ -0,0 +1,44 @@
|
||||
-- HOW TO DOWNLOAD WEBPAGES/FILES USING ROBOT
|
||||
|
||||
if not fetch then
|
||||
fetch = _G.basic_robot.http_api.fetch;
|
||||
|
||||
-- WARNING: this is run outside pcall and can crash server if errors!
|
||||
result = function(res) -- res.data is string containing result
|
||||
if not res.succeeded then self.label("#ERROR: data couldn't be downloaded :\n" .. minetest.serialize(res) ) return end
|
||||
if res.data then self.label(res.data) end
|
||||
end
|
||||
|
||||
fetch({url = "http://185.85.149.248/FILES/minetest/README.txt", timeout = 30}, result)
|
||||
end
|
||||
|
||||
--[[
|
||||
from https://github.com/minetest/minetest/blob/master/doc/lua_api.txt :
|
||||
|
||||
`HTTPRequest` definition
|
||||
------------------------
|
||||
|
||||
Used by `HTTPApiTable.fetch` and `HTTPApiTable.fetch_async`.
|
||||
|
||||
{
|
||||
url = "http://example.org",
|
||||
timeout = 10,
|
||||
-- ^ Timeout for connection in seconds. Default is 3 seconds.
|
||||
post_data = "Raw POST request data string" OR {field1 = "data1", field2 = "data2"},
|
||||
-- ^ Optional, if specified a POST request with post_data is performed.
|
||||
-- ^ Accepts both a string and a table. If a table is specified, encodes
|
||||
-- ^ table as x-www-form-urlencoded key-value pairs.
|
||||
-- ^ If post_data ist not specified, a GET request is performed instead.
|
||||
user_agent = "ExampleUserAgent",
|
||||
-- ^ Optional, if specified replaces the default minetest user agent with
|
||||
-- ^ given string.
|
||||
extra_headers = { "Accept-Language: en-us", "Accept-Charset: utf-8" },
|
||||
-- ^ Optional, if specified adds additional headers to the HTTP request.
|
||||
-- ^ You must make sure that the header strings follow HTTP specification
|
||||
-- ^ ("Key: Value").
|
||||
multipart = boolean
|
||||
-- ^ Optional, if true performs a multipart HTTP request.
|
||||
-- ^ Default is false.
|
||||
}
|
||||
|
||||
--]]
|
68
scripts/http/webcommands/minetest_webcommands.js
Normal file
@ -0,0 +1,68 @@
|
||||
// listen to web request and pass it to minetest or back to web, rnd 2018
|
||||
|
||||
// INSTRUCTIONS. url options:
|
||||
// 1./mtmsg/msg will store msg as message received from minetest ( minetest_message). note that msg cant contain spaces or newlines
|
||||
// 2./getwebmsg/ will reply = IP + ' ' + webmessage
|
||||
// 3./webmsg/msg will store message as webmessage
|
||||
// 4./getmtmsg will reply with minetest_message
|
||||
|
||||
|
||||
// NOTES: 1. avoids the need to deal with POST nastyness and complications like
|
||||
// https://stackoverflow.com/questions/4295782/how-do-you-extract-post-data-in-node-js
|
||||
|
||||
const http = require('http');
|
||||
|
||||
|
||||
const hostname = '192.168.0.10' //write address of your router (it will be accessible from internet then if you open firewall for nodejs process)
|
||||
const port = 80;
|
||||
|
||||
var webreq = "" // message from web
|
||||
var mtreq = "" // message from mt
|
||||
|
||||
// take request from web and pass it to minetest
|
||||
const server = http.createServer((req, res) => {
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
|
||||
var msg = req.url;
|
||||
if (msg == '/favicon.ico') return // prevent passing this as request
|
||||
|
||||
|
||||
var pos = msg.indexOf("/",1); // gets the 2nd / in /part1/part2/...
|
||||
var cmd = msg.substring(1,pos);
|
||||
var response = ""
|
||||
var ip = req.connection.remoteAddress;
|
||||
|
||||
switch(cmd)
|
||||
{
|
||||
case "mtmsg":
|
||||
response = msg.substring(pos+1);
|
||||
mtreq = response
|
||||
break
|
||||
case "getmtmsg":
|
||||
response = mtreq; mtreq = ''
|
||||
break
|
||||
case "getwebmsg":
|
||||
response = webreq; webreq = ''
|
||||
break
|
||||
case "webmsg":
|
||||
webreq = ip + ' ' + msg.substring(pos+1);
|
||||
response = 'request received: ' + webreq + '\nuse /getmtmsg to view response from minetest'
|
||||
break
|
||||
default:
|
||||
response = 'INSTRUCTIONS. url options:\n'+
|
||||
'1./mtmsg/msg will store msg as message received from minetest ( minetest_message). note that msg cant contain spaces or newlines\n'+
|
||||
'2./getwebmsg/ will reply = IP + " " + webmessage\n'+
|
||||
'3./webmsg/msg will store message as webmessage\n'+
|
||||
'4./getmtmsg will reply with minetest_message\n'
|
||||
}
|
||||
|
||||
if (msg!='' && cmd != 'getwebmsg') console.log('ip ' + ip + ', msg ' + msg)
|
||||
res.write(response); res.end()
|
||||
return
|
||||
});
|
||||
|
||||
// make server listen
|
||||
server.listen(port, hostname, () => {
|
||||
console.log(`Server running at http://${hostname}:${port}/`);
|
||||
});
|
71
scripts/http/webcommands/webcommands.lua
Normal file
@ -0,0 +1,71 @@
|
||||
-- webcommands : access url like: 192.168.0.10/hello_world
|
||||
|
||||
--[[ instructions:
|
||||
1.download nodejs server from
|
||||
https://nodejs.org/dist/v10.14.2/node-v10.14.2-win-x86.zip
|
||||
2. run nodejs server using run.bat
|
||||
:loop
|
||||
node $path/minetest_webcommands.js
|
||||
goto loop
|
||||
3. run robot and type 'http://192.168.0.10/webmsg/hello this is a test' into browser
|
||||
(you need to write your router address here, i.e. ip accessible from internet OR lan address)
|
||||
--]]
|
||||
|
||||
if not fetch then
|
||||
address = "192.168.0.10";
|
||||
fetch = _G.basic_robot.http_api.fetch;
|
||||
state = 0 -- ready to fetch new command
|
||||
-- WARNING: this is run outside pcall and can crash server if errors!
|
||||
result = function(res) -- res.data is string containing result
|
||||
state = 0
|
||||
if not res.succeeded then self.label("#ERROR: data couldn't be downloaded :\n" .. minetest.serialize(res) ) return end
|
||||
if res.data == "" then return end
|
||||
local req = res.data; req = string.gsub(req,"%%20"," ")
|
||||
if res.data then
|
||||
self.label(os.date("%X") ..', cmd : ' .. req)
|
||||
local i = string.find(req," !")
|
||||
if i then
|
||||
local cmd = string.sub(req,i+2)
|
||||
if cmd == "players" then
|
||||
local players = minetest.get_connected_players();
|
||||
out = {};
|
||||
for i = 1,#players do out[i] = players[i]:get_player_name() end
|
||||
MT2web("online players : " .. table.concat(out,", "))
|
||||
else
|
||||
run_commmand(cmd)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
admin = minetest.setting_get("name")
|
||||
run_commmand = function(message)
|
||||
local cmd, param = _G.string.match(message, "([^ ]+) *(.*)")
|
||||
if not param then
|
||||
param = ""
|
||||
end
|
||||
local cmd_def = minetest.chatcommands[cmd]
|
||||
if cmd_def then
|
||||
cmd_def.func(admin, param)
|
||||
else
|
||||
minetest.chat_send_all(admin..": "..message)
|
||||
end
|
||||
end
|
||||
|
||||
MT2web = function(message)
|
||||
message = string.gsub(message," ","%%20") -- NOTE: if you send request that has 'space' in it there will be error 400!
|
||||
fetch({url = "http://".. address .. "/mtmsg/"..message, timeout = 5}, result)
|
||||
end
|
||||
MT2web("minetest robot started and listening.")
|
||||
|
||||
self.listen(1)
|
||||
end
|
||||
|
||||
|
||||
|
||||
if state == 0 then
|
||||
fetch({url = "http://"..address.."/getwebmsg/", timeout = 5}, result)
|
||||
state = 1
|
||||
end
|
207
scripts/math/enigma.lua
Normal file
@ -0,0 +1,207 @@
|
||||
--ENIGMA emulator by rnd
|
||||
-- programming ~30 mins, total 3hrs 30 minutes with debugging - cause of youtube video with missing
|
||||
-- key detail - reflector!
|
||||
|
||||
-- REFERENCES:
|
||||
-- 1. https://en.wikipedia.org/wiki/Enigma_machine
|
||||
-- 2. http://users.telenet.be/d.rijmenants/en/enigmatech.htm#reflector
|
||||
-- 3. https://www.youtube.com/watch?src_vid=V4V2bpZlqx8&v=G2_Q9FoD-oQ
|
||||
|
||||
-- default settings
|
||||
settings = {}
|
||||
settings.set = function(reflector, plugboard, rotors)
|
||||
settings.reflector = reflector or 2017
|
||||
settings.plugboard = plugboard or 2017
|
||||
settings.rotors = {}
|
||||
if rotors then
|
||||
table.insert(settings.rotors, rotors[1])
|
||||
table.insert(settings.rotors, rotors[2])
|
||||
table.insert(settings.rotors, rotors[3])
|
||||
table.insert(settings.rotors, rotors[4])
|
||||
else
|
||||
table.insert(settings.rotors, 0)
|
||||
table.insert(settings.rotors, 0)
|
||||
table.insert(settings.rotors, 0)
|
||||
table.insert(settings.rotors, 2018)
|
||||
table.insert(settings.rotors, 2017)
|
||||
table.insert(settings.rotors, 2020)
|
||||
end
|
||||
--if not enigma_encrypt then
|
||||
if true then
|
||||
scramble = function(input,password,sgn) -- permutes text randomly, nice after touch to stream cypher to prevent block analysis
|
||||
_G.math.randomseed(password);
|
||||
local n = #input;
|
||||
local permute = {}
|
||||
for i = 1, n do permute[i] = i end --input:sub(i, i)
|
||||
for i = n,2,-1 do
|
||||
local j = math.random(i-1);
|
||||
local tmp = permute[j];
|
||||
permute[j] = permute[i]; permute[i] = tmp;
|
||||
end
|
||||
local out = {};
|
||||
if sgn>0 then -- unscramble
|
||||
for i = 1,n do out[permute[i]] = string.sub(input,i,i) end
|
||||
else -- scramble
|
||||
for i = 1,n do out[i] = string.sub(input,permute[i],permute[i]) end
|
||||
end
|
||||
return table.concat(out,"")
|
||||
end
|
||||
|
||||
local permutation = function(n,password,sgn) -- create random permutation of numbers 1,...,n
|
||||
_G.math.randomseed(password);
|
||||
local permute = {}
|
||||
for i = 1, n do permute[i] = i end
|
||||
for i = n,2,-1 do
|
||||
local j = math.random(i-1);
|
||||
local tmp = permute[j];
|
||||
permute[j] = permute[i]; permute[i] = tmp;
|
||||
end
|
||||
return permute;
|
||||
end
|
||||
|
||||
-- produces permutation U such that U^2 = 1; modified fisher-yates shuffle by rnd
|
||||
local reflector = function(n,password)
|
||||
_G.math.randomseed(password)
|
||||
local permute = {}
|
||||
local used = {};
|
||||
for i = 1, n do permute[i] = i end
|
||||
local rem = n;
|
||||
|
||||
for i = n,2,-1 do
|
||||
if not used[i] then
|
||||
local j = math.random(rem);
|
||||
-- now we need to find j-th unused idx
|
||||
local k = 1; local l = 0; -- k position, l how many we tried
|
||||
while l < j do
|
||||
if not used[k] then l=l+1; end
|
||||
k=k+1
|
||||
end
|
||||
j=k-1;
|
||||
|
||||
used[i]=true; used[j] = true;
|
||||
local tmp = permute[j];
|
||||
permute[j] = permute[i]; permute[i] = tmp;
|
||||
rem = rem - 2;
|
||||
end
|
||||
|
||||
end
|
||||
return permute;
|
||||
end
|
||||
|
||||
|
||||
local inverse_table = function(tbl)
|
||||
local ret = {};
|
||||
for i = 1,#tbl do
|
||||
ret[ tbl[i] ] = i;
|
||||
end
|
||||
return ret;
|
||||
end
|
||||
|
||||
local rotors = {}; -- { permutation, work index }}
|
||||
local invrotors = {}; -- reversed tables
|
||||
|
||||
-- SETUP REFLECTOR, ROTORS AND PLUGBOARD!
|
||||
local enigma_charcount = 127-32+1; -- n = 96
|
||||
local enigma_charstart = 32;
|
||||
local reflector = reflector(enigma_charcount,settings.reflector); -- this is permutation U such that U^2 = id --
|
||||
|
||||
local plugboard = permutation(enigma_charcount,settings.plugboard,1); -- setup plugboard for enigma machine
|
||||
local invplugboard = inverse_table(plugboard);
|
||||
|
||||
for i = 1,3 do rotors[i] = {rotor = permutation(enigma_charcount,settings.rotors[3 + i],1), idx = 0} end -- set up 3 rotors together with their indices
|
||||
for i = 1,3 do invrotors[i] = {rotor = inverse_table(rotors[i].rotor)} end -- set up 3 rotors together with their indices
|
||||
|
||||
-- how many possible setups:
|
||||
--[[
|
||||
n = charcount;
|
||||
rotors positions: n^3
|
||||
plugboard wiring : n!
|
||||
reflector wiring: n! / (2^n * (n/2)!)
|
||||
TOTAL: (n!)^2*n^3 / ( 2^n * (n/2)! ) ~ 6.4 * 10^77
|
||||
rotor positions & plugboard wiring: (n!)*n^3 ~ 4.8 * 10^57 (n=43)
|
||||
--]]
|
||||
|
||||
-- END OF SETUP
|
||||
|
||||
local rotate_rotor = function(i)
|
||||
local carry = 1;
|
||||
for j = i,1,-1 do
|
||||
local idx = rotors[j].idx;
|
||||
idx = idx + 1;
|
||||
if idx>=enigma_charcount then
|
||||
carry = 1;
|
||||
else
|
||||
carry = 0;
|
||||
end
|
||||
rotors[j].idx = idx % enigma_charcount;
|
||||
if carry == 0 then break end
|
||||
end
|
||||
end
|
||||
|
||||
local enigma_encrypt_char = function(x) -- x : 1 .. enigma_charcount
|
||||
-- E = P.R1.R2.R3.U.R3^-1.R2^-1.R1^-1.P^-1, P = plugboard, R = rotor, U = reflector
|
||||
x = plugboard[x];
|
||||
for i = 1,3 do
|
||||
local idx = rotors[i].idx;
|
||||
x = rotors[i].rotor[((x+idx-1) % enigma_charcount)+1];
|
||||
end
|
||||
|
||||
x = reflector[x];
|
||||
|
||||
for i = 3,1,-1 do
|
||||
local idx = rotors[i].idx;
|
||||
x = invrotors[i].rotor[x];
|
||||
x = ((x-1-idx) % enigma_charcount)+1
|
||||
|
||||
end
|
||||
|
||||
x = invplugboard[x];
|
||||
-- apply rotation to rotor - and subsequent rotors if necessary
|
||||
rotate_rotor(3)
|
||||
return x;
|
||||
end
|
||||
|
||||
|
||||
--enigma_encrypt = function(input)
|
||||
settings.encrypt = function(input)
|
||||
-- rotor settings!
|
||||
rotors[1].idx = settings.rotors[1]
|
||||
rotors[2].idx = settings.rotors[2]
|
||||
rotors[3].idx = settings.rotors[3]
|
||||
|
||||
local ret = "";
|
||||
for i = 1,#input do
|
||||
local c = string.byte(input,i) - enigma_charstart +1;
|
||||
--say(i .. " : " .. c)
|
||||
if c>=1 and c<=enigma_charcount then
|
||||
c = enigma_encrypt_char(c);
|
||||
end
|
||||
ret = ret .. string.char(enigma_charstart+c-1);
|
||||
end
|
||||
return ret
|
||||
|
||||
end
|
||||
settings.decrypt = settings.encrypt
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
msg = self.listen_msg()
|
||||
if msg then
|
||||
msg = minetest.strip_colors(msg)
|
||||
local mark = string.find(msg,"@e") -- delimiter in chat
|
||||
if mark then
|
||||
msg = string.sub(msg,mark+2);
|
||||
msg = minetest.colorize("yellow",enigma_encrypt(msg))
|
||||
say(minetest.colorize("red","#decrypted : ") .. msg)
|
||||
end
|
||||
end
|
||||
|
||||
msg = self.sent_msg()
|
||||
if msg then
|
||||
local msg = enigma_encrypt(msg);
|
||||
say("@e" .. msg,true)
|
||||
-- minetest.show_formspec("encrypted_text", "size[4,4] textarea[0,-0.25;5,5;text;;".. "@e" .. minetest.formspec_escape(msg) .. "]")
|
||||
end
|
||||
|
||||
|
92
scripts/math/fractal_bot.lua
Normal 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()
|
184
scripts/math/multiplication.lua
Normal file
@ -0,0 +1,184 @@
|
||||
if not number then
|
||||
number = {};
|
||||
number.base = 10; -- what base: 2-10
|
||||
number.data = {};
|
||||
number.size = -1;
|
||||
|
||||
function number:tostring()
|
||||
local ret = ""
|
||||
--say("tostring size " .. self.size)
|
||||
for i = self.size,1,-1 do ret = ret .. (self.data[i] or "X").."" end
|
||||
return ret
|
||||
end
|
||||
|
||||
function number:new(data)
|
||||
local o = {};_G.setmetatable(o, self); o.data = {};
|
||||
for i = 1,#data do o.data[i] = data[i] end -- do copy otherwise it just saves reference
|
||||
o.size = #data;
|
||||
self.__index = self; return o
|
||||
end
|
||||
|
||||
number.add = function (lhs, rhs,res)
|
||||
local n1 = lhs.size;local n2 = rhs.size;local n = math.max(n1,n2);
|
||||
local carry=0, sum; local base = lhs.base;
|
||||
local out = false;
|
||||
if not res then res = number:new({}) out = true end
|
||||
local data = res.data
|
||||
for i = 1,n do
|
||||
sum = (lhs.data[i] or 0)+(rhs.data[i] or 0)+carry;
|
||||
if carry>0 then carry = 0 end
|
||||
if sum>=base then data[i]=sum-base; carry = 1 else data[i] = sum end
|
||||
end
|
||||
if carry>0 then data[n+1]=1 res.size = n+1 else res.size = n end
|
||||
if out then return res end
|
||||
end
|
||||
|
||||
number.__add = add;
|
||||
|
||||
function number:set(m)
|
||||
local data = self.data;
|
||||
local mdata = m.data;
|
||||
for i=1,#mdata do
|
||||
data[i]=mdata[i];
|
||||
end
|
||||
self.size = m.size;
|
||||
end
|
||||
|
||||
-- 'slow' long multiply
|
||||
number.multiply = function (lhs, rhs, res)
|
||||
local n1 = lhs.size;local n2 = rhs.size;local n = n1+n2;
|
||||
--say("multiply sizes " .. n1 .. "," .. n2)
|
||||
local out = false;
|
||||
if not res then res = number:new({}); out = true end;
|
||||
res.size = n1+n2-1;
|
||||
res.data = {} -- if data not cleared it will interfere with result!
|
||||
local data = res.data;
|
||||
local c,prod,carry = 0; local base = lhs.base;
|
||||
for i=1,n1 do
|
||||
carry = 0;
|
||||
c = lhs.data[i] or 0;
|
||||
for j = 1,n2 do -- multiply with i-th digit and add to result
|
||||
prod = (data[i+j-1] or 0)+c*(rhs.data[j] or 0)+carry;
|
||||
carry = math.floor(prod / base);
|
||||
prod = prod % base;
|
||||
data[i+j-1] = (prod)%base;
|
||||
end
|
||||
if carry>0 then data[i+n2] = (data[i+n2] or 0)+ carry ;if res.size<i+n2 then res.size = i+n2 end end
|
||||
end
|
||||
if out then return res end
|
||||
end
|
||||
|
||||
--TO DO
|
||||
number.karatsuba_multiply = function(lhs,rhs,res)
|
||||
local n1 = lhs.size;local n2 = rhs.size;
|
||||
local n0 = math.max(n1,n2);
|
||||
if n0< 2 then -- normal multiply for "normal" sized numbers
|
||||
number.multiply(lhs, rhs, res)
|
||||
return
|
||||
end
|
||||
|
||||
n0 = math.floor(n0/2);
|
||||
local n = n1+n2;
|
||||
|
||||
local a1 = number:new({});
|
||||
local tdata = a1.data; local sdata = lhs.data;
|
||||
for i = 1,n0 do tdata[i] = sdata[i] or 0 end; a1.size = n0;
|
||||
local a2 = number:new({}); tdata = a2.data;
|
||||
for i = n0+1,n1 do tdata[i-n0] = sdata[i] or 0 end; a2.size = n1-n0;
|
||||
|
||||
local b1 = number:new({});
|
||||
local tdata = b1.data; sdata = rhs.data;
|
||||
for i = 1,n0 do tdata[i] = sdata[i] or 0 end; b1.size = n0;
|
||||
local b2 = number:new({}); tdata = b2.data;
|
||||
for i = n0+1,n1 do tdata[i-n0] = sdata[i] or 0 end; b2.size = n1-n0;
|
||||
|
||||
--say("a1 " .. a1:tostring())
|
||||
--say("a2 " .. a2:tostring())
|
||||
|
||||
--say("b1 " .. b1:tostring())
|
||||
--say("b2 " .. b2:tostring())
|
||||
|
||||
|
||||
local A = number:new({}); number.karatsuba_multiply(a1,b1,A);
|
||||
local B = number:new({}); number.karatsuba_multiply(a2,b2,B);
|
||||
local C = number:new({}); number.karatsuba_multiply(a1+a2,b1+b2,C);
|
||||
--== C-A-B
|
||||
--TODO minus, reassemble together..
|
||||
|
||||
end
|
||||
|
||||
karatsuba_multiply_test = function()
|
||||
--local in2 = number:new({2,1,1})
|
||||
--local in1 = number:new({2,1,1})
|
||||
local res = number:new({});
|
||||
local in1 = number:new({3,1,4}); --413
|
||||
local in2 = number:new({7,2,5}); -- 527
|
||||
number.karatsuba_multiply(in1,in2,res)
|
||||
end
|
||||
|
||||
karatsuba_multiply_test()
|
||||
|
||||
|
||||
multiply_test = function()
|
||||
--local in2 = number:new({2,1,1})
|
||||
--local in1 = number:new({2,1,1})
|
||||
local res = number:new({});
|
||||
local in1 = number:new({4})
|
||||
number.multiply(in1,in1,res)
|
||||
say("mult check 1 " .. res:tostring())
|
||||
--say("mult check 2 " .. number.multiply(in1,in2):tostring())
|
||||
|
||||
end
|
||||
--multiply_test()
|
||||
|
||||
number.__mul = number.multiply;
|
||||
|
||||
number.power = function(n,power_) -- calculate high powers efficiently - number of steps is log_2(power)
|
||||
local power = power_;
|
||||
local input = number:new(n.data);
|
||||
local out = number:new({});
|
||||
|
||||
local count = 0;
|
||||
|
||||
local r; local powerplan = {}; -- 0: just square a->a^2, 1 = square and multiply a-> a*a^2
|
||||
while (power>0) do
|
||||
r=power%2; powerplan[#powerplan+1] = r; power = (power-r)/2
|
||||
end
|
||||
|
||||
for i = #powerplan-1,1,-1 do
|
||||
|
||||
number.multiply(input,input,out);
|
||||
|
||||
if powerplan[i] == 1 then
|
||||
input,out = out, input;
|
||||
number.multiply(input,n,out); count = count + 2
|
||||
else count = count + 1;
|
||||
end
|
||||
|
||||
input,out = out, input;
|
||||
end
|
||||
|
||||
return input
|
||||
end
|
||||
|
||||
split = function(s,k)
|
||||
local ret = "";
|
||||
local j=1,length; length = string.len(s)/k
|
||||
for i = 1, length do
|
||||
j = (i-1)*k+1;
|
||||
ret = ret .. string.sub(s,j,j+k-1) .. "\n"
|
||||
end
|
||||
--say("j " .. j)
|
||||
if j>1 then j = j+k end
|
||||
ret = ret .. string.sub(s,j)
|
||||
return ret
|
||||
end
|
||||
|
||||
self.spam(1)
|
||||
|
||||
-- little endian ! lower bits first ..
|
||||
|
||||
--n = number:new({7,1,0,2}); local power = 2017;
|
||||
--self.label(split(n:tostring().."^"..power .. " = " .. number.power(n,power):tostring(),100))
|
||||
--2017^2017 = 3906...
|
||||
end
|
83
scripts/math/perm2cycles.lua
Normal file
@ -0,0 +1,83 @@
|
||||
if not perm2cycles then
|
||||
|
||||
perm2cycles = function(perm)
|
||||
local n = #perm;
|
||||
local i = 1; -- already reached number
|
||||
local ret = {};
|
||||
local visited = {};
|
||||
local step = 0;
|
||||
|
||||
while (true) do
|
||||
local cycle = {i}
|
||||
local j=i;
|
||||
|
||||
while (true) do
|
||||
step = step +1
|
||||
if step > 2*n then return {} end
|
||||
j=perm[j];
|
||||
visited[j] = 1;
|
||||
if j and j~=cycle[1] then cycle[#cycle+1]=j else break end
|
||||
end
|
||||
|
||||
i=n+1;
|
||||
for k=1,n do -- find smallest non visited yet
|
||||
if not visited[k] and k<i then i = k end
|
||||
end
|
||||
|
||||
ret[#ret+1] = cycle;
|
||||
if i == n+1 then return ret end
|
||||
end
|
||||
end
|
||||
|
||||
random_permute = function(arr)
|
||||
local n = #arr;
|
||||
for i = n,3,-1 do
|
||||
local j = math.random(i);
|
||||
local tmp = arr[j]; arr[j] = arr[i]; arr[i] = tmp;
|
||||
end
|
||||
end
|
||||
|
||||
get_permutations = function(n)
|
||||
free = {}; --[1] = {stack of free for 1 : head, a1,..., a_head}
|
||||
isfree = {}; -- [i]=1 if item i free, 0 if not
|
||||
current = 1; --{1,..., current element, ... , n}
|
||||
selection = {};
|
||||
local data = free[1]; for i=1,n do data[i]=i isfree[i]=1 end data[0] = n;
|
||||
|
||||
--1. pick free spot from free stack ( if not possible backtrack ) and move on onto next element ( if not possible stay at this one)
|
||||
-- backtrack: current--
|
||||
local data = free[current];
|
||||
if data[0]<1 then -- backtrack
|
||||
isfree[selection[current]] = 1;
|
||||
current = current - 1;
|
||||
if current <=0 then return end -- exhausted all options
|
||||
else
|
||||
local i = data[data[0]]; -- free possibility
|
||||
selection[current] = i; isfree[i] = 0;
|
||||
data[0]=data[0]-1; -- pop the stack
|
||||
--try move forward
|
||||
if current<n then
|
||||
current = current+1;
|
||||
-- get new free spots for new current
|
||||
local data = free[current]; data = {};
|
||||
for i = 1,n do if isfree[i] == 1 then data[#data+1]=i; break end end;
|
||||
data[0]=#data;
|
||||
if data[0] == 0 then -- nothing free, backtrack
|
||||
isfree[selection[current]] = 1;
|
||||
current = current - 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
arr2string = function(arr)
|
||||
return string.gsub(_G.dump(arr),"\n","")
|
||||
end
|
||||
|
||||
local arr = {}; for i =1,10 do arr[i] =i end; random_permute(arr);
|
||||
local cycles = perm2cycles(arr);
|
||||
say("random permutation = " .. arr2string(arr) .. " => cycles = " .. arr2string(cycles) )
|
||||
|
||||
end
|
65
scripts/pine_tree_harvest.lua
Normal file
@ -0,0 +1,65 @@
|
||||
-- PINE TREE HARVEST by rnd1
|
||||
if not harvest then
|
||||
harvest = {};
|
||||
harvest.s = -1 -- -1 idle, 0 expect tree
|
||||
harvest.tree = "default:pine_tree";harvest.wood = "default:wood";harvest.sapling = "default:pine_sapling";
|
||||
harvest.step = function()
|
||||
local s=harvest.s;
|
||||
if s == 0 then -- did we bump into tree
|
||||
local node = read_node.forward();
|
||||
if node == harvest.tree then
|
||||
dig.forward(); move.forward(); harvest.s = 1 -- found tree, moving in
|
||||
self.label("im digging up tree ")
|
||||
end
|
||||
elseif s == 1 then -- climbing up
|
||||
dig.up(); move.up(); place.down(harvest.wood);
|
||||
local node = read_node.up();
|
||||
if node ~= harvest.tree then
|
||||
harvest.s = 2 -- top
|
||||
self.label("i reached top of tree")
|
||||
end
|
||||
elseif s == 2 then -- going down
|
||||
local node = read_node.down();
|
||||
if node == harvest.wood then
|
||||
dig.down(); move.down()
|
||||
self.label("im going back down")
|
||||
else
|
||||
pickup(8);
|
||||
move.backward();place.forward(harvest.sapling);move.forward();
|
||||
harvest.s = -1 -- idle
|
||||
self.label("i finished cutting tree")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--harvest walk init
|
||||
if not angle then
|
||||
sender,mail = self.read_mail(); if sender == "rnd1" then harvest.s = tonumber(mail) or 0 end
|
||||
wall = "default:cobble";
|
||||
angle = 90
|
||||
end
|
||||
|
||||
if harvest.s~=-1 then
|
||||
harvest.step()
|
||||
elseif harvest.s==-1 then
|
||||
node = read_node.forward();
|
||||
if node==harvest.tree then
|
||||
harvest.s = 0;
|
||||
self.label("i found tree")
|
||||
else
|
||||
self.label("im walking")
|
||||
if not move.forward() then
|
||||
if node == wall then
|
||||
self.label("i hit wall")
|
||||
turn.angle(angle);
|
||||
if move.forward() then
|
||||
move.forward();move.forward();turn.angle(angle); angle = -angle
|
||||
else
|
||||
turn.angle(-angle);angle = - angle
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.send_mail("rnd1",harvest.s) -- remember state in case of reactivation
|
59
scripts/programming/brainfuck generator.lua
Normal file
@ -0,0 +1,59 @@
|
||||
N = 30; -- length of program (without ])
|
||||
|
||||
desired_frequencies = {
|
||||
[">"] = 10,
|
||||
["<"] = 10,
|
||||
["-"]=10,
|
||||
["+"]=20,
|
||||
["."]=10,
|
||||
[","]=10,
|
||||
["["]=10, -- matching "]" will be inserted automatically!
|
||||
}
|
||||
matching_parenthesis = "]";
|
||||
routine_lower = 1; routine_higher = 5; -- specify range, how many characters in routine [.....]
|
||||
|
||||
generate_selections = function(desired_frequency)
|
||||
local sum = 0
|
||||
for k,v in pairs(desired_frequency) do sum = sum + v end
|
||||
local isum = 0; local count = 0; local selections = {}
|
||||
for k,v in pairs(desired_frequency) do count = count +1 isum = isum + desired_frequency[k]/sum; selections[count] = {isum,k} end
|
||||
return selections
|
||||
end
|
||||
|
||||
choose = function(selections, rnd)
|
||||
local low, mid, high;
|
||||
low = 1; high = #selections; mid = math.floor((low+high)/2)
|
||||
local step = 0;
|
||||
while high-low>1 and step < 20 do
|
||||
step = step + 1
|
||||
if rnd <= selections[mid][1] then high = mid else low = mid end
|
||||
mid = math.floor((low+high)/2)
|
||||
end
|
||||
return selections[mid][2]
|
||||
end
|
||||
|
||||
|
||||
generate_program = function(desired_frequencies,N, routine_lower, routine_higher)
|
||||
local selections = generate_selections(desired_frequencies);
|
||||
|
||||
local ret = {};
|
||||
local count = 0
|
||||
local stack = {};
|
||||
|
||||
for count = 1, N do
|
||||
local choice = choose(selections, math.random());
|
||||
if choice == "[" then
|
||||
local i = count + math.random(routine_lower,routine_higher)
|
||||
if i > N then i = N end
|
||||
stack[#stack+1] = i;
|
||||
end
|
||||
ret[count] = choice
|
||||
end
|
||||
|
||||
for i = 1,#stack do local j = stack[i] ret[j]=ret[j]..matching_parenthesis end
|
||||
return table.concat(ret)
|
||||
end
|
||||
|
||||
say(generate_program(desired_frequencies,N, routine_lower, routine_higher))
|
||||
|
||||
self.remove()
|
66
scripts/programming/brainfuck.lua
Normal file
@ -0,0 +1,66 @@
|
||||
-- BRAINFUCK interpreter by rnd, 2017
|
||||
-- https://en.wikipedia.org/wiki/Brainfuck
|
||||
|
||||
if not ram then
|
||||
prog = "+++.>++++++.<[->+<]>."
|
||||
ramsize = 10
|
||||
maxsteps = 100; step=0; -- for RUN state only
|
||||
|
||||
n = string.len(prog);ram = {};for i = 1, ramsize do ram[i]=0 end -- init ram
|
||||
pointer = 1 -- ram pointer
|
||||
instruction = 1 -- instruction pointer
|
||||
self.spam(1)
|
||||
|
||||
RUNNING = 1; END = 2; RUN = 3;
|
||||
state = RUNNING
|
||||
|
||||
get_ram = function() msg = "" for i = 1,ramsize do msg = msg .. ram[i] .. "," end return msg end
|
||||
|
||||
cmdset = {
|
||||
[">"] = function() pointer = pointer + 1; if pointer > ramsize then pointer = 1 end end,
|
||||
["<"] = function() pointer = pointer - 1; if pointer > ramsize then pointer = 1 end end,
|
||||
["+"] = function() ram[pointer]=ram[pointer]+1 end,
|
||||
["-"] = function() ram[pointer]=ram[pointer]-1 end,
|
||||
["."] = function() say(ram[pointer]) end,
|
||||
[","] = function() ram[pointer] = tonumber(read_text.forward("infotext") or "") or 0 end,
|
||||
["["] = function()
|
||||
if ram[pointer] == 0 then
|
||||
local lvl = 0
|
||||
for j = instruction, n, 1 do
|
||||
if string.sub(prog,j,j) == "]" then lvl = lvl - 1 if lvl == 0 then
|
||||
self.label("JMP " .. j ) instruction = j return
|
||||
end end
|
||||
if string.sub(prog,j,j) == "[" then lvl = lvl + 1 end
|
||||
end
|
||||
end
|
||||
end,
|
||||
["]"] = function()
|
||||
if ram[pointer] ~= 0 then
|
||||
local lvl = 0
|
||||
for j = instruction, 1, -1 do
|
||||
if string.sub(prog,j,j) == "]" then lvl = lvl - 1 end
|
||||
if string.sub(prog,j,j) == "[" then lvl = lvl + 1 if lvl == 0 then
|
||||
self.label("JMP " .. j ) instruction = j return
|
||||
end end
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
-- EXECUTION
|
||||
if state == RUNNING then
|
||||
c = string.sub(prog,instruction,instruction) or "";
|
||||
if c and cmdset[c] then cmdset[c]() end
|
||||
self.label("ins ptr " .. instruction .. ", ram ptr " .. pointer .. ": " .. ram[pointer] .. "\n" .. string.sub(prog, instruction).."\n"..get_ram())
|
||||
instruction = instruction + 1; if instruction > n then state = END end
|
||||
|
||||
-- RUN THROUGH
|
||||
elseif state == RUN then
|
||||
while (step<maxsteps) do
|
||||
step = step + 1
|
||||
c = string.sub(prog,instruction,instruction) or "";
|
||||
if c and cmdset[c] then cmdset[c]() end
|
||||
instruction = instruction + 1; if instruction > n then self.label("ram : " .. get_ram()) step = maxsteps state = END end
|
||||
end
|
||||
end
|
53
scripts/programming/code_parser_strings_identify.lua
Normal file
@ -0,0 +1,53 @@
|
||||
function identify_strings(code) -- returns list of positions {start,end} of literal strings in lua code
|
||||
|
||||
local i = 0; local j; local length = string.len(code);
|
||||
local mode = 0; -- 0: not in string, 1: in '...' string, 2: in "..." string, 3. in [==[ ... ]==] string
|
||||
local modes = {
|
||||
{"'","'"},
|
||||
{"\"","\""},
|
||||
{"%[=*%[","%]=*%]"}
|
||||
}
|
||||
local ret = {}
|
||||
while i < length do
|
||||
i=i+1
|
||||
|
||||
local jmin = length+1;
|
||||
if mode == 0 then -- not yet inside string
|
||||
for k=1,#modes do
|
||||
j = string.find(code,modes[k][1],i);
|
||||
if j and j<jmin then -- pick closest one
|
||||
jmin = j
|
||||
mode = k
|
||||
end
|
||||
end
|
||||
if mode ~= 0 then -- found something
|
||||
j=jmin
|
||||
ret[#ret+1] = {jmin}
|
||||
end
|
||||
if not j then break end -- found nothing
|
||||
else
|
||||
_,j = string.find(code,modes[mode][2],i); -- search for closing pair
|
||||
if not j then break end
|
||||
if (mode~=2 or string.sub(code,j-1,j-1) ~= "\\") then -- not (" and \")
|
||||
ret[#ret][2] = j
|
||||
mode = 0
|
||||
end
|
||||
end
|
||||
i=j -- move to next position
|
||||
end
|
||||
if mode~= 0 then ret[#ret][2] = length end
|
||||
return ret
|
||||
end
|
||||
|
||||
identify_strings_test = function()
|
||||
local code =
|
||||
[[code; a = "text \"text\" more text"; b = 'some more text'; c = [==[ text ]==] ]]
|
||||
local strings = identify_strings(code);
|
||||
say(minetest.serialize(strings))
|
||||
for i =1,#strings do
|
||||
say(i ..": " .. string.sub(code, strings[i][1],strings[i][2]))
|
||||
end
|
||||
end
|
||||
identify_strings_test()
|
||||
|
||||
self.remove()
|
114
scripts/programming/os_show_demo.lua
Normal file
@ -0,0 +1,114 @@
|
||||
-- demo bot
|
||||
|
||||
if not s then
|
||||
-- move.forward();--turn.angle(180)
|
||||
s=0;t=0
|
||||
mag = 3;
|
||||
|
||||
boot = function()
|
||||
self.display_text("RND technologies\n(inc) 2016\n\n\nmemchk "..(t*1024).. "kb ",16,mag)
|
||||
t=t+1; if t == 8 then s = 0.5 t = 0 end
|
||||
end
|
||||
|
||||
boot_memchk = function()
|
||||
self.display_text("RND technologies\n(inc) 2016\n\n ".. (8*1024).. "kb READY",16,mag)
|
||||
t=t+1; if t==3 then t=0 s = 1 end
|
||||
end
|
||||
|
||||
os_load = function()
|
||||
if t == 0 then self.display_text("============\nrndos v2.5\n============\n\nloading...\nkernel v12.2 ",12,mag) end
|
||||
t=t+1; if t == 7 then s = 2 t = 0 end
|
||||
end
|
||||
|
||||
main_menu = function()
|
||||
if t==0 then self.display_text("MAIN MENU \n\n1) ip lister\n2) kick player\n3) teleport\n4) give fly\n5) kill\n6) turn off\n\n0 to return here from app",16,mag) end
|
||||
|
||||
text = read_text.backward();
|
||||
if text and text~="" then
|
||||
write_text.backward("")
|
||||
if text == "1" then s = 3 t = 0
|
||||
elseif text == "2" then s = 4 t = 0
|
||||
elseif text == "3" then s=5 t =0
|
||||
elseif text == "4" then s=6 t =0
|
||||
elseif text == "5" then s=7 t =0
|
||||
elseif text == "6" then self.remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ip_lister = function()
|
||||
if t%5 == 0 then
|
||||
players = _G.minetest.get_connected_players(); msg = "IP LISTER\n\n";
|
||||
for _, player in pairs(players) do
|
||||
local name = player:get_player_name(); ip = _G.minetest.get_player_ip(name) or "";
|
||||
|
||||
msg = msg .. name .. " " .. ip .. "\n";
|
||||
end
|
||||
self.display_text(msg,30,mag)
|
||||
t=0
|
||||
end
|
||||
t=t+1
|
||||
if read_text.backward() == "0" then s=2 t=0 end
|
||||
|
||||
end
|
||||
|
||||
act_on_player = function(mode)
|
||||
if t==0 then
|
||||
msg = get_player_list()
|
||||
local txt = {[1]="KICK WHO?\n", [2] = "TELEPORT WHO HERE?\n", [3] = "GIVE FLY TO WHOM?\n", [4] = "KILL WHO?\n"}
|
||||
text = txt[mode] or "";
|
||||
self.display_text(text..msg,30,mag) t=1
|
||||
end
|
||||
text = read_text.backward();
|
||||
if text then
|
||||
if text=="0" then s=2 t=0 else
|
||||
write_text.backward("");
|
||||
if mode ==1 then
|
||||
_G.minetest.kick_player(player_list[tonumber(text)] or "");
|
||||
elseif mode ==2 then
|
||||
player =_G.minetest.get_player_by_name(player_list[tonumber(text)] or "");
|
||||
if player then player:setpos(self.spawnpos()) end
|
||||
elseif mode ==3 then
|
||||
player=_G.minetest.get_player_by_name(player_list[tonumber(text)] or "");
|
||||
if player then player:set_physics_override({gravity=0.1}) end
|
||||
elseif mode ==4 then
|
||||
player=_G.minetest.get_player_by_name(player_list[tonumber(text)] or "");
|
||||
if player then player:set_hp(0) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
player_list = {}
|
||||
get_player_list = function()
|
||||
local players = _G.minetest.get_connected_players(); local msg = ""; local i=0;
|
||||
for _, player in pairs(players) do
|
||||
local name = player:get_player_name();
|
||||
i=i+1;msg = msg .. i ..") " .. name .. "\n";
|
||||
player_list[i]=name;
|
||||
end
|
||||
return msg
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
self.label(s)
|
||||
if s == 0 then
|
||||
boot()
|
||||
elseif s==0.5 then
|
||||
boot_memchk()
|
||||
elseif s==1 then
|
||||
os_load()
|
||||
elseif s==2 then
|
||||
main_menu()
|
||||
elseif s==3 then
|
||||
ip_lister()
|
||||
elseif s==4 then
|
||||
act_on_player(1)
|
||||
elseif s==5 then
|
||||
act_on_player(2)
|
||||
elseif s==6 then
|
||||
act_on_player(3)
|
||||
elseif s==7 then
|
||||
act_on_player(4)
|
||||
end
|
271
scripts/programming/rndscript.lua
Normal file
@ -0,0 +1,271 @@
|
||||
-- rnd 2017
|
||||
-- call stack, subroutines, recursive subroutines, if jumps, loops, variables
|
||||
|
||||
if not cmd then
|
||||
prog = "\n"..
|
||||
"<fd M(loop) f NNOT(air)[<] G(loop)";
|
||||
|
||||
pause = 0
|
||||
|
||||
self.label(prog)
|
||||
build = {};
|
||||
|
||||
cmd = {
|
||||
["f"] = function() move.forward() end,
|
||||
["b"] = function() move.backward() end,
|
||||
["l"] = function() move.left() end,
|
||||
["r"] = function() move.right() end,
|
||||
["u"] = function() move.up() end,
|
||||
["d"] = function() move.down() end,
|
||||
|
||||
["p"] = function() place.forward_down("default:dirt") end,
|
||||
["P"] = function() place.forward("default:dirt") end,
|
||||
["F"] = function() dig.forward() end,
|
||||
["L"] = function() dig.left() end,
|
||||
["R"] = function() dig.right() end,
|
||||
["U"] = function() dig.up() end,
|
||||
["D"] = function() dig.down() end,
|
||||
["<"] = function() turn.left() end,
|
||||
[">"] = function() turn.right() end,
|
||||
};
|
||||
|
||||
build.debug =true;
|
||||
build.stackdepth = 16;
|
||||
|
||||
build.ignored = {["]"]=true,["["]=true};
|
||||
|
||||
-- RESERVED: Ex(y)[A] : if x == y do A end, N(node)[A] : if read_node.forward()==node then do A end
|
||||
-- G(label) .. GOTO GR(label) .. CALL SUBROUTINE +a(b),-a(b),=a(b)
|
||||
|
||||
set_routine = function(i)
|
||||
local jr = string.find(prog,"%[",i+1)
|
||||
if not jr then say("error, missing [ after position " .. i+1); self.remove() end
|
||||
build.count = get_var(string.sub(prog,i+1,jr-1)) or 1
|
||||
local kr = string.find(prog,"]",jr+1);
|
||||
if not kr then say("error, missing ] after position " .. jr+1); self.remove() end
|
||||
return jr,kr --returns routine limits jr,kr
|
||||
end
|
||||
|
||||
error_msg = function(i)
|
||||
local callstack = "";
|
||||
for _,v in pairs(build.callstack) do
|
||||
callstack = callstack.. call_marks[v].. ",";
|
||||
end
|
||||
|
||||
return "ERROR: " ..string.sub(prog,i) .. "\nCALL STACK: " .. callstack
|
||||
end
|
||||
|
||||
run_routine = function(i,jr,kr)
|
||||
local count = build.count;
|
||||
if count > 0 then
|
||||
if i == kr then
|
||||
count = count - 1 i = jr+1
|
||||
end
|
||||
--say(" i " .. i .. " kr " .. kr)
|
||||
if count > 0 then
|
||||
c=string.sub(prog,i,i)
|
||||
if c == "G" then i=go_to(i); build.state = 0 else -- exit routine
|
||||
if cmd[c] then cmd[c]() else
|
||||
if not ignored[c] then
|
||||
self.label("run routine: invalid instruction at position " .. i .. "\n" .. error_msg(i));
|
||||
self.debug = false; build.state = -1
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
i=kr-- exit,jump to next instruction
|
||||
build.state = 0
|
||||
if build.debug then self.label("["..build.state .. "]" .. i .. "\nROUTINE EXIT") end
|
||||
end
|
||||
|
||||
build.count = count
|
||||
return i -- return next execution address
|
||||
end
|
||||
|
||||
push_callstack = function(val) -- addresses where to continue after function ends
|
||||
if #build.callstack > build.stackdepth then say("error: stack depth limit " .. build.stackdepth .. " exceeded "); self.remove() end
|
||||
build.callstack[#build.callstack+1] = val;
|
||||
end
|
||||
|
||||
pop_callstack = function()
|
||||
local val = build.callstack[#build.callstack];
|
||||
build.callstack[#build.callstack] = nil;
|
||||
return val
|
||||
end
|
||||
|
||||
go_to = function(i)
|
||||
local j = string.find(prog,"%(",i+1); local k = string.find(prog,"%)",j+1)
|
||||
local call = false;
|
||||
if string.sub(prog,i+1,j-1) == "R" then call = true push_callstack(k) end -- function call, save call exit address to return here
|
||||
|
||||
local target = string.sub(prog,j+1,k-1);
|
||||
if target == "" then -- marking end of routine
|
||||
i = pop_callstack();
|
||||
if build.debug then self.label("["..build.state .. "]" .. i .. "\nEXITING ROUTINE to " .. i .. ", stack level " .. #build.callstack) end
|
||||
else
|
||||
i = go_to_mark[target]-1;
|
||||
if call == false then
|
||||
if build.debug then self.label("["..build.state .. "]" .. i .. "\nGOTO " .. target .. "=".. i ) end
|
||||
else
|
||||
if build.debug then self.label("["..build.state .. "]" .. i .. "\nCALL SUBROUTINE " .. target .. "=".. i .. "\ncall stack = " .. string.gsub(_G.dump(build.callstack),"\n","")) end
|
||||
end
|
||||
end
|
||||
|
||||
return i;
|
||||
end
|
||||
|
||||
-- scan for go_to markers
|
||||
go_to_mark = {};
|
||||
call_marks = {}; -- OPTIONAL for nicer error messages
|
||||
scan_go_to = function()
|
||||
local i = 0;
|
||||
while ( i < string.len(prog)) do
|
||||
i=i+1;
|
||||
if string.sub(prog,i,i+1) == "M(" then
|
||||
local j = string.find(prog,"%(",i+1)
|
||||
local k = string.find(prog,"%)",j+1)
|
||||
local name = string.sub(prog,j+1,k-1);
|
||||
prog = string.sub(prog,1,i-1)..string.sub(prog,k+1)
|
||||
go_to_mark[name] = i;
|
||||
end
|
||||
end
|
||||
|
||||
i=0 -- OPTIONAL call marks scanning
|
||||
while ( i < string.len(prog)) do
|
||||
i=i+1;
|
||||
if string.sub(prog,i,i+2) == "GR(" then
|
||||
local j = string.find(prog,"%(",i+1)
|
||||
local k = string.find(prog,"%)",j+1)
|
||||
local name = string.sub(prog,j+1,k-1);
|
||||
call_marks[k] = name;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
get_var = function(s)
|
||||
local ns = tonumber(s);
|
||||
if _G.tostring(ns)==s then return ns else return build.var[s] end
|
||||
end
|
||||
|
||||
prog = string.gsub(prog, " ", "") -- remove all spaces
|
||||
prog = string.gsub(prog, "\n", "") -- remove all newlines
|
||||
scan_go_to()
|
||||
build.n=string.len(prog)
|
||||
build.i=1;
|
||||
build.count = 1;
|
||||
build.state = 0;
|
||||
build.callstack = {};
|
||||
build.var = {};
|
||||
self.spam(1)
|
||||
end
|
||||
|
||||
build.run = function()
|
||||
|
||||
local i=build.i; -- get current execution address
|
||||
|
||||
local jr,kr -- routine execution boundaries
|
||||
local jc, kc
|
||||
|
||||
--i=i+1; if i>build.n then i = 1 end end
|
||||
|
||||
c=string.sub(prog,i,i)
|
||||
|
||||
if build.state == 0 then
|
||||
if c == "R" then
|
||||
jr,kr=set_routine(i); i = jr; -- set up execution point i=jr
|
||||
build.state = 1
|
||||
elseif c == "=" then
|
||||
jc = string.find(prog,"%(",i+1)
|
||||
kc = string.find(prog,"%)",jc+1)
|
||||
local var1 = string.sub(prog,i+1,jc-1);
|
||||
local var2 = get_var(string.sub(prog,jc+1,kc-1));
|
||||
build.var[var1]=var2
|
||||
i=kc
|
||||
|
||||
elseif c == "+" then
|
||||
jc = string.find(prog,"%(",i+1)
|
||||
kc = string.find(prog,"%)",jc+1)
|
||||
local var1 = string.sub(prog,i+1,jc-1);
|
||||
local var2 = get_var(string.sub(prog,jc+1,kc-1));
|
||||
build.var[var1]=build.var[var1]+var2
|
||||
i=kc
|
||||
elseif c == "-" then
|
||||
jc = string.find(prog,"%(",i+1)
|
||||
kc = string.find(prog,"%)",jc+1)
|
||||
local var1 = string.sub(prog,i+1,jc-1);
|
||||
local var2 = get_var(string.sub(prog,jc+1,kc-1));
|
||||
build.var[var1]=build.var[var1]-var2
|
||||
i=kc
|
||||
elseif c == "E" then
|
||||
jc = string.find(prog,"%(",i+1)
|
||||
kc = string.find(prog,"%)",jc+1)
|
||||
local INOT = string.sub(prog,i+1,i+3) == "NOT";
|
||||
local trigger; local var1; local var2;
|
||||
|
||||
if INOT then
|
||||
var1 = get_var(string.sub(prog,i+4,jc-1));
|
||||
var2 = get_var(string.sub(prog,jc+4,kc-1));
|
||||
else
|
||||
var1 = get_var(string.sub(prog,i+1,jc-1));
|
||||
var2 = get_var(string.sub(prog,jc+1,kc-1));
|
||||
end
|
||||
trigger = (var1 == var2)
|
||||
|
||||
if (not INOT and trigger) or (INOT and not trigger)then
|
||||
i=kc;
|
||||
jr,kr=set_routine(i);i=jr
|
||||
build.state = 1
|
||||
else
|
||||
kc = string.find(prog,"]",kc+1)
|
||||
i = kc
|
||||
end
|
||||
elseif c == "N" then
|
||||
jc = string.find(prog,"%(",i+1)
|
||||
kc = string.find(prog,"%)",jc+1)
|
||||
local node = string.sub(prog,jc+1,kc-1) or "air";
|
||||
local INOT = string.sub(prog,i+1,jc-1) == "NOT";
|
||||
local trigger;
|
||||
trigger = read_node.forward() == node;
|
||||
|
||||
if (not INOT and trigger) or (INOT and not trigger)then
|
||||
i=kc;
|
||||
jr,kr=set_routine(i); i = jr
|
||||
build.state = 1
|
||||
else
|
||||
kc = string.find(prog,"]",kc+1)
|
||||
i = kc
|
||||
end
|
||||
elseif c == "G" then
|
||||
i=go_to(i);
|
||||
elseif c == "C" then
|
||||
jc = string.find(prog,"%(",i+1)
|
||||
kc = string.find(prog,"%)",jc+1)
|
||||
var = string.sub(prog,jc+1,kc-1);
|
||||
i = kc
|
||||
self.label(var .. "=" .. get_var(var))
|
||||
else
|
||||
if cmd[c] then cmd[c]() else
|
||||
if not build.ignored[c] then
|
||||
self.label("run main: invalid instruction at position " .. i.. "\n" .. error_msg(i));
|
||||
build.state = -1; self.debug = false;
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif build.state == 1 then -- routine
|
||||
jr = build.jr; kr = build.kr;
|
||||
i=run_routine(i,jr,kr)
|
||||
end
|
||||
|
||||
|
||||
i=i+1; if i>build.n then i = 1 end
|
||||
|
||||
build.i = i -- update execution address
|
||||
build.jr = jr;
|
||||
build.kr = kr;
|
||||
|
||||
end
|
||||
|
||||
if build.debug then self.label("["..build.state .. "]" .. build.i .. "\n" .. string.sub(prog,build.i)) end
|
||||
build.run()
|
50
scripts/server mods/chatlog.lua
Normal file
@ -0,0 +1,50 @@
|
||||
--rnd 2017
|
||||
if not logdata then
|
||||
self.label("chatlog bot");
|
||||
_G.minetest.forceload_block(self.pos(),true)
|
||||
n = 500; -- store so many messsages before repeating
|
||||
maxresults = 100 -- display at most 'this' result
|
||||
|
||||
logdata = {}; -- circular array to hold messages
|
||||
idx = 1;
|
||||
insert_log = function(logdata,text) -- store new message
|
||||
idx = idx +1;
|
||||
if idx > n then idx = 1 end
|
||||
logdata[idx] = text;
|
||||
end
|
||||
|
||||
retrieve_log = function(logdata,count,filter) -- return last k messages, with filter only selected messages
|
||||
|
||||
local k = 0;
|
||||
local i=idx; local j=0; local ret = {}
|
||||
|
||||
for j = 1,n do
|
||||
if not logdata[i] then break end
|
||||
if filter and not string.find(logdata[i], filter) then
|
||||
else
|
||||
ret[#ret+1] = logdata[i]
|
||||
k=k+1
|
||||
if k>=count then break end -- enough results
|
||||
end
|
||||
i=i-1; if i < 1 then i = n end
|
||||
end
|
||||
return table.concat(ret,"\n")
|
||||
end
|
||||
|
||||
self.listen(1)
|
||||
end
|
||||
|
||||
speaker, msg = self.listen_msg()
|
||||
if msg then
|
||||
if string.sub(msg,1,4) == "?log" then
|
||||
local j = string.find(msg," ",7); -- find first argument
|
||||
local k;local text;
|
||||
if j then k = tonumber(string.sub(msg,6,j-1)) else k = tonumber(string.sub(msg,6)) end -- if there was first argument find second
|
||||
k = k or maxresults;
|
||||
if j then text = retrieve_log(logdata,k,string.sub(msg,j+1)) else text = retrieve_log(logdata,k) end
|
||||
local form = "size[8,8]".. "textarea[0.,0;11.,9.5;text;chatlog;".. text .. "]"
|
||||
self.show_form(speaker, form)
|
||||
else
|
||||
insert_log(logdata, os.date("%X") .. " " .. speaker .. "> " .. msg)
|
||||
end
|
||||
end
|
42
scripts/server mods/colored chat.lua
Normal file
@ -0,0 +1,42 @@
|
||||
-- with current mods there are 4 registered chat responses so we add 5th
|
||||
-- CHANGE COLOR OF CHAT FOR CERTAIN PLAYERS
|
||||
|
||||
if not rom.color_chat_messages then rom.color_chat_messages = 1+#minetest.registered_on_chat_messages end
|
||||
|
||||
colors = {"cyan", "LawnGreen"}
|
||||
chatgroup = {}; -- players in here will see chat without colors
|
||||
--say("chat " .. rom.chat_messages)
|
||||
|
||||
minetest.registered_on_chat_messages[rom.color_chat_messages] =
|
||||
function(name,message)
|
||||
if message == "nocolor" then
|
||||
chatgroup[name] = not chatgroup[name]
|
||||
minetest.chat_send_all("colored chat display " .. (chatgroup[name] and "DISABLED" or "ENABLED") .. " for " .. name)
|
||||
return false
|
||||
else
|
||||
--message = os.date("%X") .. " " .. name .." <> " .. message;
|
||||
local newmessage = "["..name .."] " .. message;
|
||||
local player = minetest.get_player_by_name(name);
|
||||
local pos1 = player:get_pos();
|
||||
|
||||
for _,player in pairs(minetest.get_connected_players()) do
|
||||
local name = player:get_player_name()
|
||||
local pos2 = player:get_pos();
|
||||
local dist = math.sqrt((pos2.x-pos1.x)^2+(pos2.y-pos1.y)^2+ (pos2.z-pos1.z)^2)
|
||||
local length = string.len(name);
|
||||
local color = 1; -- default
|
||||
if (chatgroup[name] or dist>32 or dist == 0) then color = 0 end
|
||||
if string.find(message,string.lower(name)) then color = 2 end
|
||||
|
||||
if color == 0 then
|
||||
minetest.chat_send_player(name, newmessage)
|
||||
else
|
||||
minetest.chat_send_player(name, minetest.colorize(colors[color], newmessage))
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
self.remove()
|
335
scripts/simulators/genetic_trust.lua
Normal file
@ -0,0 +1,335 @@
|
||||
if not init then
|
||||
rom.best_player = nil
|
||||
init = true
|
||||
depth = 4; -- how many moves does random player evaluate
|
||||
error_rate = 0.25; -- players make wrong decision with this probability
|
||||
generation = 10; -- how many times we repeat
|
||||
steps = 100; -- how many steps each generation
|
||||
bestc = 0; -- how many times was new best player picked
|
||||
mode = 2; -- 1, evolution!, 2 play game
|
||||
|
||||
-- game pay offs
|
||||
rules = {
|
||||
{
|
||||
{2., 2.}, -- first player cooperate, second player cooperate: +2,+2
|
||||
{-1, 3}, -- first player cooperate, second player cheat: -1,+3
|
||||
},
|
||||
{
|
||||
{3,-1}, -- first player cheats, second player cooperate
|
||||
{0,0}, -- first player cheats, second player cheat
|
||||
}
|
||||
};
|
||||
|
||||
copytable = function(tab)
|
||||
if type(tab)~="table" then return tab end
|
||||
local ret = {};
|
||||
for k,v in pairs(tab) do ret[k] = copytable(v) end return
|
||||
ret
|
||||
end
|
||||
|
||||
copycat = {
|
||||
rules = {
|
||||
{0}, -- initial: 0 = cooperate
|
||||
{0,1}, -- after one move : cooperate if other cooperated last turn, cheat if other cheated last turn
|
||||
},
|
||||
-- encode decision sequence in binary: 01110 = dec 8+4+2=14
|
||||
memory = 1, -- how many moves back player consideres?
|
||||
moves ={}, -- opponent moves
|
||||
mood = 0, -- probability that player will cheat if he had intention to cooperate
|
||||
moody = 0.5, -- by how much cheat/cooperate change mood
|
||||
points = 0,
|
||||
name = "copycat"
|
||||
}
|
||||
|
||||
cheater = {
|
||||
rules = {
|
||||
{1},
|
||||
},
|
||||
memory = 0, -- how many moves back player consideres?
|
||||
moves ={}, -- opponent moves
|
||||
mood = 0,
|
||||
moody = 0.5,
|
||||
points = 0,
|
||||
name = "cheater"
|
||||
}
|
||||
|
||||
realplayer = {
|
||||
rules = {
|
||||
{0},
|
||||
},
|
||||
memory = 0, -- how many moves back player consideres?
|
||||
moves ={}, -- opponent moves
|
||||
mood = 0,
|
||||
moody = 0.,
|
||||
points = 0,
|
||||
name = "real player",
|
||||
real = true,
|
||||
out = 0
|
||||
}
|
||||
|
||||
|
||||
create_random_player = function(memory)
|
||||
local rules = {};
|
||||
for i = 1, memory+1 do
|
||||
rules[i] = {};
|
||||
for j = 1,2^(i-1) do
|
||||
rules[i][j] = math.random(2)-1
|
||||
end
|
||||
end
|
||||
return {rules = rules, memory = memory, moves = {}, mood = 0, moody = math.random(), points = 0, name = "randomplayer"}
|
||||
end
|
||||
|
||||
|
||||
-- player makes next move according to his memory of moves so far
|
||||
play = function(player)
|
||||
if player.real then return player.out end -- real player
|
||||
local moves = player.moves;
|
||||
local n = #moves;
|
||||
if n > player.memory then n = player.memory end -- there are many moves, examine only what we can
|
||||
local rules = player.rules[n+1]
|
||||
local state = bin2dec(player.moves,n); -- examine last n moves
|
||||
--say("n " .. n .. " state " .. state)
|
||||
return rules[state+1]
|
||||
end
|
||||
|
||||
|
||||
|
||||
group_play = function(playrs) -- each randomplayer plays with every other player in lower group
|
||||
local n = #playrs;
|
||||
local m = 10; -- play m games with each opponent, random pair order
|
||||
for i = 1,10 do
|
||||
for j = 11,20 do -- i plays with j, randomized order
|
||||
playrs[i].moves = {}; playrs[j].moves = {}; -- reset remembered moves before paired match!
|
||||
playrs[i].mood = 0;
|
||||
for k = 1,m do
|
||||
if math.random(2) == 1 then
|
||||
interact(playrs[i],playrs[j])
|
||||
else
|
||||
interact(playrs[j],playrs[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
sort_players = function(pl)
|
||||
table.sort(pl,
|
||||
function(p1,p2) return p1.points<p2.points end
|
||||
) -- sorts in ascending order of points
|
||||
end
|
||||
|
||||
genetics = function(playrs)
|
||||
local population = {}
|
||||
for i = 11,20 do -- pick out only "randomplayer"s
|
||||
population[#population+1] = playrs[i] --copytable()
|
||||
end
|
||||
|
||||
sort_players(population); -- sort from worst to best
|
||||
for i = 1,5 do -- replace 5 worst players with new random players
|
||||
local points2 = population[#population-i+1].points; -- points of i-th best player
|
||||
if points2 then
|
||||
local points1 = population[i].points;
|
||||
if points1<points2 or (points1==points2 and math.random(2) == 1) then --
|
||||
|
||||
population[i] = create_random_player(depth);
|
||||
--if i == 1 then say(serialize(population[i].rules)) end
|
||||
population[i].points = 0 --points2;
|
||||
population[i].name = "randomplayer"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--say("best random points " .. population[#population].points)
|
||||
for i = 11,20 do -- pick out only "randomplayer"s
|
||||
playrs[i] = population[i-10]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- paired interaction: player1 plays with player2, then player2 plays with player1
|
||||
interact = function(player1,player2)
|
||||
local res1 = play(player1);
|
||||
local mood = player1.mood;
|
||||
|
||||
if not player1.real and math.random(1000)<=1000*error_rate then res1=1-res1 end
|
||||
if res1 == 0 and math.random()<= mood then res1 = 1 end
|
||||
|
||||
local moves = player2.moves; moves[#moves+1 ] = res1;
|
||||
--say("1 moves : " .. serialize(moves) .. " res1 " .. res1)
|
||||
local res2 = play(player2);
|
||||
|
||||
mood = player2.mood;
|
||||
if math.random(1000)<=1000*error_rate then res2=1-res2 end
|
||||
if res2 == 0 and math.random()<= mood then res2 = 1 end
|
||||
|
||||
moves = player1.moves; moves[#moves+1] = res2;
|
||||
--say("2 moves : " .. serialize(moves) .. " res2 " .. res2)
|
||||
local res = rules[res1+1][res2+1];
|
||||
player1.points = player1.points + res[1];
|
||||
player2.points = player2.points + res[2];
|
||||
|
||||
if res2 == 0 then -- player2 cooperated, mood change
|
||||
if res1==1 then
|
||||
mood = mood + player2.moody;
|
||||
else
|
||||
mood = mood - player2.moody;
|
||||
end
|
||||
if mood>1 then mood = 1 elseif mood<0 then mood = 0 end
|
||||
player2.mood = mood
|
||||
end
|
||||
|
||||
if res1 == 0 then -- mood change for player1
|
||||
mood = player1.mood;
|
||||
if res2==1 then
|
||||
mood = mood + player1.moody;
|
||||
else
|
||||
mood = mood - player1.moody;
|
||||
end
|
||||
if mood>1 then mood = 1 elseif mood<0 then mood = 0 end
|
||||
player1.mood = mood
|
||||
end
|
||||
end
|
||||
|
||||
dec2bin = function(input)
|
||||
local ret = {};
|
||||
if input == 0 then return {0} end
|
||||
while input~=0 do
|
||||
local r=input%2; input = (input -r)/2
|
||||
ret[#ret+1] = r;
|
||||
end
|
||||
local n = #ret;
|
||||
local rret = {}
|
||||
for i = 1, n do
|
||||
rret[i]=ret[n-i+1]
|
||||
end
|
||||
return rret
|
||||
end
|
||||
|
||||
bin2dec = function(bin, length) -- length= how many last elements we take
|
||||
if length == 0 then return 0 end
|
||||
if not length then length = #bin end
|
||||
local offset = #bin - length; if offset<0 then offset = 0 end
|
||||
local ret = 0;
|
||||
for i = 1,#bin-offset do
|
||||
ret = 2*ret + bin[i+offset]
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
get_results = function(players)
|
||||
local ret = {} for i=1,#players do ret[i] = players[i].name .. " " .. players[i].points .. "M " .. players[i].mood .. "(" .. players[i].moody .. ")" end return table.concat(ret,"\n")
|
||||
end
|
||||
|
||||
|
||||
players = {} -- start with 5 cheaters, 5 copycats and 10 randomplayers
|
||||
|
||||
if mode == 1 then
|
||||
for i = 1,5 do players[i] = copytable(cheater) end
|
||||
for i = 1,5 do players[5+i] = copytable(copycat) end
|
||||
|
||||
for i = 1,10 do players[10+i] = create_random_player(depth) end -- last 10 players are random
|
||||
|
||||
|
||||
age = 0
|
||||
rom.best_player = nil;
|
||||
elseif mode == 2 then
|
||||
players = {copytable(realplayer), copytable( create_random_player(4) ) } ;
|
||||
self.listen(1)
|
||||
end
|
||||
--players[20] = copytable(rom.best_player) or create_random_player(depth) -- add best player from before
|
||||
--players[20].name = "randomplayer"
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
if mode == 1 then
|
||||
|
||||
local bestpoints = 0
|
||||
for k = 1, generation do -- repeat experiment generation*
|
||||
|
||||
--rom.best_player = nil
|
||||
for i = 1,#players do players[i].points = 0 end
|
||||
|
||||
|
||||
for j = 1, steps do -- several steps to see who is best long term on average
|
||||
--for i = 1,#players do players[i].points = 0 end
|
||||
group_play(players)
|
||||
end
|
||||
|
||||
bestpoints = 0
|
||||
|
||||
genetics(players) -- remove 5 worst randomplayers & replace them by new randoms
|
||||
|
||||
|
||||
local population = {}
|
||||
bestrandom = 0
|
||||
for i = 11,20 do
|
||||
if players[i].points >= bestrandom then bestrandom = players[i].points end
|
||||
population[#population+1] = players[i]
|
||||
end
|
||||
--say("randomplayer population size " .. #population)
|
||||
sort_players(population);
|
||||
if rom.best_player then
|
||||
lastbest = rom.best_player.points
|
||||
if bestrandom >= lastbest then -- PICK NEW BEST
|
||||
bestc = bestc+1
|
||||
rom.best_player = copytable(population[#population]); -- remember best randomplayer for next experiment
|
||||
end
|
||||
else
|
||||
rom.best_player = copytable(population[#population]); -- remember best randomplayer for next experiment
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
age = age + generation
|
||||
|
||||
--display results!
|
||||
msg = ""
|
||||
msg = msg .. "Planet Earth (galactical name Halfwits), age " .. age .. ", error_rate " .. error_rate .. ", steps " .. steps .. ", generations " .. generation .. ":\n"
|
||||
msg = msg .."\nlast round\n"
|
||||
--sort_players(players)
|
||||
bestpoints = 0
|
||||
for i =1,#players do
|
||||
if players[i].points>bestpoints then bestpoints = players[i].points end
|
||||
msg = msg .. players[i].name .. ": " .. players[i].points .. " M " .. players[i].mood .. "(" .. players[i].moody .. ")\n"
|
||||
end
|
||||
local rules = rom.best_player.rules;
|
||||
msg = msg .. "BEST: " .. bestpoints .. "\n\n## alltime best random player no. " .. bestc .. ", points " .. rom.best_player.points .. " moody " .. rom.best_player.moody ..
|
||||
"\ncurrent best random player/max current score: ".. math.floor(100*bestrandom/bestpoints*100)/100 .. "% \nrules " .. serialize(rules) .. " )\n";
|
||||
|
||||
local msg1 = "{" .. rules[1][1] .. "}\n"
|
||||
for i = 2,#rules do
|
||||
local rule = rules[i];
|
||||
msg1 = msg1.. "{"
|
||||
for j = 1,#rule do
|
||||
local rule_string = table.concat(dec2bin(j-1),"");
|
||||
rule_string = string.rep("0",i-string.len(rule_string)-1) .. rule_string;
|
||||
msg1 = msg1 .. rule_string .."="..rule[j]..","
|
||||
end
|
||||
msg1 = msg1 .. "}\n"
|
||||
end
|
||||
|
||||
self.label(msg .. msg1)
|
||||
|
||||
elseif mode == 2 then -- play vs player
|
||||
|
||||
speaker,msg = self.listen_msg();
|
||||
if msg then
|
||||
if msg == "0" or msg == "1" then
|
||||
local nextmove = tonumber(msg);
|
||||
if nextmove == 1 and players[1].out == 1 then
|
||||
say("look buddy, we dont like cheaters around here. dont cheat twice in a row")
|
||||
else
|
||||
players[1].out = nextmove
|
||||
interact(players[1], players[2])
|
||||
say("input " .. players[1].out .. ", BOT MOVES: " .. serialize(players[1].moves) .. " BOT MOOD " .. players[2].mood .. "(" .. players[2].moody .. ") SCORE: you " .. players[1].points .. " bot " .. players[2].points)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
153
scripts/simulators/group_assembly.lua
Normal file
@ -0,0 +1,153 @@
|
||||
-- rnd's robot swarm assembly algorithm 2017
|
||||
-- https://www.youtube.com/watch?v=xK54Bu9HFRw&feature=youtu.be&list=PLC7119C2D50BEA077
|
||||
-- notes:
|
||||
-- 1. limitation: there must be room for diagonal move
|
||||
-- this is not big limitation: assume bots are circles of radius 1, then to allow diagonal movement
|
||||
-- just spread them by factor sqrt(2)~1.4 initially
|
||||
-- 2. initial random placement(not part of move algorithm): due to collision some bots may occupy same place
|
||||
|
||||
if not pos then
|
||||
n=50; m = 500;
|
||||
stuck = m;
|
||||
state = 0;
|
||||
step = 0
|
||||
|
||||
pos = {}; tpos = {};
|
||||
-- INIT
|
||||
for i = 1, m do
|
||||
--local r = i % n;local c = (i-r)/n;pos[i]={n-c,r+1}; -- regular rectangle shape
|
||||
pos[i]={math.random(n),math.random(n)};
|
||||
--tpos[i]={math.random(n),math.random(n)}; -- random shape
|
||||
local r = i % n;local c = (i-r)/n;tpos[i]={c+1,r+1}; -- regular rectangle shape
|
||||
end
|
||||
doswap = true -- use closest swap or not?
|
||||
|
||||
-- initially swap ids so that i-th bot is closest to i-th target
|
||||
permute2closest = function()
|
||||
-- swap bot i with one closest to i-th target
|
||||
free = {}; for i = 1, m do free[i] = i end -- list of available ids for swapping
|
||||
local opos = {};
|
||||
for i=1,m do opos[i] = {pos[i][1],pos[i][2]} end
|
||||
closest = {};
|
||||
|
||||
for i = 1,m do
|
||||
-- find closest bot to i-th point
|
||||
local dmin = 2*n;
|
||||
local jmin = -1;
|
||||
local tp = tpos[i];
|
||||
for j = 1,#free do
|
||||
local p = opos[free[j]];
|
||||
local d = math.sqrt((p[1]-tp[1])^2+(p[2]-tp[2])^2);
|
||||
if d< dmin then dmin = d; jmin = j end
|
||||
end
|
||||
if jmin>0 then
|
||||
local newj = free[jmin];
|
||||
pos[i] = {opos[newj][1], opos[newj][2]}; -- reassign id
|
||||
table.remove(free,jmin);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if doswap then
|
||||
permute2closest()
|
||||
else
|
||||
for i=1,m do pos[i] = opos[i] end -- just copy positions
|
||||
end
|
||||
|
||||
data = {};
|
||||
|
||||
for i = 1,n do data[i]={}; for j=1,n do data[i][j] = {0,0,1} end end -- 0/1 present, id, move status?
|
||||
for i = 1,#pos do data[pos[i][1]][pos[i][2]] = {1,i,1} end -- 1=present,i = id, 1=move status
|
||||
|
||||
|
||||
step_move = function()
|
||||
local count = 0;
|
||||
for i = 1, #pos do
|
||||
local p = pos[i];
|
||||
local tp = tpos[i];
|
||||
local x = tp[1]-p[1];
|
||||
local y = tp[2]-p[2];
|
||||
local d = math.sqrt(x^2+y^2);
|
||||
if d~=0 then
|
||||
x=x/d;y=y/d
|
||||
x=p[1]+x;y=p[2]+y;
|
||||
x=math.floor(x+0.5);y=math.floor(y+0.5);
|
||||
if data[x][y][1]==0 then -- target is empty
|
||||
data[p[1]][p[2]][1] = 0; data[x][y][1] = 1
|
||||
pos[i]={x,y}; data[x][y][2] = i; data[x][y][3] = 1;
|
||||
end
|
||||
else
|
||||
data[p[1]][p[2]][3] = 0 -- already at position
|
||||
count = count +1
|
||||
end
|
||||
end
|
||||
return m-count -- how many missaligned
|
||||
end
|
||||
|
||||
render = function()
|
||||
out = "";
|
||||
for i = 1,n do
|
||||
for j= 1,n do
|
||||
if data[i][j][1]==1 then
|
||||
local id = data[i][j][2]; id = id % 10;
|
||||
if data[i][j][3] == 0 then
|
||||
out = out .. id
|
||||
else
|
||||
out = out .. "S" -- didnt move last step
|
||||
end
|
||||
else
|
||||
out = out .. "_" -- empty
|
||||
end
|
||||
end
|
||||
out = out .. "\n"
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
s=1
|
||||
self.listen(1)
|
||||
end
|
||||
|
||||
speaker,msg = self.listen_msg()
|
||||
if speaker == "rnd" then
|
||||
if msg == "p" then
|
||||
say("permute2closest()")
|
||||
permute2closest()
|
||||
end
|
||||
end
|
||||
|
||||
if s == 1 then
|
||||
step = step + 1
|
||||
local c = step_move();
|
||||
--state = how many times stuck count was constant; if more than 3x then perhaps it stabilized?
|
||||
-- stuck = how many robots not yet in position
|
||||
if c<stuck then stuck = c else state = state + 1; if state > 3 then state = 0 s = 2 end end
|
||||
self.label(render().. "\nleft " .. stuck .. "="..(100*stuck/m) .. "%")
|
||||
if stuck == 0 then say("*** COMPLETED! in " .. step .." steps ***") s = 3 end
|
||||
elseif s == 2 then
|
||||
-- do swaps of stuck ones..
|
||||
for i = 1, #pos do
|
||||
local p = {pos[i][1], pos[i][2]};
|
||||
local tp = tpos[i];
|
||||
local x = tp[1]-p[1];
|
||||
local y = tp[2]-p[2];
|
||||
local d = math.sqrt(x^2+y^2);
|
||||
if d~=0 then
|
||||
x=x/d;y=y/d
|
||||
x=p[1]+x;y=p[2]+y;
|
||||
x=math.floor(x+0.5);y=math.floor(y+0.5); -- see whats going on in attempted move direction
|
||||
if data[x][y][1]==1 then -- there is obstruction, do id swap
|
||||
local x1,y1;
|
||||
x1 = x; y1 = y;
|
||||
local idb = data[x][y][2]; -- blocker id, stuck robot id is i
|
||||
pos[i]={x,y}; -- new position for id-i is position of blocker
|
||||
pos[idb] = {p[1],p[2]}; -- new position for blocker is position of stuck robot
|
||||
-- reset stuck status
|
||||
data[x][y][3]=1;data[p[1]][y][p[2]]=1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
s=1
|
||||
end
|
||||
--TO DO: if robots stuck do permute2closest again
|
132
scripts/simulators/layout_designer.lua
Normal file
@ -0,0 +1,132 @@
|
||||
-- rnd 2017
|
||||
if not data then
|
||||
m=50;n=50; minescount = m*n/14;
|
||||
|
||||
t0 = _G.minetest.get_gametime();
|
||||
rom.data = {}; rom.rooms = {} -- so we dont make new tables everytime
|
||||
data = rom.data; spawnpos = self.spawnpos();
|
||||
rooms = rom.rooms;
|
||||
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
|
||||
|
||||
get_mine_count = function(i,j)
|
||||
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
|
||||
for k = -1,1 do for l = -1,1 do
|
||||
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
|
||||
-- generate level data
|
||||
for i = 1,m do rooms[i]={}; for j = 1,n do
|
||||
if get_mine_count(i,j) > 0 or (data[i] and data[i][j] == 1) then
|
||||
rooms[i][j] = 1
|
||||
else
|
||||
rooms[i][j] = 0
|
||||
end
|
||||
end end
|
||||
|
||||
|
||||
-- find passages
|
||||
for i = 2,m-1 do for j = 2,n-1 do
|
||||
if rooms[i][j] == 0 then
|
||||
local A11 = rooms[i-1][j-1]; local A21 = rooms[i][j-1];local A31 = rooms[i+1][j-1];
|
||||
local A12 = rooms[i-1][j]; local A32 = rooms[i+1][j];
|
||||
local A13 = rooms[i-1][j+1]; local A23 = rooms[i][j+1];local A33 = rooms[i+1][j+1];
|
||||
|
||||
if (A12~=1 and A32~=1 and A21 == 1 and A23 == 1) or
|
||||
(A12==1 and A32==1 and A21 ~= 1 and A23 ~= 1)
|
||||
then
|
||||
rooms[i][j] = 2; -- passage
|
||||
end
|
||||
end
|
||||
end end
|
||||
|
||||
read_room = function(i,j)
|
||||
if i<1 or i > m then return nil end
|
||||
if j<1 or j > n then return nil end
|
||||
return rooms[i][j]
|
||||
end
|
||||
|
||||
render_rooms = function()
|
||||
for i = 1,m do for j = 1,n do
|
||||
local tile = rooms[i][j];
|
||||
if tile == 0 then
|
||||
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "air"})
|
||||
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+j}, {name = "air"})
|
||||
elseif tile == 1 then
|
||||
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "basic_robot:buttonFFFFFF"})
|
||||
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+j}, {name = "basic_robot:buttonFFFFFF"})
|
||||
elseif tile == 2 then -- passage, insert 1 door in it
|
||||
--determine direction
|
||||
local dir = {0,0}
|
||||
if read_room(i+1,j) == 2 then dir = {1,0}
|
||||
elseif read_room(i-1,j) == 2 then dir = {-1,0}
|
||||
elseif read_room(i,j+1) == 2 then dir = {0,1}
|
||||
elseif read_room(i,j-1) == 2 then dir = {0,-1}
|
||||
elseif read_room(i-1,j) ~= 0 or read_room(i+1,j) ~= 0 then dir = {0,1}
|
||||
else dir = {1,0}
|
||||
end
|
||||
local k1 = 0; local k2 = 0;
|
||||
for k = 1, 10 do
|
||||
if read_room(i+dir[1]*k,j+dir[2]*k)~= 2 then k1 = k-1 break
|
||||
else
|
||||
if rooms[i+dir[1]*k] then rooms[i+dir[1]*k][j+dir[2]*k] = 0 end
|
||||
_G.minetest.swap_node({x=spawnpos.x+i+dir[1]*k,y=spawnpos.y,z=spawnpos.z+j+dir[2]*k}, {name = "air"})
|
||||
end
|
||||
end
|
||||
for k = 1, 10 do
|
||||
if read_room(i-dir[1]*k,j-dir[2]*k)~= 2 then k2 = -(k-1) break
|
||||
else
|
||||
if rooms[i+dir[1]*k] then rooms[i+dir[1]*k][j+dir[2]*k] = 0 end
|
||||
_G.minetest.swap_node({x=spawnpos.x+i-dir[1]*k,y=spawnpos.y,z=spawnpos.z+j-dir[2]*k}, {name = "air"})
|
||||
end
|
||||
end
|
||||
local k = math.floor((k1+k2)/2);
|
||||
--place door
|
||||
local param = 1
|
||||
if dir[1]~=0 then param = 2 end
|
||||
_G.minetest.swap_node({x=spawnpos.x+i+dir[1]*k,y=spawnpos.y+1,z=spawnpos.z+j+dir[2]*k}, {name = "air"})
|
||||
if param == 1 then
|
||||
_G.minetest.swap_node({x=spawnpos.x+i+dir[1]*k,y=spawnpos.y,z=spawnpos.z+j+dir[2]*k}, {name = "doors:door_wood_a", param2 = 2})
|
||||
else
|
||||
_G.minetest.swap_node({x=spawnpos.x+i+dir[1]*k,y=spawnpos.y,z=spawnpos.z+j+dir[2]*k}, {name = "doors:door_wood_a", param2 = 1})
|
||||
end
|
||||
|
||||
|
||||
|
||||
elseif tile == 3 then
|
||||
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "default:stonebrick"})
|
||||
|
||||
end
|
||||
end end
|
||||
end
|
||||
|
||||
render_rooms()
|
||||
|
||||
|
||||
fill_room = function(x,y, roomIdx) -- room index: 1,2,3,... will be written as -1,-2,.. in rooms
|
||||
local tile = rooms[i][j];
|
||||
if tile ~= 0 then return false end
|
||||
|
||||
rooms[i][j] = -roomIdx;
|
||||
local stk = {{i,j}}; -- stack to place border tiles
|
||||
local tmpstk = {}; -- temporary stack
|
||||
|
||||
local free = true; -- are there any free room tiles
|
||||
|
||||
while free do
|
||||
|
||||
-- loop all stack tiles
|
||||
for i=1,#stk do
|
||||
local p = stk[i];
|
||||
tile = rooms[p[1]][p[2]];
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
self.remove()
|
70
scripts/simulators/nuclear.lua
Normal file
@ -0,0 +1,70 @@
|
||||
-- rnd 2017
|
||||
if not data then
|
||||
data = {FUEL = 1.8,TV=0,T=0,P=0,E=0;}
|
||||
|
||||
generate_nuclear_power = function(CONTROL,COOLING)
|
||||
if COOLING>1 then COOLING = 1 elseif COOLING<0 then COOLING = 0 end
|
||||
if CONTROL>1 then CONTROL = 1 elseif CONTROL<0 then CONTROL = 0 end
|
||||
--data = ...
|
||||
local FUEL = data.FUEL;
|
||||
local TV = data.TV;
|
||||
local T = data.T;
|
||||
local P = data.P;
|
||||
local E = data.E;
|
||||
|
||||
-- reactor specifications
|
||||
local TVmax = 10000;
|
||||
local FUELC = 2; -- critical mass for fuel
|
||||
local DECAYRATE = 1-0.01; -- how much fuel decays per time step
|
||||
local COOLINGCOEF = 1; -- how efficient is cooling per 1 unit of power (how many degrees are cooled)
|
||||
local PCOEF = 1; -- how efficiently temperature is converted to power
|
||||
|
||||
local TGV = FUEL/(1-(FUEL/FUELC)^2);
|
||||
if TGV>TVmax then TGV = TVmax end; if FUEL>FUELC then TGV = TVmax end -- basic temperature generation speed generated in core
|
||||
TV = TV + TGV* CONTROL - P*COOLING*COOLINGCOEF; -- temperature change speed
|
||||
T = T + TV ;
|
||||
P = 0.5*P + T*PCOEF; P = P - P*COOLING -- produced power
|
||||
FUEL = FUEL*DECAYRATE;
|
||||
if P<0 then P = 0 end if T<0 then T = 0 end E=E+P;
|
||||
|
||||
data.FUEL = FUEL;
|
||||
data.T = T;
|
||||
data.TV = TV;
|
||||
data.P = P;
|
||||
data.E = E
|
||||
return E, P, T, FUEL, TV
|
||||
end
|
||||
|
||||
render = function(data) -- data should be normalized [0,1]
|
||||
local tname = "pix.png";
|
||||
local obj = _G.basic_robot.data[self.name()].obj;
|
||||
local n = 150; local m = n;
|
||||
local length = #data; if length == 0 then return end
|
||||
local hsize = 1; local wsize=hsize;
|
||||
local tex = "[combine:"..(wsize*m).."x"..(hsize*n);
|
||||
for i = 1,length do
|
||||
j=math.floor((1-data[i])*m);
|
||||
local ix = math.floor((i/length)*n)
|
||||
tex = tex .. ":"..(ix*wsize).."," .. (j*hsize) .. "="..tname
|
||||
end
|
||||
obj:set_properties({visual = "sprite",textures = {tex}})
|
||||
end
|
||||
|
||||
tdata = {};
|
||||
COOLING = 0.03
|
||||
|
||||
end
|
||||
|
||||
-- generate_nuclear_power(CONTROL, COOLING)
|
||||
-- CONTROL = 0.20; -- control rods; 1 = no control, between 0 and 1
|
||||
-- COOLING = 0.03; -- how much power assigned for cooling, in ratio of output power P; between 0 and 1
|
||||
|
||||
|
||||
E,P,T,FUEL,TV = generate_nuclear_power(0.2,COOLING)
|
||||
-- cooling strategy
|
||||
if TV < 0 then COOLING = 0.0 elseif T>90 then COOLING = 0.03 else COOLING = 0 end
|
||||
|
||||
tdata[#tdata+1]=math.min(T/100,1);
|
||||
render(tdata)
|
||||
|
||||
self.label( "T " .. T .. "\nTV " .. TV .. "\nP ".. P .. "\nFUEL " .. FUEL .. "\nTOTAL ENERGY PRODUCED " .. E )
|
507
scripts/simulators/redstone_emulator.lua
Normal file
@ -0,0 +1,507 @@
|
||||
-- REDSTONE EMULATOR & EDITOR
|
||||
--v 06/28/2018a
|
||||
|
||||
if not init then
|
||||
local players = find_player(5);
|
||||
if not players then
|
||||
name = "";
|
||||
else
|
||||
name = players[1]
|
||||
player_ = puzzle.get_player(name)
|
||||
local inv = player_:get_inventory();
|
||||
inv:set_stack("main", 8, puzzle.ItemStack("basic_robot:control 1 0 \"@\"")) -- add controller in players inventory
|
||||
--add items for building
|
||||
inv:set_stack("main", 1, puzzle.ItemStack("default:pick_diamond"))
|
||||
inv:set_stack("main", 2, puzzle.ItemStack("basic_robot:button_273 999")) -- switch 9 = 273/274
|
||||
inv:set_stack("main", 3, puzzle.ItemStack("basic_robot:button_275 999")) -- button 7 = 275/276
|
||||
inv:set_stack("main", 4, puzzle.ItemStack("basic_robot:button_277 999")) -- equalizer 61 = 277
|
||||
inv:set_stack("main", 5, puzzle.ItemStack("basic_robot:button_278 999")) -- setter 15 = 278
|
||||
inv:set_stack("main", 6, puzzle.ItemStack("basic_robot:button_279 999")) -- piston 171 = 279
|
||||
inv:set_stack("main", 7, puzzle.ItemStack("basic_robot:button_282 999")) -- delayer 232 = 282
|
||||
inv:set_stack("main", 9, puzzle.ItemStack("basic_robot:button_281 999")) -- NOT 33 = 281
|
||||
inv:set_stack("main", 10, puzzle.ItemStack("basic_robot:button_280 999")) -- diode 175 = 280
|
||||
inv:set_stack("main", 11, puzzle.ItemStack("basic_robot:button_283 999")) -- platform 22 = 283
|
||||
inv:set_stack("main", 12, puzzle.ItemStack("basic_robot:button_284 999")) -- giver 23 150/284
|
||||
inv:set_stack("main", 13, puzzle.ItemStack("basic_robot:button_285 999")) -- checker 24 151/285
|
||||
|
||||
|
||||
local round = math.floor; protector_position = function(pos) local r = 32;local ry = 2*r; return {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry,z=round(pos.z/r+0.5)*r}; end
|
||||
local spawnblock = protector_position(self.spawnpos())
|
||||
|
||||
local meta = puzzle.get_meta(spawnblock);
|
||||
meta:set_string("shares", name) -- add player to protection!
|
||||
puzzle.chat_send_player(name,colorize("yellow","#EDITOR: if you need any blocks get them by using 'give me' in craft guide. you can now use controller to make links from pointed at blocks. In addition hold SHIFT to display infos. Reset block links by selecting block 2x"))
|
||||
end
|
||||
|
||||
init = true
|
||||
self.spam(1)
|
||||
self.label(colorize("orange","REDSTONE EMULATOR/EDITOR"))
|
||||
|
||||
|
||||
|
||||
|
||||
-- 1. EMULATOR CODE
|
||||
|
||||
|
||||
TTL = 16 -- signal propagates so many steps before dissipate
|
||||
--self.label(colorize("red","REDSTONE")..colorize("yellow","EMULATOR"))
|
||||
opcount = 0;
|
||||
|
||||
-- DEFINITIONS OF BLOCKS THAT CAN BE ACTIVATED
|
||||
toggle_button_action = function(mode,pos,ttl) -- SIMPLE TOGGLE BUTTONS - SWITCH
|
||||
if not ttl or ttl <=0 then return end
|
||||
if mode == 1 then -- turn on
|
||||
puzzle.set_node(pos,{name = "basic_robot:button_274"})
|
||||
local meta = puzzle.get_meta(pos);
|
||||
if not meta then return end
|
||||
local n = meta:get_int("n");
|
||||
for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
|
||||
else -- turn off
|
||||
puzzle.set_node(pos,{name = "basic_robot:button_273"})
|
||||
local meta = puzzle.get_meta(pos);
|
||||
if not meta then return end
|
||||
local n = meta:get_int("n");
|
||||
for i = 1,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
button_action = function(mode,pos,ttl) -- SIMPLE ON BUTTON, TOGGLES BACK OFF after 1s
|
||||
if not ttl or ttl <=0 then return end
|
||||
if mode == 0 then return end
|
||||
puzzle.set_node(pos,{name = "basic_robot:button_276"})
|
||||
local meta = puzzle.get_meta(pos);
|
||||
if not meta then return end
|
||||
local n = meta:get_int("n");
|
||||
|
||||
minetest.after(1, function()
|
||||
puzzle.set_node(pos,{name = "basic_robot:button_275"})
|
||||
for i = 1,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
|
||||
end)
|
||||
for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
|
||||
end
|
||||
|
||||
giver_action = function(mode,pos,ttl) -- GIVER: give block below it to player and activate targets
|
||||
local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name;
|
||||
if nodename == "air" then return end
|
||||
local objects = minetest.get_objects_inside_radius(pos, 5);local player1;
|
||||
for _,obj in pairs(objects) do if obj:is_player() then player1 = obj; break end end
|
||||
if player1 then
|
||||
player1:get_inventory():add_item("main", puzzle.ItemStack(nodename))
|
||||
local meta = puzzle.get_meta(pos);
|
||||
if not meta then return end
|
||||
local n = meta:get_int("n");
|
||||
for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
|
||||
end
|
||||
end
|
||||
|
||||
checker_action = function(mode,pos,ttl) -- CHECKER: check if player has block below it, then remove block from player and activate targets
|
||||
local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name;
|
||||
if nodename == air then return end
|
||||
local objects = minetest.get_objects_inside_radius(pos, 5);local player1;
|
||||
for _,obj in pairs(objects) do if obj:is_player() then player1 = obj; break end end
|
||||
if player1 then
|
||||
local inv = player1:get_inventory();
|
||||
if inv:contains_item("main", puzzle.ItemStack(nodename)) then
|
||||
inv:remove_item("main",puzzle.ItemStack(nodename))
|
||||
local meta = puzzle.get_meta(pos);
|
||||
if not meta then return end
|
||||
local n = meta:get_int("n");
|
||||
for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
equalizer_action = function(mode,pos,ttl) -- CHECK NODES AT TARGET1,TARGET2. IF EQUAL ACTIVATE TARGET3,TARGET4,...
|
||||
if not ttl or ttl <=0 then return end
|
||||
if mode == 0 then return end
|
||||
|
||||
local meta = puzzle.get_meta(pos);
|
||||
if not meta then return end
|
||||
local n = meta:get_int("n");
|
||||
local node1 = puzzle.get_node({x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}).name
|
||||
local node2 = puzzle.get_node({x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z}).name
|
||||
|
||||
|
||||
if node1==node2 then
|
||||
for i = 3,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
|
||||
else
|
||||
for i = 3,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
|
||||
end
|
||||
end
|
||||
|
||||
delayer_action = function(mode,pos,ttl) -- DELAY FORWARD SIGNAL, delay determined by distance of target1 from delayer ( in seconds)
|
||||
if not ttl or ttl <=0 then return end
|
||||
local meta = puzzle.get_meta(pos);
|
||||
if not meta then return end
|
||||
|
||||
local n = meta:get_int("n");
|
||||
local pos1 = {x=meta:get_int("x1"),y=meta:get_int("y1"),z=meta:get_int("z1")}
|
||||
local delay = math.sqrt(pos1.x^2+pos1.y^2+pos1.z^2);
|
||||
|
||||
if delay > 0 then
|
||||
minetest.after(delay, function()
|
||||
if mode == 1 then
|
||||
for i = 2,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
|
||||
else
|
||||
for i = 2,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
diode_action = function(mode,pos,ttl) -- ONLY pass through ON signal
|
||||
if not ttl or ttl <=0 then return end
|
||||
if mode ~= 1 then return end
|
||||
local meta = puzzle.get_meta(pos);
|
||||
if not meta then return end
|
||||
local n = meta:get_int("n");
|
||||
for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
|
||||
end
|
||||
|
||||
not_action = function(mode,pos,ttl) -- negate signal: 0 <-> 1
|
||||
if not ttl or ttl <=0 then return end
|
||||
local meta = puzzle.get_meta(pos);
|
||||
if not meta then return end
|
||||
local n = meta:get_int("n");
|
||||
for i = 1,n do activate(1-mode,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
|
||||
end
|
||||
|
||||
setter_action = function(mode,pos,ttl) -- SETS NODES IN TARGET AREA TO PRESELECTED NODE
|
||||
if not ttl or ttl <=0 then return end
|
||||
if mode == 0 then return end
|
||||
|
||||
local meta = puzzle.get_meta(pos);
|
||||
if not meta then return end
|
||||
local n = meta:get_int("n");
|
||||
if n ~= 3 then say("#setter: error, needs to be set with 3 links"); return end
|
||||
local node1 = puzzle.get_node({x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z})
|
||||
local pos1 = {x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z}
|
||||
local pos2 = {x=meta:get_int("x3")+pos.x,y=meta:get_int("y3")+pos.y,z=meta:get_int("z3")+pos.z}
|
||||
|
||||
if pos1.x>pos2.x then pos1.x,pos2.x = pos2.x,pos1.x end
|
||||
if pos1.y>pos2.y then pos1.y,pos2.y = pos2.y,pos1.y end
|
||||
if pos1.z>pos2.z then pos1.z,pos2.z = pos2.z,pos1.z end
|
||||
|
||||
local size = (pos2.x-pos1.x+1)*(pos2.y-pos1.y+1)*(pos2.z-pos1.z+1)
|
||||
if size > 27 then say("#setter: target area too large, more than 27 blocks!"); return end
|
||||
for x = pos1.x,pos2.x do
|
||||
for y = pos1.y,pos2.y do
|
||||
for z = pos1.z,pos2.z do
|
||||
puzzle.set_node({x=x,y=y,z=z},node1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local piston_displaceable_nodes = {["air"] = 1,["default:water_flowing"] = 1}
|
||||
|
||||
piston_action = function(mode,pos,ttl) -- PUSH NODE AT TARGET1 AWAY FROM PISTON
|
||||
if not ttl or ttl <=0 then return end
|
||||
--if mode == 0 then return end
|
||||
|
||||
local meta = puzzle.get_meta(pos);
|
||||
if not meta then return end
|
||||
local n = meta:get_int("n");
|
||||
if n < 1 or n>2 then say("#piston: error, needs to be set with at least link and most two"); return end
|
||||
local pos1 = {x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}
|
||||
|
||||
-- determine direction
|
||||
local dir = {x=pos1.x-pos.x, y= pos1.y-pos.y, z= pos1.z-pos.z};
|
||||
|
||||
local dirabs = {x=math.abs(dir.x), y= math.abs(dir.y), z= math.abs(dir.z)};
|
||||
local dirmax = math.max(dirabs.x,dirabs.y,dirabs.z);
|
||||
|
||||
if dirabs.x == dirmax then dir = { x = dir.x>0 and 1 or -1, y = 0,z = 0 }
|
||||
elseif dirabs.y == dirmax then dir = { x = 0, y = dir.y>0 and 1 or -1, z=0}
|
||||
else dir = {x = 0, y = 0, z = dir.z>0 and 1 or -1}
|
||||
end
|
||||
|
||||
local pos2 = {x=pos1.x+dir.x,y=pos1.y+dir.y,z=pos1.z+dir.z};
|
||||
|
||||
if mode == 0 then pos1,pos2 = pos2,pos1 end
|
||||
|
||||
local node1 = puzzle.get_node(pos1)
|
||||
if node1.name == "air" then return end
|
||||
|
||||
|
||||
if piston_displaceable_nodes[puzzle.get_node(pos2).name] then
|
||||
puzzle.set_node(pos2, node1)
|
||||
puzzle.set_node(pos1, {name = "air"})
|
||||
minetest.check_for_falling(pos2)
|
||||
self.sound("doors_door_open",1,pos)
|
||||
end
|
||||
end
|
||||
|
||||
platform_action = function(mode,pos,ttl) -- SPAWN MOVING PLATFORM
|
||||
|
||||
if mode~=1 then return end
|
||||
local meta = puzzle.get_meta(pos);
|
||||
if not meta then return end
|
||||
local n = meta:get_int("n");
|
||||
if n ~= 2 then say("#platform: error, needs to be set with 2 targets"); return end
|
||||
local pos1 = {x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}
|
||||
local pos2 = {x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z}
|
||||
|
||||
-- determine direction
|
||||
local dir = {x=pos2.x-pos1.x, y= pos2.y-pos1.y, z= pos2.z-pos1.z};
|
||||
|
||||
local obj = minetest.add_entity(pos1, "basic_robot:projectile");
|
||||
|
||||
if not obj then return end
|
||||
obj:setvelocity(dir);
|
||||
--obj:setacceleration({x=0,y=-gravity,z=0});
|
||||
local luaent = obj:get_luaentity();
|
||||
luaent.name = name;
|
||||
luaent.spawnpos = pos1;
|
||||
|
||||
local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name;
|
||||
local tiles = minetest.registered_nodes[nodename].tiles; tiles = tiles or {};
|
||||
local texture = tiles[1] or "default_stone";
|
||||
obj:set_properties({visual = "cube",textures = {texture,texture,texture,texture,texture,texture},visual_size = {x=1,y=1},
|
||||
collisionbox={-0.5,-0.5,-0.5,0.5,0.5,0.5}})
|
||||
end
|
||||
|
||||
|
||||
-- HOW TO ACTIVATE TARGET ELEMENT - adds mesecons/basic machines compatibility
|
||||
activate = function(mode, pos, ttl)
|
||||
if not ttl or ttl <=0 then return end
|
||||
local nodename = puzzle.get_node(pos).name;
|
||||
local active_element = active_elements[nodename];
|
||||
opcount = opcount + 1
|
||||
if opcount > 64 then say("#puzzle error: opcount 64 exceeded. too many active connections."); error("#puzzle: abort") end
|
||||
|
||||
if active_element then
|
||||
active_element(mode,pos,ttl-1)
|
||||
else -- try mesecons activate
|
||||
local nodename = puzzle.get_node(pos).name
|
||||
local table = minetest.registered_nodes[nodename];
|
||||
if table and table.mesecons then else return end
|
||||
|
||||
local effector=table.mesecons.effector;
|
||||
|
||||
if mode == 1 then
|
||||
if effector.action_on then
|
||||
effector.action_on(pos,node,ttl)
|
||||
end
|
||||
else
|
||||
if effector.action_off then
|
||||
effector.action_off(pos,node,ttl)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- THESE REACT WHEN PUNCHED
|
||||
interactive_elements = {
|
||||
[275] = {button_action,1}, -- BUTTON, 1 means it activates(ON) on punch
|
||||
[273] = {toggle_button_action,1}, -- TOGGLE BUTTON_OFF
|
||||
[274] = {toggle_button_action,0}, -- TOGGLE BUTTON_ON, 0 means it deactivates
|
||||
[284] = {giver_action,0}, -- GIVER: give player item below it when activated and activate targets after that
|
||||
[285] = {checker_action,0}, -- CHECKER: check if player has block below it in inventory, remove it and activate targets after that
|
||||
}
|
||||
|
||||
-- THESE CAN BE ACTIVATED WITH SIGNAL
|
||||
active_elements = {
|
||||
["basic_robot:button_275"] = button_action, -- BUTTON, what action to do on activate
|
||||
["basic_robot:button_273"] = toggle_button_action, -- TOGGLE BUTTON_OFF
|
||||
["basic_robot:button_274"] = toggle_button_action, -- TOGGLE BUTTON_ON
|
||||
["basic_robot:button_278"] = setter_action, -- SETTER
|
||||
["basic_robot:button_277"] = equalizer_action, -- EQUALIZER
|
||||
["basic_robot:button_279"] = piston_action, -- PISTON
|
||||
["basic_robot:button_283"] = platform_action, -- PLATFORM
|
||||
["basic_robot:button_282"] = delayer_action, -- DELAYER
|
||||
["basic_robot:button_280"] = diode_action, -- DIODE
|
||||
["basic_robot:button_281"] = not_action, -- NOT
|
||||
}
|
||||
|
||||
|
||||
-- EDITOR CODE --
|
||||
|
||||
edit = {};
|
||||
edit.state = 1; -- tool state
|
||||
edit.source = {}; edit.sourcenode = ""; -- selected source
|
||||
|
||||
-- blocks that can be activated
|
||||
edit.active_elements = {
|
||||
["basic_robot:button_275"] = "button: now select one or more targets", -- button
|
||||
["basic_robot:button_273"] = "switch: now select one or more targets", -- switch OFF
|
||||
["basic_robot:button_274"] = "switch: now select one or more targets", -- switch ON
|
||||
["basic_robot:button_278"] = "setter: target1 defines what material wall will use, target2/3 defines where wall will be", -- setter
|
||||
["basic_robot:button_277"] = "equalizer: target1 and target2 are for comparison, other targets are activated", -- equalizer
|
||||
["basic_robot:button_279"] = "piston: push block at target1 in direction away from piston", -- equalizer
|
||||
["basic_robot:button_283"] = "platform: select target1 to set origin, target2 for direction", -- PLATFORM
|
||||
["basic_robot:button_282"] = "delayer: distance from delayer to target1 determines delay", -- delayer
|
||||
["basic_robot:button_280"] = "diode: only pass through ON signal", -- DIODE
|
||||
["basic_robot:button_281"] = "NOT gate: negates the signal", -- NOT
|
||||
|
||||
["basic_robot:button_284"] = "GIVER: give player item below it when activated and activate targets after that",
|
||||
["basic_robot:button_285"] = "CHECKER: check if player has block below it in inventory, remove it and activate targets after that",
|
||||
}
|
||||
|
||||
linker_use = function(pos)
|
||||
if not pos then return end
|
||||
|
||||
--say(serialize(player_:get_player_control()))
|
||||
if edit.state < 0 then -- link edit mode!
|
||||
local meta = puzzle.get_meta(edit.source);
|
||||
local i = -edit.state;
|
||||
meta:set_int("x" ..i, pos.x-edit.source.x); meta:set_int("y" ..i, pos.y-edit.source.y); meta:set_int("z" ..i, pos.z-edit.source.z)
|
||||
puzzle.chat_send_player(name, colorize("red", "EDIT ".. " target " .. i .. " changed"))
|
||||
edit.state = 1
|
||||
goto display_particle
|
||||
end
|
||||
|
||||
if player_:get_player_control().sneak then -- SHOW LINKS
|
||||
local meta = puzzle.get_meta(pos);
|
||||
local n = meta:get_int("n");
|
||||
local nodename = puzzle.get_node(pos).name;
|
||||
local active_element = edit.active_elements[nodename]
|
||||
if active_element and edit.source.x == pos.x and edit.source.y == pos.y and edit.source.z == pos.z then -- gui with more info
|
||||
local form = "size[4,"..(0.75*n).."] label[0,-0.25; "..active_element .."]" ;
|
||||
for i = 1,n do -- add targets as lines
|
||||
form = form ..
|
||||
"button[0,".. (0.75*i-0.5) .. ";1,1;".."S"..i..";" .. "show " .. i .. "]"..
|
||||
"button_exit[1,".. (0.75*i-0.5) .. ";1,1;".."E"..i..";" .. "edit " .. "]" ..
|
||||
"button_exit[2,".. (0.75*i-0.5) .. ";1,1;".."D"..i..";" .. "delete " .. "]"..
|
||||
"label[3,".. (0.75*i-0.25) .. "; " .. meta:get_int("x"..i) .. " " .. meta:get_int("y"..i) .. " " .. meta:get_int("z"..i) .."]"
|
||||
end
|
||||
self.show_form(name,form);
|
||||
edit.state = 3;
|
||||
return
|
||||
end
|
||||
edit.source = {x=pos.x,y=pos.y, z=pos.z};
|
||||
edit.state = 1
|
||||
if not active_element then return end
|
||||
local i = string.find(active_element,":");
|
||||
if not i then return end
|
||||
puzzle.chat_send_player(name,colorize("red","#INFO ".. string.sub(active_element,1,i-1) ..":") .." has " .. n .. " targets. Select again for more info.")
|
||||
meta:set_string("infotext",string.sub(active_element,1,i-1)) -- write name of element on it!
|
||||
|
||||
for i = 1, n do
|
||||
minetest.add_particle(
|
||||
{
|
||||
pos = {x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},
|
||||
expirationtime = 5,
|
||||
velocity = {x=0, y=0,z=0},
|
||||
size = 18,
|
||||
texture = "010.png",
|
||||
acceleration = {x=0,y=0,z=0},
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
}
|
||||
)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if edit.state == 1 then -- SET SOURCE
|
||||
local nodename = puzzle.get_node(pos).name;
|
||||
local active_element = edit.active_elements[nodename]
|
||||
if not active_element then puzzle.chat_send_player(name,colorize("red","#ERROR linker:").. " source must be valid element like switch"); return end
|
||||
edit.source = {x=pos.x,y=pos.y, z=pos.z};
|
||||
sourcenode = nodename;
|
||||
puzzle.chat_send_player(name, colorize("yellow","SETUP " ..edit.state .. ": ").. active_element)
|
||||
edit.state = 2
|
||||
else -- SET TARGET
|
||||
local meta = puzzle.get_meta(edit.source);
|
||||
local n = meta:get_int("n");
|
||||
|
||||
if edit.state == 2 and pos.x == edit.source.x and pos.y == edit.source.y and pos.z == edit.source.z then -- RESET LINK FOR SOURCE
|
||||
local meta = puzzle.get_meta(pos);meta:set_int("n",0) -- reset links
|
||||
puzzle.chat_send_player(name, colorize("red", "SETUP " .. edit.state .. ":") .. " resetted links for selected source.")
|
||||
edit.state = 1;return
|
||||
else
|
||||
n=n+1;
|
||||
meta:set_int("x"..n, pos.x-edit.source.x);meta:set_int("y"..n, pos.y-edit.source.y);meta:set_int("z"..n, pos.z-edit.source.z) -- relative to source!
|
||||
meta:set_int("n",n)
|
||||
puzzle.chat_send_player(name, colorize("red", "SETUP "..edit.state .. ":") .. " added target #" .. n)
|
||||
edit.state = 1
|
||||
end
|
||||
end
|
||||
|
||||
-- display
|
||||
::display_particle::
|
||||
|
||||
minetest.add_particle(
|
||||
{
|
||||
pos = pos,
|
||||
expirationtime = 5,
|
||||
velocity = {x=0, y=0,z=0},
|
||||
size = 18,
|
||||
texture = "009.png",
|
||||
acceleration = {x=0,y=0,z=0},
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
tools = {
|
||||
["basic_robot:control"] = linker_use
|
||||
}
|
||||
|
||||
------ END OF EDIT PROGRAM
|
||||
|
||||
end
|
||||
|
||||
opcount = 0
|
||||
event = keyboard.get() -- handle keyboard
|
||||
if event then
|
||||
if event.type == 0 then -- EDITING
|
||||
if event.puncher == name then -- players in protection can edit -- not minetest.is_protected({x=event.x,y=event.y,z=event.z},event.puncher)
|
||||
local wield_item = player_:get_wielded_item():get_name()
|
||||
local tool = tools[wield_item]
|
||||
if tool then tool({x=event.x,y=event.y,z=event.z}) end
|
||||
end
|
||||
else -- EMULATOR
|
||||
local typ = event.type;
|
||||
local interactive_element = interactive_elements[typ]
|
||||
if interactive_element then
|
||||
interactive_element[1](interactive_element[2],{x=event.x,y=event.y,z=event.z},TTL)
|
||||
self.sound("doors_glass_door_open",1,{x=event.x,y=event.y,z=event.z})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
sender,fields = self.read_form() -- handle gui for editing
|
||||
if sender then
|
||||
edit.state = 1
|
||||
for k,_ in pairs(fields) do
|
||||
local c = string.sub(k,1,1);
|
||||
local i = tonumber(string.sub(k,2)) or 1;
|
||||
if c == "S" then
|
||||
|
||||
local meta = puzzle.get_meta(edit.source);
|
||||
minetest.add_particle(
|
||||
{
|
||||
pos = {x=meta:get_int("x"..i)+edit.source.x,y=meta:get_int("y"..i)+edit.source.y,z=meta:get_int("z"..i)+edit.source.z},
|
||||
expirationtime = 5,
|
||||
velocity = {x=0, y=0,z=0},
|
||||
size = 18,
|
||||
texture = "010.png",
|
||||
acceleration = {x=0,y=0,z=0},
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
}
|
||||
)
|
||||
elseif c == "E" then
|
||||
edit.state = -i;
|
||||
puzzle.chat_send_player(name, colorize("yellow", "#EDIT: select target " .. i));
|
||||
elseif c == "D" then
|
||||
local meta = puzzle.get_meta(edit.source);
|
||||
local n = meta:get_int("n")
|
||||
if n > 0 then
|
||||
for j = i,n-1 do
|
||||
meta:set_int("x"..j, meta:get_int("x"..(j+1)))
|
||||
meta:set_int("y"..j, meta:get_int("y"..(j+1)))
|
||||
meta:set_int("z"..j, meta:get_int("z"..(j+1)))
|
||||
end
|
||||
meta:set_int("n",n-1)
|
||||
end
|
||||
puzzle.chat_send_player(name, colorize("red", "#EDIT: target " .. i .. " deleted"));
|
||||
end
|
||||
--say(serialize(fields))
|
||||
end
|
||||
end
|
266
scripts/simulators/trust_game.lua
Normal file
@ -0,0 +1,266 @@
|
||||
-- cheat trust game by rnd, made in 60 minutes
|
||||
-- http://ncase.me/trust/
|
||||
|
||||
--[[
|
||||
TO DO:
|
||||
hierarchies, they determine who plays with who (boss plays only with his direct underlings)
|
||||
fakers: they give fake +points
|
||||
--]]
|
||||
|
||||
|
||||
if not init then
|
||||
init = true
|
||||
if not find_player(6) then error("#TRUST GAME: no players near") end
|
||||
|
||||
rules = {
|
||||
{
|
||||
{2., 2.}, -- first player cooperate, second player cooperate: +2,+2
|
||||
{-1, 3}, -- first player cooperate, second player cheat: -1,+3
|
||||
},
|
||||
{
|
||||
{3,-1}, -- first player cheats, second player cooperate
|
||||
{0,0}, -- first player cheats, second player cheat
|
||||
}
|
||||
};
|
||||
|
||||
error_rate = 0.0; -- 10% probability we take wrong decision
|
||||
|
||||
grudger =
|
||||
{
|
||||
name = "grudger",
|
||||
opponent = {}, -- list of opponent moves from previous games,
|
||||
state = 1, -- internal state, for grudger it means how he plays next move
|
||||
reset = function(self)
|
||||
self.state = 1; self.opponent = {};
|
||||
end,
|
||||
game = function(self) -- how will we play next move
|
||||
if self.state == 1 then
|
||||
local opp = self.opponent;
|
||||
if #opp>0 and opp[#opp] == 2 then self.state = 2 end -- you cheat me once and we are done, pardner.
|
||||
end
|
||||
return self.state
|
||||
end,
|
||||
err = error_rate, -- probability we make different decision
|
||||
}
|
||||
|
||||
cheater =
|
||||
{
|
||||
name = "cheater",
|
||||
opponent = {}, -- list of opponent moves from previous games,
|
||||
reset = function(self)
|
||||
self.opponent = {};
|
||||
end,
|
||||
game = function(self)
|
||||
return 2
|
||||
end,
|
||||
err = error_rate, -- probability we make different decision
|
||||
}
|
||||
|
||||
cooperator =
|
||||
{
|
||||
name = "cooperator",
|
||||
opponent = {},
|
||||
reset = function(self)
|
||||
self.opponent = {};
|
||||
end,
|
||||
game = function(self)
|
||||
return 1
|
||||
end,
|
||||
err = error_rate, -- probability we make different decision
|
||||
}
|
||||
|
||||
copycat =
|
||||
{
|
||||
name = "copycat",
|
||||
opponent = {},
|
||||
reset = function(self)
|
||||
self.opponent = {};
|
||||
end,
|
||||
game = function(self)
|
||||
local opp = self.opponent;
|
||||
if #opp>0 then return opp[#opp] else return 1 end -- i do to you what you did to me last move
|
||||
end,
|
||||
err = error_rate, -- probability we make different decision
|
||||
}
|
||||
|
||||
fcopycat =
|
||||
{
|
||||
name = "forgiving copycat",
|
||||
opponent = {},
|
||||
reset = function(self)
|
||||
self.opponent = {}; self.state = 0; self.cheat = 0;
|
||||
end,
|
||||
state = 0,
|
||||
cheat = 0,
|
||||
game = function(self)
|
||||
local opp = self.opponent;
|
||||
if #opp>0 then
|
||||
if opp[#opp] == 2 then -- you cheat me
|
||||
if self.state == 1 then self.cheat = self.cheat+ 1 else self.state = 1 end -- cheat in a row
|
||||
else
|
||||
self.state = 0
|
||||
self.cheat = 0
|
||||
end
|
||||
if self.cheat >= 1 then -- fk you
|
||||
return 2
|
||||
else -- you cheated me less than 2x, its still cool
|
||||
return 1
|
||||
end
|
||||
else -- first time
|
||||
return 1
|
||||
end
|
||||
end,
|
||||
err = error_rate, -- probability we make different decision
|
||||
}
|
||||
|
||||
|
||||
detective =
|
||||
{
|
||||
name = "detective",
|
||||
opponent = {},
|
||||
moves = {1,2,1,1}, -- starting 4 moves
|
||||
step = 0, -- what move we played so far
|
||||
state = 0,
|
||||
reset = function(self)
|
||||
self.state = 0; self.step = 0; self.opponent = {};
|
||||
end,
|
||||
game = function(self) -- how will we play next move
|
||||
local st = self.step+1;
|
||||
self.step = st;
|
||||
local opp = self.opponent;
|
||||
|
||||
if st < 5 then
|
||||
if self.state == 0 and opp[#opp] == 2 then self.state = 1 end -- i caught you cheating!
|
||||
return self.moves[st]
|
||||
end
|
||||
|
||||
if self.state == 0 then -- exploiter
|
||||
return 2
|
||||
else -- copycat
|
||||
if #opp>0 then return opp[#opp] end -- copycat
|
||||
end
|
||||
return self.state
|
||||
end,
|
||||
err = error_rate, -- probability we make different decision
|
||||
}
|
||||
|
||||
-- internal functions
|
||||
|
||||
Player = {}
|
||||
function Player:new (o)
|
||||
ret = {}; _G.setmetatable(ret, self); self.__index = self
|
||||
for k,v in pairs(o) do ret[k] = v end
|
||||
ret.points = 0
|
||||
return ret
|
||||
end
|
||||
|
||||
gamestep = function(player1,player2)
|
||||
local res1 = player1:game(); if math.random(1000)<=1000*player1.err then res1 = 3-res1 end
|
||||
local opponent = player2.opponent;
|
||||
opponent[#opponent+1] = res1; -- player2 remembers player1 move
|
||||
local res2 = player2:game(); if math.random(1000)<=1000*player2.err then res2 = 3-res2 end
|
||||
opponent = player1.opponent;
|
||||
opponent[#opponent+1] = res2; -- player1 remembers player2 move
|
||||
local res = rules[res1][res2];
|
||||
player1.points = player1.points + res[1];
|
||||
player2.points = player2.points + res[2];
|
||||
return res1,res2 -- return what players did
|
||||
end
|
||||
|
||||
paired_match = function(player1,player2, rounds)
|
||||
player1:reset();player2:reset()
|
||||
for i = 1, rounds do
|
||||
gamestep(player1,player2)
|
||||
end
|
||||
end
|
||||
|
||||
sort_players = function(players)
|
||||
table.sort(players,
|
||||
function(p1,p2) return p1.points<p2.points end
|
||||
) -- sorts in ascending order of points
|
||||
end
|
||||
|
||||
group_match = function(players,elimination) -- each player plays once with every other player
|
||||
local n = #players;
|
||||
local m = 10; -- play m games with each opponent
|
||||
for i = 2,n do
|
||||
for j = 1,i-1 do
|
||||
if math.random(2) == 1 then
|
||||
paired_match(players[i],players[j],m)
|
||||
else
|
||||
paired_match(players[j],players[i],m)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if elimination then
|
||||
sort_players(players)
|
||||
for i = 1,5 do -- replace 5 worse players with clones of 5 best players
|
||||
local points2 = players[#players-i+1].points;
|
||||
if points2 then
|
||||
local points1 = players[i].points;
|
||||
if points1<points2 or (points1==points2 and math.random(2) == 1) then
|
||||
players[i] = Player:new( players[#players-i+1] );
|
||||
players[i].points = points2;
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
get_results = function(players)
|
||||
local msg = colorize("red","player results\n")
|
||||
for j = 1, #players do -- display final results
|
||||
msg = msg .. j .. ". " .. players[j].name .. ": points " .. players[j].points .. "\n"
|
||||
end
|
||||
return msg
|
||||
end
|
||||
|
||||
|
||||
-- SIMULATION START
|
||||
|
||||
self.spam(1)
|
||||
_G.math.randomseed(os.time())
|
||||
|
||||
players = {Player:new(grudger), Player:new(cheater), Player:new(cooperator), Player:new(copycat),Player:new(detective)} --,Player:new(fcopycat)}
|
||||
|
||||
M = 10000; -- do 1000 matches, make average later
|
||||
for i = 1, M do
|
||||
group_match(players) -- SIMPLE GROUP MATCH, everyone play 1x vs everyone else (random player ordering in single match)
|
||||
end
|
||||
for i =1,#players do players[i].points = players[i].points/M end
|
||||
|
||||
|
||||
|
||||
|
||||
-- players = {}
|
||||
-- for i = 1, 5 do players[#players+1 ] = Player:new(grudger) end
|
||||
-- for i = 1, 5 do players[#players+1 ] = Player:new(cheater) end
|
||||
-- for i = 1, 5 do players[#players+1 ] = Player:new(cooperator) end
|
||||
-- for i = 1, 5 do players[#players+1 ] = Player:new(copycat) end
|
||||
-- for i = 1, 5 do players[#players+1 ] = Player:new(detective) end
|
||||
-- group_match(players)
|
||||
|
||||
-- for i = 1, 10 do-- play 10 matches with elimination
|
||||
-- group_match(players,true)
|
||||
-- end
|
||||
|
||||
sort_players(players)
|
||||
self.label("10000 rounds + averaged, error rate = " .. error_rate .."\n" .. get_results(players) .. "comment: under 6.2% error copycats more succesful, but above 6.2% cheaters. at 50% error everyone same")
|
||||
players = nil; -- clear up
|
||||
|
||||
|
||||
--STEP BY STEP DUAL VS 2 PLAYERS
|
||||
-- players = {Player:new(grudger),Player:new(detective)};
|
||||
-- step = 0;
|
||||
-- say("#game start")
|
||||
-- for i = 1, 10 do
|
||||
-- step = step +1
|
||||
-- local res1,res2;
|
||||
-- res1,res2 = gamestep(players[1],players[2])
|
||||
-- say("step " .. step .. ": " .. players[1].name .. " " .. res1 .. " -> " .. players[1].points .. " VS " .. players[2].name .. " " .. res2 .. " -> " .. players[2].points);
|
||||
-- end
|
||||
|
||||
end
|
28
scripts/simulators/turtlebot.lua
Normal file
@ -0,0 +1,28 @@
|
||||
if not cmd then
|
||||
cmd = {
|
||||
["f"] = function() move.forward() end,
|
||||
["b"] = function() move.backward() end,
|
||||
["l"] = function() move.left() end,
|
||||
["r"] = function() move.right() end,
|
||||
["u"] = function() move.up() end,
|
||||
["d"] = function() move.down() end,
|
||||
["a"] = function() activate.forward(1) end,
|
||||
["<"] = function() turn.left() end,
|
||||
[">"] = function() turn.right() end,
|
||||
}
|
||||
i=0;
|
||||
prog = read_text.right(); s=0
|
||||
prog = string.gsub(prog,"%s","");
|
||||
--say(prog)
|
||||
self.label("RUNNING PROGRAM: " .. prog);n=string.len(prog);
|
||||
if string.sub(prog,1,1) == " " then self.label("WRITE A PROGRAM FIRST!") s=1 end
|
||||
|
||||
end
|
||||
|
||||
if s == 0 then
|
||||
i=i+1; if i > n then self.label("PROGRAM ENDED");s=1 end;
|
||||
if s == 0 then
|
||||
c=string.sub(prog,i,i)
|
||||
if cmd[c] then cmd[c]() else self.label("INVALID PROGRAM INSTRUCTION : " .. c) s=1 end
|
||||
end
|
||||
end
|
62
scripts/spawn_quiz.lua
Normal file
@ -0,0 +1,62 @@
|
||||
if not s then
|
||||
s=0
|
||||
t=0
|
||||
option = {"A","B","C","D","E"}
|
||||
generate_question = function()
|
||||
local a = math.random(10)+0;
|
||||
local b = math.random(10)+0;
|
||||
local c = math.random(20)-10;
|
||||
local d = a*b+c;
|
||||
msg = "To get out solve the math problem\n";
|
||||
msg = msg .. colorize("LawnGreen",a.." * "..b.." + "..c .. " = ?\n\n")
|
||||
problem = a.."*"..b.."+"..c .. " = ?";
|
||||
correct = math.random(5);
|
||||
local frm = "";
|
||||
|
||||
for i =1,5 do
|
||||
local offset = 0;
|
||||
if i~=correct then offset = math.random(10)-5; if offset == 0 then offset = -1 end end
|
||||
frm = frm .. "button_exit[".. -0.1+(i-1)*1.25 ..",0.75;1.25,1;" .. i .. ";".. d + offset .. "]"
|
||||
end
|
||||
|
||||
local form = "size[6,1.25]" .. "label[0.05,-0.3;".. msg.."] "..frm .. "button_exit[4.9,-0.25;1.2,1;cancel;cancel]";
|
||||
return form, correct
|
||||
end
|
||||
|
||||
selection = 1;
|
||||
question = "";
|
||||
problem = "";
|
||||
end
|
||||
|
||||
|
||||
if t%4 == 0 then
|
||||
t = 0; form,selection = generate_question();
|
||||
for _,obj in pairs(_G.minetest.get_objects_inside_radius({x=2,y=2,z=0}, 1)) do
|
||||
if obj:is_player() then
|
||||
local pname = obj:get_player_name();
|
||||
self.show_form(pname,form)
|
||||
end
|
||||
end
|
||||
end
|
||||
t=t+1;
|
||||
|
||||
|
||||
sender,fields = self.read_form()
|
||||
if sender then
|
||||
player = _G.minetest.get_player_by_name(sender);
|
||||
if player then
|
||||
|
||||
answer = 0;
|
||||
for i = 1,5 do if fields[_G.tostring(i)] then answer = i end end
|
||||
|
||||
if answer == correct then
|
||||
player:setpos({x=0,y=2,z=3})
|
||||
--inv = player:get_inventory(); inv:add_item("main", "default:apple")
|
||||
--_G.minetest.chat_send_player(sender,"<MATH ROBOT> congratulations, here is an apple.")
|
||||
elseif answer ~= 0 then
|
||||
player:setpos({x=0,y=-6,z=-1})
|
||||
say(sender .. " failed to solve the problem " .. problem)
|
||||
self.show_form(sender, "size[1.25,0.5] label[0,0; WRONG]")
|
||||
end
|
||||
end
|
||||
end
|
112
scripts/tree_harvest.lua
Normal file
@ -0,0 +1,112 @@
|
||||
-- rnd, 2017
|
||||
if not tree then
|
||||
tree = {};
|
||||
wood = "default:tree";
|
||||
support = "default:tree";
|
||||
leaves = "default:leaves";
|
||||
sapling = "default:sapling";
|
||||
|
||||
tree.s = 0 -- searching
|
||||
tree.st = 0
|
||||
end
|
||||
|
||||
tree_step = function()
|
||||
|
||||
if tree.s == 0 then -- search
|
||||
node = read_node.forward()
|
||||
if node == wood then tree.s = 1 end
|
||||
elseif tree.s==1 then -- found
|
||||
dig.forward();
|
||||
move.forward();
|
||||
tree.s=2
|
||||
elseif tree.s==2 then -- dig up
|
||||
node = read_node.up()
|
||||
dig.up()
|
||||
move.up()
|
||||
place.down(support)
|
||||
if node ~= wood then
|
||||
tree.s=3 tree.st = 0
|
||||
end
|
||||
elseif tree.s==3 then -- on top
|
||||
if tree.st == 0 then
|
||||
move.up();
|
||||
turn.right();place.forward_down(support);
|
||||
dig.forward()
|
||||
turn.angle(180); place.forward_down(support);
|
||||
tree.st=1
|
||||
elseif tree.st == 1 then
|
||||
dig.forward(); move.forward();
|
||||
tree.st=2
|
||||
elseif tree.st == 2 then
|
||||
turn.left(); dig.forward(); turn.angle(180)
|
||||
tree.st=3
|
||||
elseif tree.st == 3 then
|
||||
dig.forward(); turn.right(); tree.st = 4
|
||||
elseif tree.st == 4 then
|
||||
dig.down();move.forward(); move.forward(); tree.st = 5
|
||||
elseif tree.st == 5 then
|
||||
turn.left(); dig.forward(); turn.angle(180)
|
||||
tree.st=6
|
||||
elseif tree.st == 6 then
|
||||
dig.forward(); turn.right(); tree.st = 7
|
||||
elseif tree.st == 7 then
|
||||
dig.down();move.forward();
|
||||
turn.right();
|
||||
tree.st = 0; tree.s = 4
|
||||
end
|
||||
elseif tree.s == 4 then -- going down
|
||||
node = read_node.down()
|
||||
if node == wood then
|
||||
dig.down();
|
||||
move.down();
|
||||
else
|
||||
pickup(8); move.forward();
|
||||
place.backward(sapling)
|
||||
tree.s=5
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
-- walk around
|
||||
if not s then
|
||||
wall = "basic_robot:buttonFFFFFF";
|
||||
s=0
|
||||
if rom.tree and rom.s then
|
||||
tree.s = rom.tree.s; tree.st = rom.tree.st
|
||||
s = rom.s;
|
||||
rom.s = nil;
|
||||
else
|
||||
rom.tree = {}
|
||||
end;
|
||||
|
||||
angle = 90
|
||||
end
|
||||
|
||||
if s==0 then -- walk
|
||||
|
||||
if not move.forward() then
|
||||
node = read_node.forward();
|
||||
if node == wood then
|
||||
s = 1
|
||||
else
|
||||
turn.angle(angle); node = read_node.forward()
|
||||
if node == wall then
|
||||
turn.angle(180);move.forward();turn.angle(-angle)
|
||||
else
|
||||
move.forward(); turn.angle(angle)angle=-angle;
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif s==1 then
|
||||
tree_step();
|
||||
if tree.s == 5 then s = 0; tree.s = 0 end
|
||||
end
|
||||
|
||||
rom.s = s;rom.tree.s = tree.s; rom.tree.st = tree.st -- remember whats it doing
|
||||
|
||||
|
||||
--self.label(s .. " " .. tree.s .. " " .. tree.st)
|
46
scripts/utils/chatlog.lua
Normal file
@ -0,0 +1,46 @@
|
||||
--rnd 2017
|
||||
if not logdata then
|
||||
self.label("chatlog bot");
|
||||
_G.minetest.forceload_block(self.pos(),true)
|
||||
n = 250;
|
||||
idx = 1;
|
||||
logdata = {};
|
||||
|
||||
insert = function(text) -- insert new message
|
||||
idx = idx +1;
|
||||
if idx > n then idx = 1 end
|
||||
logdata[idx] = text;
|
||||
end
|
||||
|
||||
last = function(k,filter) -- return last k messages
|
||||
if k > n then k = 30 end
|
||||
local i,j,ret;
|
||||
i=idx;j=0; ret = ""
|
||||
|
||||
for j = 1,k do
|
||||
if not logdata[i] then break end
|
||||
if filter and not string.find(logdata[i], filter) then
|
||||
else
|
||||
ret = ret .. logdata[i] .. "\n";
|
||||
end
|
||||
i=i-1; if i < 1 then i = n end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
self.listen(1)
|
||||
end
|
||||
|
||||
speaker, msg = self.listen_msg()
|
||||
if msg then
|
||||
if string.sub(msg,1,4) == "?log" then
|
||||
local j = string.find(msg," ",6);
|
||||
local k = tonumber(string.sub(msg,6) or "") or n;
|
||||
local text;
|
||||
if j then text = last(k,string.sub(msg,j+1)) else text = last(k) end
|
||||
local form = "size[8,8]".. "textarea[0.,0;11.,9.5;text;chatlog;".. text .. "]"
|
||||
self.show_form(speaker, form)
|
||||
else
|
||||
insert(os.date("%X") .. " " .. speaker .. "> " .. msg)
|
||||
end
|
||||
end
|
89
scripts/utils/helper_chat_bot.lua
Normal file
@ -0,0 +1,89 @@
|
||||
if not init then
|
||||
init = true; self.listen(1);
|
||||
self.spam(1); self.label("help bot")
|
||||
|
||||
talk = function(msg) minetest.chat_send_all("<help bot> " .. msg) end
|
||||
keywords = {
|
||||
{"tp", 14},
|
||||
{"help",
|
||||
{"robot",6},{"",1}
|
||||
},
|
||||
{"how",
|
||||
{"play",1},{"robot", 6},{"stone",4},{"tree",3},{"wood",3},{"lava",5},{"cobble",4},{"dirt",10},
|
||||
{"do i get",1},{"do i make",1}, {"to get",1}
|
||||
},
|
||||
{"i need",
|
||||
{"wood",3}
|
||||
},
|
||||
|
||||
{"hello",2}, -- words matched must appear at beginning
|
||||
{"hi",2},
|
||||
{"back",7},
|
||||
{" hard",{"",9}}, -- word matched can appear anywhere
|
||||
{" died", {"",9}},
|
||||
{" die",{"",8}}, {" dead",{"",8}},
|
||||
{"rnd",{"",11}},
|
||||
{"bye",{"",12}},
|
||||
{"!!",{"",9}},
|
||||
|
||||
{"calc", 13},
|
||||
}
|
||||
answers = {
|
||||
"%s open inventory, click 'Quests' and do them to get more stuff", --1
|
||||
"hello %s",
|
||||
"do the dirt quest to get sticks then do sapling quest",
|
||||
"get pumice from lava and water. then search craft guide how to make cobble",
|
||||
"you get lava as compost quest reward or with grinder", -- 5
|
||||
"you have to write a program so that robot knows what to do. for list of commands click 'help' button inside robot.",
|
||||
"wb %s",
|
||||
"dont die, you lose your stuff and it will reset your level on level 1",
|
||||
"you suck %s!", -- 9
|
||||
"to get dirt craft composter and use it with leaves", -- 10
|
||||
"rnd is afk. in the meantime i can answer your questions",
|
||||
"bye %s",
|
||||
function(speaker,msg) -- 13, calc
|
||||
local expr = string.sub(msg,5); if string.find(expr,"%a") then return end
|
||||
local res = _G.loadstring("return " .. expr)(); say(expr .. " = " .. res)
|
||||
end,
|
||||
function(speaker,msg) -- 14,tp
|
||||
local p1 = minetest.get_player_by_name(speaker);
|
||||
local p2 = minetest.get_player_by_name(string.sub(msg,4));
|
||||
if p1 and p2 then
|
||||
p1:setpos(p2:getpos())
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
speaker,msg = self.listen_msg();
|
||||
if msg then
|
||||
--msg = string.lower(msg);
|
||||
sel = 0;
|
||||
for i = 1, #keywords do
|
||||
local k = string.find(msg,keywords[i][1])
|
||||
if k then
|
||||
if type(keywords[i][2])~="table" then -- one topic only
|
||||
if k == 1 then sel = keywords[i][2] break end
|
||||
else
|
||||
for j=2,#keywords[i] do -- category of several topics
|
||||
if string.find(msg,keywords[i][j][1]) then
|
||||
sel = keywords[i][j][2]; break;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
if sel>0 then
|
||||
local response = answers[sel];
|
||||
if type(response) == "function" then
|
||||
response(speaker,msg)
|
||||
elseif string.find(response,"%%s") then
|
||||
talk(string.format(response,speaker))
|
||||
else
|
||||
talk(response)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
123
scripts/utils/language translator.lua
Normal file
@ -0,0 +1,123 @@
|
||||
if not dict then
|
||||
lang = "german"
|
||||
dict = {};
|
||||
fname = "F:\\games\\rpg\\minetest-0415server\\mods\\basic_translate\\"..lang;
|
||||
local f = _G.assert(_G.io.open(fname, "r"));local dicts = f:read("*all");f:close()
|
||||
|
||||
step = 0; maxwords = 10000;
|
||||
i=0
|
||||
|
||||
while(step<maxwords) do
|
||||
|
||||
step=step+1
|
||||
i1 = string.find(dicts,"\t",i+1)
|
||||
i2 = string.find(dicts,"\n",i+1)
|
||||
|
||||
if not i2 then break end
|
||||
|
||||
local word = string.sub(dicts, i+1,i1-1);
|
||||
local tword = string.sub(dicts, i1+1,i2-1);
|
||||
local word1; local word2;local data;
|
||||
i12 = string.find(word," ");
|
||||
|
||||
if i12 and i12<i2 then
|
||||
word1 = string.sub(word,1,i12-1); word2 = string.sub(word,i12+1)
|
||||
data = dict[word1];
|
||||
if not data then
|
||||
dict[word1] = {{[word2] = tword}}
|
||||
else
|
||||
data[#data+1] = {[word2] = tword};
|
||||
end
|
||||
else
|
||||
data = dict[word];
|
||||
if not data then
|
||||
dict[word] = {{[""] = tword}}
|
||||
else
|
||||
data[#data+1] = {[""] = tword};
|
||||
end
|
||||
end
|
||||
--say("X"..word.."X"..tword)
|
||||
i=i2
|
||||
end
|
||||
|
||||
say(lang .. " dictionary: ".. step .. " words loaded")
|
||||
-- self.label(string.gsub(_G.dump(dict),"\n",""))
|
||||
|
||||
local deb = false;
|
||||
translate = function(input)
|
||||
local out = "";
|
||||
input = string.lower(input)
|
||||
local i = 0; local n = string.len(input)
|
||||
local step = 0
|
||||
while i and i<n and step < 100 do
|
||||
step = step + 1
|
||||
local i1 = string.find(input," ",i+1);
|
||||
local word;
|
||||
if not i1 then --end
|
||||
word = string.sub(input, i+1)
|
||||
else -- just take one word until " "
|
||||
word = string.sub(input, i+1, i1-1)
|
||||
end
|
||||
|
||||
i1 = i+string.len(word)+1
|
||||
if deb then say("parsed word " .. word .. " remainder '" .. string.sub(input, i1).."'") end
|
||||
|
||||
local data = dict[word];
|
||||
if data then
|
||||
if #data == 1 then
|
||||
local newout = data[1][""];
|
||||
if not newout then
|
||||
for key,v in pairs(data[1]) do newout = v; i1 = i1 + string.len(key)+1 break end
|
||||
end
|
||||
out = out .. " " .. newout
|
||||
if deb then say("immediate trans for " .. word) end
|
||||
else -- more possibilities
|
||||
if deb then say("more possibilities : ".. #data) end
|
||||
--check patterns for match:like a de -> c or a b -> d, where data for a = {{[de] = c},{[b] = d}
|
||||
local found = false
|
||||
local defaultv = ""
|
||||
|
||||
|
||||
for j=1,#data do
|
||||
|
||||
for key,v in pairs(data[j]) do
|
||||
local keylen = string.len(key)
|
||||
local pattern = string.sub(input,i1+1,i1+keylen)
|
||||
if deb then say("pattern '" .. pattern .. "' key '" .. key .. "' len " .. keylen) end
|
||||
if key == "" then defaultv = v
|
||||
elseif pattern == key then
|
||||
found = true;
|
||||
if deb then say(word .. " " .. pattern .. " -> match key " .. key) end
|
||||
out = out .. " " .. v; i1 = i1+string.len(key)+1 -- skip to after key
|
||||
end
|
||||
end
|
||||
if found then break end
|
||||
end
|
||||
|
||||
if not found then out = out .. " " .. defaultv end
|
||||
|
||||
end
|
||||
else
|
||||
out = out .. " " .. word
|
||||
end
|
||||
i=i1
|
||||
if deb then say("next word at i " .. i1 .. " remainder " .. string.sub(input,i1)) end
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
-- say(translate("hello world"))
|
||||
self.listen(1)
|
||||
end
|
||||
|
||||
|
||||
|
||||
speaker,msg = self.listen_msg()
|
||||
if msg then
|
||||
if string.sub(msg,1,1) == "?" then
|
||||
msg = string.sub(msg,2)
|
||||
local transmsg = translate(msg);
|
||||
_G.minetest.chat_send_all("TRANSLATOR> " .. transmsg)
|
||||
end
|
||||
end
|
32
scripts/utils/object_lister.lua
Normal file
@ -0,0 +1,32 @@
|
||||
-- return minetest object count for 5x5x5 blocks
|
||||
|
||||
if not init then init = true
|
||||
|
||||
local objs = minetest.get_objects_inside_radius(self.pos(), 30000);
|
||||
local ret = {};
|
||||
|
||||
local round = function(x) return math.floor(x/5)*5 end
|
||||
local ret = {};
|
||||
|
||||
for i = 1, #objs do
|
||||
local p = objs[i]:get_pos();
|
||||
local phash = round(p.x) .. " " .. round(p.y) .. " " .. round(p.z);
|
||||
ret[phash] = (ret[phash] or 0) + 1
|
||||
end
|
||||
|
||||
local out = {};
|
||||
for k,v in pairs(ret) do
|
||||
out[#out+1] = {k,v}
|
||||
end
|
||||
|
||||
table.sort(out, function(a,b) return a[2]>b[2] end)
|
||||
local res = {};
|
||||
for i = 1, #out do
|
||||
res[#res+1] = out[i][1] .. "=" .. out[i][2]
|
||||
end
|
||||
|
||||
self.label("#objects " .. #objs .. "\n" .. table.concat(res, "\n"))
|
||||
|
||||
|
||||
|
||||
end
|
16
scripts/utils/resource_display.lua
Normal file
@ -0,0 +1,16 @@
|
||||
-- SHOWS ACTIVE ROBOTS AND STATISTICS
|
||||
if not init then init = true
|
||||
local data = _G.basic_robot.data;
|
||||
local ret = {};
|
||||
for k,v in pairs(data) do
|
||||
if k~="listening" and v.obj then
|
||||
local ent = v.obj:get_luaentity();
|
||||
local t = v.t or 0; if t< 100000 then t = math.floor(t * 10000)/10 else t = 0 end
|
||||
if ent then ret[#ret+1] = k .. " " .. string.len(ent.code or "") .. " " .. string.len(_G.string.dump(v.bytecode) or "") .. " ~ " .. t end
|
||||
end
|
||||
end
|
||||
mem1 = _G.collectgarbage("count")
|
||||
self.label("memory used by lua (kbytes) ".. mem1 .. " ( delta " .. mem1 - (mem0 or 0) .. ")\n\nACTIVE ROBOTS\nrobot name | source code size | bytecode size | ~ time (ms)\n" .. table.concat(ret,"\n"))
|
||||
mem0 = mem1
|
||||
init = false
|
||||
end
|
76
scripts/utils/serverbot.lua
Normal file
@ -0,0 +1,76 @@
|
||||
--SERVER ROBOT : can send various data to other robots that requested it
|
||||
if not cmds then
|
||||
|
||||
-- user auth data
|
||||
auth = {["rnd1"]=2};
|
||||
|
||||
-- server commands
|
||||
cmds = {
|
||||
list = {
|
||||
run = function()
|
||||
local ret = ""; for i,_ in pairs(cmds) do ret = ret .. " " .. i end; return ret
|
||||
end,
|
||||
help = "list all commands",
|
||||
level = 0
|
||||
},
|
||||
|
||||
help = {
|
||||
run = function(words)
|
||||
local arg = words[2];
|
||||
if not arg then return "help: missing argument" end
|
||||
local cmd = cmds[arg];
|
||||
if not cmd then return "help: nonexistent command" end
|
||||
return cmd.help or ""
|
||||
end,
|
||||
help = "display help for command",
|
||||
level = 0
|
||||
},
|
||||
|
||||
chat = {
|
||||
run = function(words)
|
||||
words[1] = "";_G.minetest.chat_send_all("#server bot : " .. table.concat(words," ") or ""); return true;
|
||||
end,
|
||||
help = "prints text globally",
|
||||
level = 2
|
||||
},
|
||||
|
||||
minetest = {
|
||||
run = function() return minetest end,
|
||||
help = "returns minetest namespace",
|
||||
level = 3
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LISTENING = 0; --states
|
||||
state = LISTENING; -- init
|
||||
_G.minetest.forceload_block(self.pos(),true)
|
||||
end
|
||||
|
||||
|
||||
|
||||
if state == LISTENING then
|
||||
sender,mail = self.read_mail()
|
||||
if mail then
|
||||
if type(mail)~="string" then mail = "" end
|
||||
self.label("received request " .. mail);
|
||||
local words = {};
|
||||
for word in string.gmatch(mail,"%S+") do words[#words+1]=word end -- get arguments
|
||||
if not words or not words[1] then
|
||||
self.send_mail(sender,"error: nil request")
|
||||
else
|
||||
local cmd = cmds[words[1]];
|
||||
if not cmd or not cmd.run then
|
||||
self.send_mail(sender,"error: illegal command")
|
||||
elseif (auth[sender] or 0) < cmd.level then
|
||||
self.send_mail(sender,"error: auth level " .. (auth[sender] or 0) ..", need level " .. cmd.level)
|
||||
else
|
||||
self.send_mail(sender,cmd.run(words));
|
||||
self.label("sending data to " .. sender .. " ...")
|
||||
end
|
||||
end
|
||||
else
|
||||
self.label("listening...")
|
||||
end
|
||||
|
||||
end
|
34
scripts/utils/simple_house_builder.lua
Normal file
@ -0,0 +1,34 @@
|
||||
-- rnd 2017
|
||||
if not pos then
|
||||
pos = self.spawnpos();
|
||||
n = 6; -- width
|
||||
m = 4; -- height
|
||||
door = math.floor(n/2)+1; -- door place
|
||||
|
||||
plan = {};
|
||||
build_cube = function(x,y,z)
|
||||
plan[#plan+1] = {c= math.random(10)+6, pos={x=pos.x+x,y=pos.y+y,z=pos.z+z}};
|
||||
end
|
||||
|
||||
--floor
|
||||
y=0;for z=1,n do for x=1,n do build_cube(x,y,z) end end --bottom
|
||||
|
||||
z=1;for y=1,m do for x=1,n do build_cube(x,y,z) end end --wall 1
|
||||
z=n;for y=1,m do for x=1,n do build_cube(x,y,z) end end --wall2
|
||||
|
||||
x=n;for y=1,m do for z=2,n-1 do build_cube(x,y,z) end end -- wall3
|
||||
x=1;for y=1,m do for z=2,n-1 do if z~=door then build_cube(x,y,z) end end end -- wall4
|
||||
x=1;z=door;for y=3,m do build_cube(x,y,z) end -- door hole
|
||||
|
||||
|
||||
y=m;for x = 2,n-1 do for z = 2,n-1 do build_cube(x,y,z) end end -- ceiling
|
||||
s=0
|
||||
--self.remove()
|
||||
end
|
||||
|
||||
s=s+1;
|
||||
if plan[s] then
|
||||
keyboard.set(plan[s].pos,plan[s].c)
|
||||
else
|
||||
self.remove()
|
||||
end
|
BIN
textures/000.png
Before Width: | Height: | Size: 88 B After Width: | Height: | Size: 116 B |
BIN
textures/001.png
Before Width: | Height: | Size: 125 B After Width: | Height: | Size: 127 B |
BIN
textures/002.png
Before Width: | Height: | Size: 127 B After Width: | Height: | Size: 130 B |
BIN
textures/003.png
Before Width: | Height: | Size: 110 B After Width: | Height: | Size: 141 B |
BIN
textures/004.png
Before Width: | Height: | Size: 110 B After Width: | Height: | Size: 126 B |
BIN
textures/005.png
Before Width: | Height: | Size: 117 B After Width: | Height: | Size: 122 B |
BIN
textures/006.png
Before Width: | Height: | Size: 107 B After Width: | Height: | Size: 122 B |
BIN
textures/007.png
Before Width: | Height: | Size: 101 B After Width: | Height: | Size: 119 B |
BIN
textures/008.png
Before Width: | Height: | Size: 107 B After Width: | Height: | Size: 118 B |
BIN
textures/009.png
Before Width: | Height: | Size: 104 B After Width: | Height: | Size: 119 B |
BIN
textures/010.png
Before Width: | Height: | Size: 104 B After Width: | Height: | Size: 118 B |
BIN
textures/011.png
Before Width: | Height: | Size: 110 B After Width: | Height: | Size: 123 B |
BIN
textures/012.png
Before Width: | Height: | Size: 110 B After Width: | Height: | Size: 123 B |
BIN
textures/013.png
Before Width: | Height: | Size: 110 B After Width: | Height: | Size: 122 B |
BIN
textures/014.png
Before Width: | Height: | Size: 110 B After Width: | Height: | Size: 123 B |
BIN
textures/015.png
Before Width: | Height: | Size: 125 B After Width: | Height: | Size: 130 B |
BIN
textures/016.png
Before Width: | Height: | Size: 107 B After Width: | Height: | Size: 122 B |
BIN
textures/017.png
Before Width: | Height: | Size: 107 B After Width: | Height: | Size: 123 B |
BIN
textures/018.png
Before Width: | Height: | Size: 112 B After Width: | Height: | Size: 124 B |
BIN
textures/019.png
Before Width: | Height: | Size: 98 B After Width: | Height: | Size: 113 B |
BIN
textures/020.png
Before Width: | Height: | Size: 109 B After Width: | Height: | Size: 120 B |
BIN
textures/021.png
Before Width: | Height: | Size: 126 B After Width: | Height: | Size: 127 B |
BIN
textures/022.png
Before Width: | Height: | Size: 93 B After Width: | Height: | Size: 113 B |
BIN
textures/023.png
Before Width: | Height: | Size: 119 B After Width: | Height: | Size: 128 B |
BIN
textures/024.png
Before Width: | Height: | Size: 108 B After Width: | Height: | Size: 121 B |
BIN
textures/025.png
Before Width: | Height: | Size: 110 B After Width: | Height: | Size: 120 B |
BIN
textures/026.png
Before Width: | Height: | Size: 110 B After Width: | Height: | Size: 124 B |
BIN
textures/027.png
Before Width: | Height: | Size: 109 B After Width: | Height: | Size: 123 B |
BIN
textures/028.png
Before Width: | Height: | Size: 111 B After Width: | Height: | Size: 125 B |
BIN
textures/029.png
Before Width: | Height: | Size: 106 B After Width: | Height: | Size: 118 B |
BIN
textures/030.png
Before Width: | Height: | Size: 107 B After Width: | Height: | Size: 125 B |
BIN
textures/031.png
Before Width: | Height: | Size: 107 B After Width: | Height: | Size: 124 B |
BIN
textures/032.png
Before Width: | Height: | Size: 88 B After Width: | Height: | Size: 112 B |
BIN
textures/033.png
Before Width: | Height: | Size: 98 B After Width: | Height: | Size: 118 B |