Skip to content
Snippets Groups Projects
Commit 082eecf6 authored by rnd1's avatar rnd1
Browse files

books, setcode, can listen to chat

parent c51bf32a
No related branches found
No related tags found
No related merge requests found
......@@ -13,16 +13,19 @@ local function pos_in_dir(obj, dir) -- position after we move in specified direc
elseif dir == 3 then
elseif dir == 4 then
yaw = yaw+pi;
elseif dir == 5 then
elseif dir == 5 then -- up
pos.y=pos.y+1
elseif dir == 6 then
elseif dir == 6 then -- down
pos.y=pos.y-1
elseif dir == 7 then -- forward, down
pos.y=pos.y-1
end
if dir<5 then
if dir<5 or dir == 7 then -- left, right, back
pos.x = pos.x+math.cos(yaw)
pos.z = pos.z+math.sin(yaw)
end
return pos
end
......@@ -30,14 +33,27 @@ basic_robot.commands.move = function(name,dir)
local obj = basic_robot.data[name].obj;
local pos = pos_in_dir(obj, dir)
if minetest.get_node(pos).name ~= "air" then return end
-- can move through walkable nodes
if minetest.registered_nodes[minetest.get_node(pos).name].walkable then return end
-- up; no levitation!
if minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name == "air" and
minetest.get_node({x=pos.x,y=pos.y-2,z=pos.z}).name == "air" then
return
return false
end
obj:moveto(pos, true)
-- sit and stand up for model - doenst work for overwriten obj export
-- if dir == 5 then-- up
-- obj:set_animation({x=0,y=0})
-- elseif dir == 6 then -- down
-- obj:set_animation({x=81,y=160})
-- end
return true
end
basic_robot.commands.turn = function (name, angle)
......@@ -50,19 +66,91 @@ basic_robot.commands.dig = function(name,dir)
local obj = basic_robot.data[name].obj;
local pos = pos_in_dir(obj, dir)
local luaent = obj:get_luaentity();
if minetest.is_protected(pos,luaent.owner ) then return end
if minetest.is_protected(pos,luaent.owner ) then return false end
local nodename = minetest.get_node(pos).name;
if nodename == "air" then return end
if nodename == "air" then return false end
local spos = obj:get_luaentity().spawnpos;
local inv = minetest.get_meta(spos):get_inventory();
if not inv then return end
inv:add_item("main",ItemStack( nodename ));
--DS
local sounds = minetest.registered_nodes[minetest.get_node(pos).name].sounds
if sounds then
local sound = sounds.dug
if sound then
minetest.sound_play(sound,{object=obj, max_hear_distance = 10})
end
end
minetest.set_node(pos,{name = "air"})
return true
end
basic_robot.commands.insert_item = function(name,item, inventory,dir)
local obj = basic_robot.data[name].obj;
local tpos = pos_in_dir(obj, dir); -- position of target block
local luaent = obj:get_luaentity();
if minetest.is_protected(tpos,luaent.owner ) then return false end
local pos = basic_robot.data[name].spawnpos; -- position of spawner block
local meta = minetest.get_meta(pos);
local tmeta = minetest.get_meta(tpos);
local inv = minetest.get_meta(pos):get_inventory();
local tinv = minetest.get_meta(tpos):get_inventory();
if not inventory then inventory = "main"; end
--if not inv then return end
local stack = ItemStack(item);
if (not inv:contains_item("main", stack) or not tinv:room_for_item(inventory, stack)) and meta:get_int("admin")~=1 then
return false
end
tinv:add_item(inventory,stack);
inv:remove_item("main", stack);
return true
end
basic_robot.commands.take_item = function(name,item, inventory,dir)
local obj = basic_robot.data[name].obj;
local tpos = pos_in_dir(obj, dir); -- position of target block
local luaent = obj:get_luaentity();
if minetest.is_protected(tpos,luaent.owner ) then return false end
local pos = basic_robot.data[name].spawnpos; -- position of spawner block
if basic_robot.bad_inventory_blocks[ minetest.get_node(tpos).name ] then return false end -- dont allow take from
local meta = minetest.get_meta(pos);
local tmeta = minetest.get_meta(tpos);
local inv = minetest.get_meta(pos):get_inventory();
local tinv = minetest.get_meta(tpos):get_inventory();
if not inventory then inventory = "main"; end
--if not inv then return end
local stack = ItemStack(item);
if (not tinv:contains_item(inventory, stack) or not inv:room_for_item("main", stack)) and meta:get_int("admin")~=1 then
return false
end
inv:add_item("main",stack);
tinv:remove_item(inventory, stack);
return true
end
basic_robot.commands.read_node = function(name,dir)
local obj = basic_robot.data[name].obj;
local pos = pos_in_dir(obj, dir)
......@@ -79,15 +167,74 @@ basic_robot.commands.place = function(name,nodename, dir)
local obj = basic_robot.data[name].obj;
local pos = pos_in_dir(obj, dir)
local luaent = obj:get_luaentity();
if minetest.is_protected(pos,luaent.owner ) then return end
if minetest.get_node(pos).name~="air" then return end
if minetest.is_protected(pos,luaent.owner ) then return false end
if minetest.get_node(pos).name~="air" then return false end
local spos = obj:get_luaentity().spawnpos;
local meta = minetest.get_meta(spos);
local inv = meta:get_inventory();
if not inv then return end
if not inv then return false end
if not inv:contains_item("main", ItemStack(nodename)) and meta:get_int("admin")~=1 then return end
inv:remove_item("main", ItemStack(nodename));
--DS
local sounds = minetest.registered_nodes[nodename].sounds
if sounds then
local sound = sounds.place
if sound then
minetest.sound_play(sound,{object=obj, max_hear_distance = 10})
end
end
minetest.set_node(pos,{name = nodename})
end
\ No newline at end of file
return true
end
basic_robot.commands.attack = function(name, target) -- attack range 4, damage 5
local reach = 4;
local damage = 5;
local tplayer = minetest.get_player_by_name(target);
if not tplayer then return false end
local obj = basic_robot.data[name].obj;
local pos = obj:getpos();
local tpos = tplayer:getpos();
if math.abs(pos.x-tpos.x)> reach or math.abs(pos.y-tpos.y)> reach or math.abs(pos.z-tpos.z)> reach then
return false
end
tplayer:set_hp(tplayer:get_hp()-damage)
return true
end
basic_robot.commands.read_book = function (itemstack) -- itemstack should contain book
local data = minetest.deserialize(itemstack:get_metadata())
if data then
return data.text;
else
return nil
end
end
basic_robot.commands.write_book = function(name,text) -- returns itemstack containing book
local lpp = 14;
local new_stack = ItemStack("default:book_written")
local data = {}
data.title = "program book"
data.text = text
data.text_len = #data.text
data.page = 1
data.page_max = math.ceil((#data.text:gsub("[^\n]", "") + 1) / lpp)
data.owner = name
local data_str = minetest.serialize(data)
new_stack:set_metadata(data_str);
return new_stack;
end
......@@ -3,8 +3,15 @@
basic_robot = {};
basic_robot.call_limit = 32; -- how many execution calls per script execution allowed
basic_robot.bad_inventory_blocks = {
["craft_guide:sign_wall"] = true,
}
basic_robot.data = {};
basic_robot.data.listening = {}; -- which robots listen to chat
--[[
[name] = {sandbox= .., bytecode = ..., ram = ..., obj = robot object,spawnpos=...}
robot object = object of entity, used to manipulate movements and more
......@@ -37,33 +44,65 @@ function getSandboxEnv (name)
},
dig = {
left = function() commands.dig(name,1) end,
right = function() commands.dig(name,2) end,
forward = function() commands.dig(name,3) end,
backward = function() commands.dig(name,4) end,
down = function() commands.dig(name,6) end,
up = function() commands.dig(name,5) end,
left = function() return commands.dig(name,1) end,
right = function() return commands.dig(name,2) end,
forward = function() return commands.dig(name,3) end,
backward = function() return commands.dig(name,4) end,
down = function() return commands.dig(name,6) end,
up = function() return commands.dig(name,5) end,
forward_down = function() return commands.dig(name,7) end,
},
place = {
left = function(nodename) commands.place(name,nodename, 1) end,
right = function(nodename) commands.place(name,nodename, 2) end,
forward = function(nodename) commands.place(name,nodename, 3) end,
backward = function(nodename) commands.place(name,nodename, 4) end,
down = function(nodename) commands.place(name,nodename, 6) end,
up = function(nodename) commands.place(name,nodename, 5) end,
left = function(nodename) return commands.place(name,nodename, 1) end,
right = function(nodename) return commands.place(name,nodename, 2) end,
forward = function(nodename) return commands.place(name,nodename, 3) end,
backward = function(nodename) return commands.place(name,nodename, 4) end,
down = function(nodename) return commands.place(name,nodename, 6) end,
up = function(nodename) return commands.place(name,nodename, 5) end,
forward_down = function(nodename) return commands.place(name,nodename, 7) end,
},
insert = { -- insert item from inventory into another inventory TODO
forward = function(item, inventory) robot_insert(name,item, inventory,1) end,
backward = function(item, inventory) robot_insert(name,item, inventory,2) end,
down = function(item, inventory) robot_insert(name,item, inventory,3) end,
up = function(item, inventory) robot_insert(name,item, inventory,4) end,
insert = { -- insert item from robot inventory into another inventory
left = function(item, inventory) commands.insert_item(name,item, inventory,1) end,
right = function(item, inventory) commands.insert_item(name,item, inventory,2) end,
forward = function(item, inventory) commands.insert_item(name,item, inventory,3) end,
backward = function(item, inventory) commands.insert_item(name,item, inventory,4) end,
down = function(item, inventory) commands.insert_item(name,item, inventory,6) end,
up = function(item, inventory) commands.insert_item(name,item, inventory,5) end,
},
take = {}, -- take item from inventory TODO
take = { -- takes item from inventory and puts it in robot inventory
left = function(item, inventory) commands.take_item(name,item, inventory,1) end,
right = function(item, inventory) commands.take_item(name,item, inventory,2) end,
forward = function(item, inventory) commands.take_item(name,item, inventory,3) end,
backward = function(item, inventory) commands.take_item(name,item, inventory,4) end,
down = function(item, inventory) commands.take_item(name,item, inventory,6) end,
up = function(item, inventory) commands.take_item(name,item, inventory,5) end,
}, -- take item from inventory TODO
selfpos = function() return basic_robot.data[name].obj:getpos() end,
self = {
pos = function() return basic_robot.data[name].obj:getpos() end,
spawnpos = function() return basic_robot.data[name].spawnpos end,
viewdir = function() local yaw = basic_robot.data[name].obj:getyaw(); return {x=math.cos(yaw), y = 0, z=math.sin(yaw)} end,
listen = function (mode)
if mode == 1 then
basic_robot.data.listening[name] = true
else
basic_robot.data.listening[name] = nil
end
end,
spam = function (mode) -- allow more than one msg per "say"
if mode == 1 then
basic_robot.data[name].allow_spam = true
else
basic_robot.data[name].allow_spam = nil
end
end,
},
find_nodes =
function(nodename,r)
......@@ -71,6 +110,25 @@ function getSandboxEnv (name)
return (minetest.find_node_near(basic_robot.data[name].obj:getpos(), r, nodename)~=nil)
end, -- in radius around position
find_player =
function(r)
if r>8 then return false end
local objects = minetest.get_objects_inside_radius(basic_robot.data[name].obj:getpos(), r);
for _,obj in pairs(objects) do
if obj:is_player() then return obj:get_player_name() end
end
return false
end, -- in radius around position
player = {
getpos = function(name)
local player = minetest.get_player_by_name(name);
if player then return player:getpos() else return nil end
end,
},
attack = function(target) return basic_robot.commands.attack(name,target) end, -- attack player if nearby
read_node = { -- returns node name
left = function() return commands.read_node(name,1) end,
......@@ -79,6 +137,7 @@ function getSandboxEnv (name)
backward = function() return commands.read_node(name,4) end,
down = function() return commands.read_node(name,6) end,
up = function() return commands.read_node(name,5) end,
forward_down = function() return commands.read_node(name,7) end,
},
read_text = { -- returns node name
......@@ -90,10 +149,76 @@ function getSandboxEnv (name)
up = function() return commands.read_text(name,5) end,
},
say = function(text)
minetest.chat_send_all("<robot ".. name .. "> " .. text)
say = function(text)
if not basic_robot.data[name].quiet_mode 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(name,"<robot ".. name .. "> " .. text)
end
end,
listen_msg = function()
local msg = basic_robot.data[name].listen_msg;
local speaker = basic_robot.data[name].listen_speaker;
basic_robot.data[name].listen_msg = nil;
basic_robot.data[name].listen_speaker = nil;
return speaker,msg
end,
fire = function(speed, pitch,gravity) -- experimental: fires an projectile
local obj = basic_robot.data[name].obj;
local pos = obj:getpos();
local yaw = obj:getyaw();
-- fire particle
minetest.add_particle(
{
pos = pos,
expirationtime = 10,
velocity = {x=speed*math.cos(yaw)*math.cos(pitch), y=speed*math.sin(pitch),z=speed*math.sin(yaw)*math.cos(pitch)},
size = 5,
texture = "default_apple.png",
acceleration = {x=0,y=-gravity,z=0},
collisiondetection = true,
collision_removal = true,
}
);
end,
book = {
read = function(i)
if i<=0 or i > 32 then return nil end
local inv = minetest.get_meta(basic_robot.data[name].spawnpos):get_inventory();
local itemstack = inv:get_stack("library", i);
if itemstack then
return commands.read_book(itemstack);
else
return nil
end
end,
write = function(i,text)
if i<=0 or i > 32 then return nil end
local inv = minetest.get_meta(basic_robot.data[name].spawnpos):get_inventory();
local stack = basic_robot.commands.write_book(name,text);
if stack then inv:set_stack("library", i, stack) end
end
},
code = {
set = function(text) -- replace bytecode in sandbox with this
local err = commands.setCode( name, text ); -- compile code
if err then
minetest.chat_send_player(name,"#ROBOT CODE COMPILATION ERROR : " .. err)
local obj = basic_robot.data[name].obj;
obj:remove();
basic_robot.data[name].obj = nil;
return
end
end
},
string = {
byte = string.byte, char = string.char,
......@@ -133,6 +258,10 @@ function getSandboxEnv (name)
time = os.time,
},
tonumber = tonumber,
pairs = pairs,
ipairs = ipairs,
error = error,
debug = debug,
......@@ -241,6 +370,7 @@ local function setCode( name, script ) -- to run script: 1. initSandbox 2. setCo
return nil
end
basic_robot.commands.setCode=setCode; -- so we can use it
local function runSandbox( name)
......@@ -283,22 +413,23 @@ local robot_spawner_update_form = function (pos, mode)
if mode ~= 1 then
form =
"size[8,6]" .. -- width, height
"textarea[0.1,0.75;8.4,6.5;code;code;".. code.."]"..
"button_exit[-0.25,-0.25;1.5,1;spawn;START]"..
"button[1.25,-0.25;1.5,1;despawn;STOP]"..
"button[2.75,-0.25;1.5,1;inventory;inventory]"..
"button[5.25,-0.25;1,1;help;help]"..
"button_exit[6.25,-0.25;1,1;reset;CLEAR]"..
"button_exit[7.25,-0.25;1,1;OK;SAVE]";
"size[9.5,6]" .. -- width, height
"textarea[1.25,-0.25;8.75,7.6;code;;".. code.."]"..
"button_exit[-0.25,-0.25;1.25,1;OK;SAVE]"..
"button_exit[-0.25, 0.75;1.25,1;spawn;START]"..
"button[-0.25, 1.75;1.25,1;despawn;STOP]"..
"button[-0.25, 3.6;1.25,1;inventory;storage]"..
"button[-0.25, 4.6;1.25,1;library;library]"..
"button[-0.25, 5.6;1.25,1;help;help]";
else
form =
"size[8,6]" .. -- width, height
"textarea[0.1,0.75;8.4,6.5;code;code;".. code.."]"..
"button[1.25,-0.25;1.5,1;despawn;STOP]"..
"button[2.75,-0.25;1.5,1;inventory;inventory]"..
"button[5.25,-0.25;1,1;help;help]"..
"button_exit[7.25,-0.25;1,1;OK;save]";
"size[9.5,6]" .. -- width, height
"textarea[1.25,-0.25;8.75,7.6;code;;".. code.."]"..
"button_exit[-0.25,-0.25;1.25,1;OK;SAVE]"..
"button[-0.25, 1.75;1.25,1;despawn;STOP]"..
"button[-0.25, 3.6;1.25,1;inventory;storage]"..
"button[-0.25, 4.6;1.25,1;library;library]"..
"button[-0.25, 5.6;1.25,1;help;help]";
end
if mode ==1 then return form end
......@@ -307,7 +438,12 @@ local robot_spawner_update_form = function (pos, mode)
end
local function init_robot(self)
basic_robot.data[self.owner].obj = self.object; -- BUG: some problems with functions using object later??
basic_robot.data.listening[self.owner] = nil -- dont listen at beginning
basic_robot.data[self.owner].quiet_mode = false;
self.object:set_properties({infotext = "robot " .. self.owner});
self.object:set_properties({nametag = "robot " .. self.owner,nametag_color = "LawnGreen"});
self.object:set_armor_groups({fleshy=0})
......@@ -324,8 +460,9 @@ minetest.register_entity("basic_robot:robot",{
timestep = 1, -- run every 1 second
spawnpos = nil,
--visual="mesh",
--mesh = "character.b3d",
--mesh = "char.obj", --this is good: aligned and rotated in blender - but how to move nametag up? now is stuck in head
--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"},
......@@ -517,15 +654,31 @@ local on_receive_robot_form = function(pos, formname, fields, sender)
if fields.help then
local text = "BASIC LUA SYNTAX\n \nif CONDITION then BLOCK1 else BLOCK2 end \nmyTable1 = {1,2,3}, myTable2 = {[\"entry1\"]=5, [\"entry2\"]=1}\n"..
local text = "BASIC LUA SYNTAX\n \nif x==1 then do_something else do_something_else end"..
"\nfor i = 1, 5 do something end \nwhile i<6 do something; i=i+1; end\n"..
"\narrays: myTable1 = {1,2,3}, myTable2 = {[\"entry1\"]=5, [\"entry2\"]=1}\n"..
"access table entries with myTable1[1] or myTable2.entry1 or myTable2[\"entry1\"]\n"..
"\nROBOT COMMANDS\n\n"..
"move.direction(), where direction is forward, backward, left,right, up, down\n"..
"**MOVEMENT,DIGGING,INVENTORT TAKE/INSERT\nmove.direction(), where direction is forward, backward, left,right, up, down)\n"..
"forward_down direction only works with dig, place and read_node\n"..
"turn.left(), turn.right(), turn.angle(45)\n"..
"dig.direction(), place.direction(\"default:dirt\")\nread_node.direction() tells you names of nodes\n"..
"insert.direction(item, inventory) inserts item from robot inventory to target inventory\n"..
"take.direction(item, inventory) takes item from target inventory into robot inventory\n"..
"read_text.direction() reads text of signs, chests and other blocks\n"..
"**BOOKS/CODE\nbook.read(i) returns contents of book at i-th position in library \nbook.write(i,text) writes book at i-th position\n"..
"code.set(text) replaces current bytecode of robot\n"..
"find_nodes(\"default:dirt\",3) is true if node can be found at radius 3 around robot, otherwise false\n"..
"selfpos() returns table {x=pos.x,y=pos.y,z=pos.z}\n"..
"find_player(3) finds player and returns his name in radius 3 around robot, if not returns false\n"..
"attack(target) attempts to attack target player if nearby \n"..
"self.pos() returns table {x=pos.x,y=pos.y,z=pos.z}\n"..
"self.spawnpos() returns position of spawner block\n"..
"self.viewdir() returns vector of view for robot\n"..
"self.listen(mode) (de)attaches chat listener to robot\n"..
"listen_msg() retrieves last chat message if robot listens\n"..
"player.getpos(name) return position of player, player.connected() returns list of players\n"..
"fire = function(speed, pitch,gravity) fires a projectile from robot\n"..
"say(\"hello\") will speak";
......@@ -564,6 +717,15 @@ local on_receive_robot_form = function(pos, formname, fields, sender)
minetest.show_formspec(sender:get_player_name(), "robot_inventory", form);
end
if fields.library then
local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z ;
local form =
"size[8,8]" .. -- width, height
"list["..list_name..";library;0.,0;8,4;]"..
"list[current_player;main;0,4.25;8,4;]";
minetest.show_formspec(sender:get_player_name(), "robot_inventory", form);
end
end
-- handle form when rightclicking robot entity
......@@ -587,6 +749,17 @@ minetest.register_on_player_receive_fields(
end
)
-- handle chats
minetest.register_on_chat_message(
function(name, message)
local listeners = basic_robot.data.listening;
for pname,_ in pairs(listeners) do
basic_robot.data[pname].listen_msg = message;
basic_robot.data[pname].listen_speaker = name;
end
end
)
minetest.register_node("basic_robot:spawner", {
description = "Spawns robot",
......@@ -608,6 +781,7 @@ minetest.register_node("basic_robot:spawner", {
local inv = meta:get_inventory(); -- spawner inventory
inv:set_size("main",32);
inv:set_size("library",32);
end,
mesecons = {effector = {
......@@ -638,7 +812,7 @@ minetest.register_node("basic_robot:spawner", {
can_dig = function(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then return false end
local meta = minetest.get_meta(pos);
if not meta:get_inventory():is_empty("main") then return false end
if not meta:get_inventory():is_empty("main") or not meta:get_inventory():is_empty("library") then return false end
return true
end
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment