init
This commit is contained in:
commit
216bf4adf3
21
README.txt
Normal file
21
README.txt
Normal file
@ -0,0 +1,21 @@
|
||||
BASIC_ROBOT: lightweight robot mod for multiplayer
|
||||
minetest 0.4.14
|
||||
(c) 2016 rnd
|
||||
|
||||
MANUAL:
|
||||
1. ingame help: right click spawner and click help button
|
||||
|
||||
---------------------------------------------------------------------
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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/>.
|
||||
----------------------------------------------------------------------
|
68
commands.lua
Normal file
68
commands.lua
Normal file
@ -0,0 +1,68 @@
|
||||
basic_robot.commands = {};
|
||||
|
||||
local pi = math.pi;
|
||||
|
||||
local function pos_in_dir(obj, dir) -- position after we move in specified direction
|
||||
local yaw = obj:getyaw();
|
||||
local pos = obj:getpos();
|
||||
|
||||
if dir == 1 then
|
||||
yaw = yaw + pi/2;
|
||||
elseif dir == 2 then
|
||||
yaw = yaw - pi/2;
|
||||
elseif dir == 3 then
|
||||
elseif dir == 4 then
|
||||
yaw = yaw+pi;
|
||||
elseif dir == 5 then
|
||||
pos.y=pos.y+1
|
||||
elseif dir == 6 then
|
||||
pos.y=pos.y-1
|
||||
end
|
||||
|
||||
if dir<5 then
|
||||
pos.x = pos.x+math.cos(yaw)
|
||||
pos.z = pos.z+math.sin(yaw)
|
||||
end
|
||||
return pos
|
||||
end
|
||||
|
||||
basic_robot.commands.move = function(obj,dir)
|
||||
local pos = pos_in_dir(obj, dir)
|
||||
|
||||
if minetest.get_node(pos).name ~= "air" 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
|
||||
end
|
||||
|
||||
obj:moveto(pos, true)
|
||||
end
|
||||
|
||||
|
||||
basic_robot.commands.turn = function (obj, angle)
|
||||
local yaw = obj:getyaw()+angle;
|
||||
obj:setyaw(yaw);
|
||||
end
|
||||
|
||||
|
||||
basic_robot.commands.dig = function(obj,dir)
|
||||
local pos = pos_in_dir(obj, dir)
|
||||
local luaent = obj:get_luaentity();
|
||||
if minetest.is_protected(pos,luaent.owner ) then return end
|
||||
minetest.set_node(pos,{name = "air"})
|
||||
end
|
||||
|
||||
basic_robot.commands.read_node = function(obj,dir)
|
||||
local pos = pos_in_dir(obj, dir)
|
||||
return minetest.get_node(pos).name or ""
|
||||
end
|
||||
|
||||
|
||||
basic_robot.commands.place = function(obj,nodename, dir)
|
||||
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
|
||||
minetest.set_node(pos,{name = nodename})
|
||||
end
|
443
init.lua
Normal file
443
init.lua
Normal file
@ -0,0 +1,443 @@
|
||||
-- basic_robot by rnd, 2016
|
||||
basic_robot = {};
|
||||
|
||||
basic_robot.data = {};
|
||||
--[[
|
||||
[name] = {sandbox= .., bytecode = ..., ram = ..., obj = robot object}
|
||||
robot object = object of entity, used to manipulate movements and more
|
||||
--]]
|
||||
|
||||
|
||||
|
||||
dofile(minetest.get_modpath("basic_robot").."/commands.lua")
|
||||
|
||||
-- SANDBOX for running lua code isolated and safely
|
||||
|
||||
function getSandboxEnv (name)
|
||||
local obj = basic_robot.data[name].obj;
|
||||
local commands = basic_robot.commands;
|
||||
return
|
||||
{
|
||||
pcall=pcall,
|
||||
ram = basic_robot.data[name].ram, -- "ram" - used to store variables
|
||||
move = { -- changes position of robot
|
||||
left = function() commands.move(obj, 1) end,
|
||||
right = function() commands.move(obj, 2) end,
|
||||
forward = function() commands.move(obj, 3) end,
|
||||
backward = function() commands.move(obj, 4) end,
|
||||
up = function() commands.move(obj,5) end,
|
||||
down = function() commands.move(obj,6) end,
|
||||
},
|
||||
|
||||
turn = {
|
||||
left = function() commands.turn(obj,math.pi/2) end,
|
||||
right = function() commands.turn(obj,-math.pi/2) end,
|
||||
angle = function(angle) commands.turn(obj,angle*math.pi/180) end,
|
||||
},
|
||||
|
||||
dig = {
|
||||
left = function() commands.dig(obj, 1) end,
|
||||
right = function() commands.dig(obj, 2) end,
|
||||
forward = function() commands.dig(obj, 3) end,
|
||||
backward = function() commands.dig(obj, 4) end,
|
||||
down = function() commands.dig(obj, 6) end,
|
||||
up = function() commands.dig(obj, 5) end,
|
||||
},
|
||||
|
||||
place = {
|
||||
left = function(nodename) commands.place(obj, nodename, 1) end,
|
||||
right = function(nodename) commands.place(obj,nodename, 2) end,
|
||||
forward = function(nodename) commands.place(obj,nodename, 3) end,
|
||||
backward = function(nodename) commands.place(obj,nodename, 4) end,
|
||||
down = function(nodename) commands.place(obj,nodename, 6) end,
|
||||
up = function(nodename) commands.place(obj,nodename, 5) end,
|
||||
},
|
||||
|
||||
insert = { -- insert item from inventory into another inventory TODO
|
||||
forward = function(item, inventory) robot_insert(obj, item, inventory,1) end,
|
||||
backward = function(item, inventory) robot_insert(obj, item, inventory,2) end,
|
||||
down = function(item, inventory) robot_insert(obj, item, inventory,3) end,
|
||||
up = function(item, inventory) robot_insert(obj, item, inventory,4) end,
|
||||
},
|
||||
|
||||
take = {}, -- take item from inventory TODO
|
||||
|
||||
selfpos = function() return obj:getpos() end,
|
||||
|
||||
find_nodes =
|
||||
function(nodename,r)
|
||||
if r>8 then return false end
|
||||
return (minetest.find_node_near(obj:getpos(), r, nodename)~=nil)
|
||||
end, -- in radius around position
|
||||
|
||||
|
||||
read_node = { -- returns node name
|
||||
left = function() return commands.read_node(obj, 1) end,
|
||||
right = function() return commands.read_node(obj, 2) end,
|
||||
forward = function() return commands.read_node(obj, 3) end,
|
||||
backward = function() return commands.read_node(obj, 4) end,
|
||||
down = function() return commands.read_node(obj, 6) end,
|
||||
up = function() return commands.read_node(obj, 5) end,
|
||||
},
|
||||
|
||||
say = function(text)
|
||||
minetest.chat_send_all("<robot ".. name .. "> " .. text)
|
||||
end,
|
||||
|
||||
|
||||
string = {
|
||||
byte = string.byte, char = string.char,
|
||||
find = string.find,
|
||||
format = string.format, gsub = string.gsub,
|
||||
len = string.len, lower = string.lower,
|
||||
upper = string.upper, rep = string.rep,
|
||||
reverse = string.reverse, sub = string.sub,
|
||||
},
|
||||
math = {
|
||||
abs = math.abs, acos = math.acos,
|
||||
asin = math.asin, atan = math.atan,
|
||||
atan2 = math.atan2, ceil = math.ceil,
|
||||
cos = math.cos, cosh = math.cosh,
|
||||
deg = math.deg, exp = math.exp,
|
||||
floor = math.floor, fmod = math.fmod,
|
||||
frexp = math.frexp, huge = math.huge,
|
||||
ldexp = math.ldexp, log = math.log,
|
||||
log10 = math.log10, max = math.max,
|
||||
min = math.min, modf = math.modf,
|
||||
pi = math.pi, pow = math.pow,
|
||||
rad = math.rad, random = math.random,
|
||||
sin = math.sin, sinh = math.sinh,
|
||||
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,
|
||||
time = os.time,
|
||||
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local function check_code(code)
|
||||
|
||||
local bad_code = {"while ", "for ", "do ", "repeat ", "until ", "goto "}
|
||||
|
||||
for _, v in pairs(bad_code) do
|
||||
if string.find(code, v) then
|
||||
return v .. " is not allowed!";
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local function CompileCode ( script )
|
||||
local ScriptFunc, CompileError = loadstring( script )
|
||||
if CompileError then
|
||||
return nil, CompileError
|
||||
end
|
||||
return ScriptFunc, nil
|
||||
end
|
||||
|
||||
local function initSandbox ( name )
|
||||
basic_robot.data[name].sandbox = getSandboxEnv (name);
|
||||
end
|
||||
|
||||
local function setCode( name, script ) -- to run script: 1. initSandbox 2. setCode 3. runSandbox
|
||||
local err;
|
||||
err = check_code(script);
|
||||
if err then return err end
|
||||
local bytecode, err = CompileCode ( script );
|
||||
if err then return err end
|
||||
basic_robot.data[name].bytecode = bytecode;
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local function runSandbox( name)
|
||||
|
||||
local ScriptFunc = basic_robot.data[name].bytecode;
|
||||
if not ScriptFunc then
|
||||
return "Bytecode missing."
|
||||
end
|
||||
|
||||
setfenv( ScriptFunc, basic_robot.data[name].sandbox )
|
||||
|
||||
local Result, RuntimeError = pcall( ScriptFunc )
|
||||
if RuntimeError then
|
||||
return RuntimeError
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- note: to see memory used by lua in kbytes: collectgarbage("count")
|
||||
-- /spawnentity basic_robot:robot
|
||||
|
||||
-- TODO.. display form when right click robot
|
||||
local function update_formspec_robot(self)
|
||||
|
||||
end
|
||||
|
||||
minetest.register_entity("basic_robot:robot",{
|
||||
energy = 1,
|
||||
owner = "",
|
||||
hp_max = 10,
|
||||
code = "",
|
||||
timer = 0,
|
||||
timestep = 1,
|
||||
spawnpos = "",
|
||||
visual="cube",
|
||||
visual_size={x=1,y=1},
|
||||
running = 0,
|
||||
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
|
||||
physical=true,
|
||||
|
||||
textures={"arrow.png","basic_machine_side.png","face.png","basic_machine_side.png","basic_machine_side.png","basic_machine_side.png"},
|
||||
|
||||
on_activate = function(self, staticdata)
|
||||
|
||||
-- how to make it remember owner when it reactivates ?? staticdata seems to be empty
|
||||
|
||||
if staticdata~="" then -- reactivate robot
|
||||
|
||||
self.owner = staticdata; -- remember its owner
|
||||
if not basic_robot.data[self.owner] then
|
||||
self.object:remove();
|
||||
minetest.chat_send_player(self.owner, "#ROBOT INIT: error. spawn robot again.")
|
||||
return;
|
||||
end
|
||||
|
||||
basic_robot.data[self.owner].obj = self.object;
|
||||
initSandbox ( self.owner )
|
||||
self.running = 1;
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- init robot
|
||||
minetest.after(0,
|
||||
function()
|
||||
if not basic_robot.data[self.owner] then
|
||||
basic_robot.data[self.owner] = {};
|
||||
end
|
||||
basic_robot.data[self.owner].obj = self.object;
|
||||
|
||||
initSandbox ( self.owner )
|
||||
local err = setCode( self.owner, self.code );
|
||||
if err then
|
||||
minetest.chat_send_player(self.owner,"#ROBOT CODE COMPILATION ERROR : " .. err)
|
||||
self.running = 0; -- stop execution
|
||||
end
|
||||
|
||||
self.running = 1
|
||||
end
|
||||
)
|
||||
|
||||
self.object:setyaw(0);
|
||||
|
||||
end,
|
||||
|
||||
get_staticdata = function(self)
|
||||
return self.owner;
|
||||
end,
|
||||
|
||||
on_punch = function (self, puncher, time_from_last_punch, tool_capabilities, dir)
|
||||
|
||||
end,
|
||||
|
||||
on_step = function(self, dtime)
|
||||
self.timer=self.timer+dtime
|
||||
if self.timer>self.timestep and self.running ~= 0 then
|
||||
self.timer = 0;
|
||||
local err = runSandbox(self.owner);
|
||||
if err then
|
||||
minetest.chat_send_player(self.owner,"#ROBOT ERROR : " .. err)
|
||||
self.running = 0; -- stop execution
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
return
|
||||
end,
|
||||
|
||||
on_rightclick = function(self, clicker)
|
||||
-- TODO
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
local robot_spawner_update_form = function (pos)
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
local x0,y0,z0;
|
||||
x0=meta:get_int("x0");y0=meta:get_int("y0");z0=meta:get_int("z0"); -- direction of velocity
|
||||
local code = minetest.formspec_escape(meta:get_string("code"));
|
||||
|
||||
local form =
|
||||
"size[8,6]" .. -- width, height
|
||||
"textarea[0.1,0.75;8.4,6.5;code;code;".. code.."]"..
|
||||
"button_exit[-0.25,-0.25;2,1;spawn;SPAWN]"..
|
||||
"button[1.75,-0.25;2,1;despawn;REMOVE]"..
|
||||
"button[5.25,-0.25;1,1;help;help]"..
|
||||
"button_exit[6.25,-0.25;1,1;reset;reset]"..
|
||||
"button_exit[7.25,-0.25;1,1;OK;OK]";
|
||||
|
||||
meta:set_string("formspec",form);
|
||||
|
||||
end
|
||||
|
||||
local spawn_robot = function(pos,node,ttl)
|
||||
if ttl<0 then return end
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
local t0 = meta:get_int("t");
|
||||
local t1 = minetest.get_gametime();
|
||||
local T = meta:get_int("T"); -- temperature
|
||||
|
||||
if t0>t1-2 then -- activated before natural time
|
||||
T=T+1;
|
||||
else
|
||||
if T>0 then
|
||||
T=T-1
|
||||
if t1-t0>5 then T = 0 end
|
||||
end
|
||||
end
|
||||
meta:set_int("T",T);
|
||||
meta:set_int("t",t1); -- update last activation time
|
||||
|
||||
if T > 2 then -- overheat
|
||||
minetest.sound_play("default_cool_lava",{pos = pos, max_hear_distance = 16, gain = 0.25})
|
||||
meta:set_string("infotext","overheat: temperature ".. T)
|
||||
return
|
||||
end
|
||||
|
||||
-- spawn robot on top
|
||||
pos.y=pos.y+1;
|
||||
local owner = meta:get_string("owner")
|
||||
-- if robot already exists do nothing
|
||||
if basic_robot.data[owner] and basic_robot.data[owner].obj then
|
||||
if basic_robot.data[owner].obj:getpos() then
|
||||
minetest.chat_send_player(owner,"#ROBOT ERROR : robot already active")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local obj = minetest.add_entity(pos,"basic_robot:robot");
|
||||
local luaent = obj:get_luaentity();
|
||||
luaent.owner = meta:get_string("owner");
|
||||
luaent.code = meta:get_string("code");
|
||||
luaent.spawnpos = minetest.pos_to_string({x=pos.x,y=pos.y-1,z=pos.z});
|
||||
end
|
||||
|
||||
minetest.register_node("basic_robot:spawner", {
|
||||
description = "Spawns robot",
|
||||
tiles = {"cpu.png"},
|
||||
groups = {oddly_breakable_by_hand=2,mesecon_effector_on = 1},
|
||||
drawtype = "allfaces",
|
||||
paramtype = "light",
|
||||
param1=1,
|
||||
walkable = true,
|
||||
alpha = 150,
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("owner", placer:get_player_name());
|
||||
local privs = minetest.get_player_privs(placer:get_player_name()); if privs.privs then meta:set_int("admin",1) end
|
||||
|
||||
meta:set_string("code","");
|
||||
robot_spawner_update_form(pos);
|
||||
|
||||
end,
|
||||
|
||||
mesecons = {effector = {
|
||||
action_on = spawn_robot
|
||||
}
|
||||
},
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
|
||||
local name = sender:get_player_name();
|
||||
if minetest.is_protected(pos,name) then return end
|
||||
|
||||
if fields.reset then
|
||||
local meta = minetest.get_meta(pos);
|
||||
meta:set_string("code","");
|
||||
robot_spawner_update_form(pos);
|
||||
return
|
||||
end
|
||||
|
||||
if fields.OK then
|
||||
local privs = minetest.get_player_privs(sender:get_player_name());
|
||||
local meta = minetest.get_meta(pos);
|
||||
--minetest.chat_send_all("form at " .. dump(pos) .. " fields " .. dump(fields))
|
||||
|
||||
if fields.code then
|
||||
local code = fields.code or "";
|
||||
meta:set_string("code", code)
|
||||
end
|
||||
|
||||
robot_spawner_update_form(pos);
|
||||
return
|
||||
end
|
||||
|
||||
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"..
|
||||
"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"..
|
||||
"turn.left(), turn.right(), turn.angle(45)\n"..
|
||||
"dig.direction(), place.direction(\"default:dirt\")\nread_node.direction() tells you names of nodes\n"..
|
||||
"find_nodes(3,\"default:dirt\") 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"..
|
||||
"say(\"hello\") will speak";
|
||||
|
||||
|
||||
text = minetest.formspec_escape(text);
|
||||
|
||||
local form = "size [8,7] textarea[0,0;8.5,8.5;help;HELP;".. text.."]"
|
||||
minetest.show_formspec(sender:get_player_name(), "robot_help", form);
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if fields.spawn then
|
||||
spawn_robot(pos,0,0);
|
||||
return
|
||||
end
|
||||
|
||||
if fields.despawn then
|
||||
local meta = minetest.get_meta(pos);
|
||||
local owner = meta:get_string("owner");
|
||||
|
||||
if not basic_robot.data[owner] then return end
|
||||
if basic_robot.data[owner].obj then
|
||||
basic_robot.data[owner].obj:remove();
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
end,
|
||||
|
||||
})
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = "basic_robot:spawner",
|
||||
recipe = {
|
||||
{"default:mese_crystal", "default:mese_crystal","default:mese_crystal"},
|
||||
{"default:mese_crystal", "default:mese_crystal","default:mese_crystal"},
|
||||
{"default:stone", "default:steel_ingot", "default:stone"}
|
||||
}
|
||||
})
|
BIN
textures/arrow.png
Normal file
BIN
textures/arrow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
textures/basic_machine_side.png
Normal file
BIN
textures/basic_machine_side.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
textures/cpu.png
Normal file
BIN
textures/cpu.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 676 B |
BIN
textures/face.png
Normal file
BIN
textures/face.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 795 B |
Loading…
x
Reference in New Issue
Block a user