From 893fb0ebb2cae887eba01cc12d13fe2672589951 Mon Sep 17 00:00:00 2001 From: Novatux Date: Sat, 15 Jun 2013 19:54:40 +0200 Subject: [PATCH] Initial commit --- .gitignore | 1 + depends.txt | 2 + init.lua | 2 + models/turtle.x | 129 ++++++ progs/programs_here.txt | 0 turtle.lua | 957 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1091 insertions(+) create mode 100644 .gitignore create mode 100644 depends.txt create mode 100644 init.lua create mode 100644 models/turtle.x create mode 100644 progs/programs_here.txt create mode 100644 turtle.lua diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b25c15b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..0b73adb --- /dev/null +++ b/depends.txt @@ -0,0 +1,2 @@ +pipeworks + diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..27d68b0 --- /dev/null +++ b/init.lua @@ -0,0 +1,2 @@ +local modpath = minetest.get_modpath("turtle") +dofile(modpath.."/turtle.lua") diff --git a/models/turtle.x b/models/turtle.x new file mode 100644 index 0000000..df92bee --- /dev/null +++ b/models/turtle.x @@ -0,0 +1,129 @@ +xof 0303txt 0032 + +template XSkinMeshHeader { + <3cf169ce-ff7c-44ab-93c0-f78f62d172e2> + WORD nMaxSkinWeightsPerVertex; + WORD nMaxSkinWeightsPerFace; + WORD nBones; +} + +template SkinWeights { + <6f0d123b-bad2-4167-a0d0-80224f25fabb> + STRING transformNodeName; + DWORD nWeights; + array DWORD vertexIndices[nWeights]; + array float weights[nWeights]; + Matrix4x4 matrixOffset; +} + +Frame Root { + FrameTransformMatrix { + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 1.000000, 0.000000, + 0.000000, 1.000000,-0.000000, 0.000000, + 0.000000, 0.000000, 0.000000, 1.000000;; + } + Frame Armature { + FrameTransformMatrix { + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.000000,-1.000000, 0.000000, + -0.000000, 1.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 0.000000, 1.000000;; + } + Frame Armature_Bone { + FrameTransformMatrix { + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 1.000000, 0.000000, + 0.000000,-1.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 0.000000, 1.000000;; + } + } //End of Armature_Bone + } //End of Armature + Frame Cube { + FrameTransformMatrix { + 5.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 5.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 5.000000, 0.000000, + 0.000000, 0.000000, 0.000000, 1.000000;; + } + Mesh { //Cube_001 Mesh + 24; + -1.000000;-1.000000;-1.000000;, + -1.000000; 1.000000;-1.000000;, + -1.000000; 1.000000; 1.000000;, + -1.000000;-1.000000; 1.000000;, + -1.000000; 1.000000;-1.000000;, + 1.000000; 1.000000;-1.000000;, + 1.000000; 1.000000; 1.000000;, + -1.000000; 1.000000; 1.000000;, + 1.000000; 1.000000;-1.000000;, + 1.000000;-1.000000;-1.000000;, + 1.000000;-1.000000; 1.000000;, + 1.000000; 1.000000; 1.000000;, + 1.000000;-1.000000;-1.000000;, + -1.000000;-1.000000;-1.000000;, + -1.000000;-1.000000; 1.000000;, + 1.000000;-1.000000; 1.000000;, + 1.000000;-1.000000;-1.000000;, + 1.000000; 1.000000;-1.000000;, + -1.000000; 1.000000;-1.000000;, + -1.000000;-1.000000;-1.000000;, + -1.000000;-1.000000; 1.000000;, + -1.000000; 1.000000; 1.000000;, + 1.000000; 1.000000; 1.000000;, + 1.000000;-1.000000; 1.000000;; + 6; + 4;0;1;2;3;, + 4;4;5;6;7;, + 4;8;9;10;11;, + 4;12;13;14;15;, + 4;16;17;18;19;, + 4;20;21;22;23;; + MeshNormals { //Cube_001 Normals + 24; + -1.000000; 0.000000; 0.000000;, + -1.000000; 0.000000; 0.000000;, + -1.000000; 0.000000; 0.000000;, + -1.000000; 0.000000; 0.000000;, + 0.000000; 1.000000;-0.000000;, + 0.000000; 1.000000;-0.000000;, + 0.000000; 1.000000;-0.000000;, + 0.000000; 1.000000;-0.000000;, + 1.000000; 0.000000;-0.000000;, + 1.000000; 0.000000;-0.000000;, + 1.000000; 0.000000;-0.000000;, + 1.000000; 0.000000;-0.000000;, + 0.000000;-1.000000; 0.000000;, + 0.000000;-1.000000; 0.000000;, + 0.000000;-1.000000; 0.000000;, + 0.000000;-1.000000; 0.000000;, + -0.000000; 0.000000;-1.000000;, + -0.000000; 0.000000;-1.000000;, + -0.000000; 0.000000;-1.000000;, + -0.000000; 0.000000;-1.000000;, + -0.000000; 0.000000; 1.000000;, + -0.000000; 0.000000; 1.000000;, + -0.000000; 0.000000; 1.000000;, + -0.000000; 0.000000; 1.000000;; + 6; + 4;0;1;2;3;, + 4;4;5;6;7;, + 4;8;9;10;11;, + 4;12;13;14;15;, + 4;16;17;18;19;, + 4;20;21;22;23;; + } //End of Cube_001 Normals + MeshMaterialList { //Cube_001 Material List + 1; + 1; + 0;; + Material Default_Material { + 0.800000; 0.800000; 0.800000; 0.800000;; + 96.078431; + 0.500000; 0.500000; 0.500000;; + 0.000000; 0.000000; 0.000000;; + } + } //End of Cube_001 Material List + } //End of Cube_001 Mesh + } //End of Cube +} //End of Root Frame diff --git a/progs/programs_here.txt b/progs/programs_here.txt new file mode 100644 index 0000000..e69de29 diff --git a/turtle.lua b/turtle.lua new file mode 100644 index 0000000..db01e6b --- /dev/null +++ b/turtle.lua @@ -0,0 +1,957 @@ +-- Turtle mod for Minetest +-- License: LGPL + +local FUEL_EFFICIENCY = 3 -- How many moves can the turtle do with a second fuel +local TURTLES_FORCE_LOAD = true -- Useless for now, has to wait until force_load is merged + + + +--TODO: Change serialization so that it supports functions +local safe_serialize = function(value) + return minetest.serialize(value) +end +local safe_deserialize = minetest.deserialize + +local turtle_invs +local turtle_updates + +local serialize_inv = function(l) + local l2={} + for _,i in pairs(l or {}) do + l2[_]=i:to_table() + end + return l2 +end + +local deserialize_inv = function(l) + local l2={} + for _,i in pairs(l or {}) do + l2[_]=ItemStack(i) + end + return l2 +end + + + +local wpath = minetest.get_worldpath() +local function read_file(fn) + local f = io.open(fn, "r") + if f==nil then return {} end + local t = f:read("*all") + f:close() + if t=="" or t==nil then return {} end + return minetest.deserialize(t) +end + +local function write_file(fn, tbl) + local f = io.open(fn, "w") + f:write(minetest.serialize(tbl)) + f:close() +end + +local get_turtle_info + +turtle_infos = read_file(wpath.."/turtle_infos") +turtle_updates = read_file(wpath.."/turtle_updates") + +turtle_updates_to_add={} + +local tupdate + +minetest.register_globalstep(function(dtime) + for _, timer in ipairs(turtle_updates_to_add) do + table.insert(turtle_updates, timer) + end + turtle_updates_to_add = {} + for index, timer in ipairs(turtle_updates) do + local info = get_turtle_info(timer.update.turtle) + if info["turtle"]~=nil then --turtle is loaded + timer.time = timer.time - dtime + if timer.time <= 0 then + tupdate(timer.update) + table.remove(turtle_updates,index) + end + end + end +end) + +local function turtle_add_update(time, update) + table.insert(turtle_updates_to_add, {time=time, update=update}) +end + +minetest.register_on_shutdown(function() + for turtle,i in pairs(turtle_infos) do + i["turtle"]=nil + i["inventory"]=serialize_inv(turtle_invs:get_list(turtle)) + end + write_file(wpath.."/turtle_infos",turtle_infos) + for _, timer in ipairs(turtle_updates_to_add) do + table.insert(turtle_updates, timer) + end + write_file(wpath.."/turtle_updates",turtle_updates) +end) + +get_turtle_info = function(turtle) + if turtle_infos[turtle]==nil then turtle_infos[turtle]={} end + return turtle_infos[turtle] +end + +local function get_turtle_id() + i=0 + while true do + if turtle_infos["turtle"..tostring(i)]==nil then return "turtle"..tostring(i) end + i=i+1 + end +end + +local function round_pos(p) + return {x=math.floor(p.x+0.5), + y=math.floor(p.y+0.5), + z=math.floor(p.z+0.5)} +end + +local update_formspec = function(turtle, code, errmsg, filename, player, exit) + local info = get_turtle_info(turtle) + info["code"]=code or "" + if minetest.formspec_escape then + code = minetest.formspec_escape(code or "") + errmsg = minetest.formspec_escape(errmsg or "") + else + code = string.gsub(code or "", "%[", "(") -- would otherwise + code = string.gsub(code, "%]", ")") -- corrupt formspec + errmsg = string.gsub(errmsg or "", "%[", "(") -- would otherwise + errmsg = string.gsub(errmsg, "%]", ")") -- corrupt formspec + end + info["filename"] = filename + info["formspec"]= "size[9,10]".. + "textarea[0.3,0;4.7,5;code;;"..code.."]".. + "list[detached:turtle:invs;"..turtle..";4.8,0;4,4;]".. + "image_button[0,4.6;2.5,1;turtle_execute.png;program;]".. + "image_button_exit[8.72,-0.25;0.425,0.4;turtle_close.png;exit;]".. + "label[4.6,4;"..errmsg.."]".. + "list[current_player;main;0.5,6;8,4;]".. + "field[3,5;4,1;filename;Filename:;"..filename.."]".. + "button[7,4.65;1,1;open;Open]".. + "button[8,4.65;1,1;save;Save]" + info["heat"]=0 + if exit==nil then + minetest.show_formspec(player:get_player_name(), turtle, info["formspec"]) + end +end + +-------------------- +-- Overheat stuff -- +-------------------- + +local heat = function (turtle) -- warm up + local info = get_turtle_info(turtle) + local h = info["heat"] + if h ~= nil then + info["heat"]=h+1 + else + info["heat"]=1 + end +end + +local cool = function (turtle) -- cool down after a while + local info = get_turtle_info(turtle) + local h = info["heat"] + if h ~= nil then + info["heat"]=h-1 + end +end + +local overheat = function (turtle) -- determine if too hot + local info = get_turtle_info(turtle) + local h = info["heat"] + return h==nil or h>400 +end + +------------------- +-- Parsing stuff -- +------------------- + +local code_prohibited = function(code) + -- Clean code + local prohibited = {"while", "for", "repeat", "until", "goto"}--, "function"} + for _, p in ipairs(prohibited) do + if string.find(code, "%A"..p.."%A") then + return "Prohibited command: "..p + end + end +end + +local safe_print = function(param) + print(dump(param)) +end + +local interrupt = function(params) + turtle_update(params.turtle, {type="interrupt", iid = params.iid}) +end + +local getinterrupt = function(turtle) + local interrupt = function (time, iid) -- iid = interrupt id + if type(time) ~= "number" then return end + local iid = iid or math.random() + local info = get_turtle_info(turtle) + --local interrupts = safe_deserialize(info["interrupts"]) or {} + local interrupts = info["interrupts"] or {} + local found = false + local search = safe_serialize(iid) + for _, i in ipairs(interrupts) do + if safe_serialize(i) == search then + found = true + break + end + end + if not found then + table.insert(interrupts, iid) + --info["interrupts"]= safe_serialize(interrupts) + info["interrupts"]=interrupts + end + turtle_add_update(time, {turtle=turtle, type="interrupt", iid = iid}) + end + return interrupt +end + +local function getv(dir) + if dir==0 then return {x=0,y=0,z=1} + elseif dir==1 then return {x=1,y=0,z=0} + elseif dir==2 then return {x=0,y=0,z=-1} + elseif dir==3 then return {x=-1,y=0,z=0} end +end + +local function v3add(v1,v2) + return {x=v1.x+v2.x,y=v1.y+v2.y,z=v1.z+v2.z} +end + +local function turtle_can_go(nname) + return nname=="air" or minetest.registered_nodes[nname].liquidtype~="none" +end + +local function stack_set_count(stack, count) + stack = stack:to_table() + if stack==nil then return nil end + stack.count=count + return ItemStack(stack) +end + +-------------------------- +-- /\ |--\ | -- +-- /--\ |__/ | -- +-- / \ | | -- +-------------------------- + +tupdate = function(update) + local turtle = update.turtle + local t = update.type + local info = get_turtle_info(turtle) + if t=="failmove" then + turtle_update(turtle,{type="failmove",iid=update.iid}) + elseif t=="endmove" then + info["moveint"]=nil + info["spos"]=info["npos"] + info["npos"]=nil + local tobject = info["turtle"] + tobject.object:setvelocity({x=0,y=0,z=0}) + tobject.object:setpos(info["spos"]) + turtle_update(turtle,{type="endmove",iid=update.iid}) + elseif t=="endturn" then + info["moveint"]=nil + info["dir"]=info["ndir"] + info["ndir"]=nil + info["rotate"]=nil + local tobject = info["turtle"] + tobject.object:setyaw(info["dir"]*math.pi/2) + turtle_update(turtle,{type="endmove",iid=update.iid}) + elseif t=="interrupt" then + interrupt(update) + elseif t=="cool" then + cool(turtle) + end +end + +local get_turtle_funcs = function(turtle) + return { + forward = function(iid) + if iid==nil then iid="nil" end + local info = get_turtle_info(turtle) + local tobject = info["turtle"] + if info["fuel"]==0 then + turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) + return + end + if info["moveint"]==nil then + local spos = info["spos"] + local dir = info["dir"] + info["npos"] = v3add(spos, getv(dir)) + if not turtle_can_go(minetest.env:get_node(info["npos"]).name) then + info["npos"]=nil + turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) + return + end + info["fuel"]=info["fuel"]-1 + info["moveint"]=iid + tobject.object:setvelocity(getv(dir)) + turtle_add_update(1,{turtle=turtle, type="endmove", iid=iid}) + else + turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) + end + end, + back = function(iid) + if iid==nil then iid="nil" end + local info = get_turtle_info(turtle) + local tobject = info["turtle"] + if info["fuel"]==0 then + turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) + return + end + if info["moveint"]==nil then + local spos = info["spos"] + local dir = (info["dir"]+2)%4 + info["npos"] = v3add(spos, getv(dir)) + if not turtle_can_go(minetest.env:get_node(info["npos"]).name) then + info["npos"]=nil + turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) + return + end + info["fuel"]=info["fuel"]-1 + info["moveint"]=iid + tobject.object:setvelocity(getv(dir)) + turtle_add_update(1,{turtle=turtle, type="endmove", iid=iid}) + else + turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) + end + end, + up = function(iid) + if iid==nil then iid="nil" end + local info = get_turtle_info(turtle) + local tobject = info["turtle"] + if info["fuel"]==0 then + turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) + return + end + if info["moveint"]==nil then + local spos = info["spos"] + info["npos"] = v3add(spos, {x=0,y=1,z=0}) + if not turtle_can_go(minetest.env:get_node(info["npos"]).name) then + info["npos"]=nil + turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) + return + end + info["fuel"]=info["fuel"]-1 + info["moveint"]=iid + tobject.object:setvelocity({x=0,y=1,z=0}) + turtle_add_update(1,{turtle=turtle, type="endmove", iid=iid}) + else + turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) + end + end, + down = function(iid) + if iid==nil then iid="nil" end + local info = get_turtle_info(turtle) + local tobject = info["turtle"] + if info["fuel"]==0 then + turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) + return + end + if info["moveint"]==nil then + local spos = info["spos"] + info["npos"] = v3add(spos, {x=0,y=-1,z=0}) + if not turtle_can_go(minetest.env:get_node(info["npos"]).name) then + info["npos"]=nil + turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) + return + end + info["fuel"]=info["fuel"]-1 + info["moveint"]=iid + tobject.object:setvelocity({x=0,y=-1,z=0}) + turtle_add_update(1,{turtle=turtle, type="endmove", iid=iid}) + else + turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) + end + end, + turnleft = function(iid) + if iid==nil then iid="nil" end + local info = get_turtle_info(turtle) + local tobject = info["turtle"] + if info["moveint"]==nil then + local dir = info["dir"] + info["ndir"]=(dir+3)%4 + info["moveint"]=iid + info["rotate"]=math.pi/2 + turtle_add_update(1,{turtle=turtle, type="endturn", iid=iid}) + else + turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) + end + end, + turnright = function(iid) + if iid==nil then iid="nil" end + local info = get_turtle_info(turtle) + local tobject = info["turtle"] + if info["moveint"]==nil then + local dir = info["dir"] + info["ndir"]=(dir+1)%4 + info["moveint"]=iid + info["rotate"]=-math.pi/2 + turtle_add_update(1,{turtle=turtle, type="endturn", iid=iid}) + else + turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) + end + end, + detect = function() + local info = get_turtle_info(turtle) + local pos = v3add(info["spos"],getv(info["dir"])) + return minetest.env:get_node(pos).name + end, + detectup = function() + local info = get_turtle_info(turtle) + local pos = v3add(info["spos"],{x=0,y=1,z=0}) + return minetest.env:get_node(pos).name + end, + detectdown = function() + local info = get_turtle_info(turtle) + local pos = v3add(info["spos"],{x=0,y=-1,z=0}) + return minetest.env:get_node(pos).name + end, + dig = function() + local info = get_turtle_info(turtle) + local dpos = v3add(info["spos"],getv(info["dir"])) + local dnode = minetest.env:get_node(dpos) + if turtle_can_go(dnode.name) or dnode.name=="ignore" then return false end + local drops = minetest.get_node_drops(dnode.name, "default:pick_mese") + local _, dropped_item + for _, dropped_item in ipairs(drops) do + local leftover = turtle_invs:add_item(turtle,dropped_item) + minetest.env:add_item(info["spos"],leftover) + end + minetest.env:remove_node(dpos) + return true + end, + digup = function() + local info = get_turtle_info(turtle) + local dpos = v3add(info["spos"],{x=0,y=1,z=0}) + local dnode = minetest.env:get_node(dpos) + if turtle_can_go(dnode.name) or dnode.name=="ignore" then return false end + local drops = minetest.get_node_drops(dnode.name, "default:pick_mese") + local _, dropped_item + for _, dropped_item in ipairs(drops) do + local leftover = turtle_invs:add_item(turtle,dropped_item) + minetest.env:add_item(info["spos"],leftover) + end + minetest.env:remove_node(dpos) + return true + end, + digdown = function() + local info = get_turtle_info(turtle) + local dpos = v3add(info["spos"],{x=0,y=-1,z=0}) + local dnode = minetest.env:get_node(dpos) + if turtle_can_go(dnode.name) or dnode.name=="ignore" then return false end + local drops = minetest.get_node_drops(dnode.name, "default:pick_mese") + local _, dropped_item + for _, dropped_item in ipairs(drops) do + local leftover = turtle_invs:add_item(turtle,dropped_item) + minetest.env:add_item(info["spos"],leftover) + end + minetest.env:remove_node(dpos) + return true + end, + place = function(slot) + local info = get_turtle_info(turtle) + local ppos = v3add(info["spos"],getv(info["dir"])) + local dnode = minetest.env:get_node(ppos) + if (not turtle_can_go(dnode.name)) or dnode.name=="ignore" then return false end + local stack = turtle_invs:get_stack(turtle,slot) + if stack:is_empty() or minetest.registered_nodes[stack:get_name()]==nil then return false end + minetest.env:set_node(ppos, {name=stack:get_name()}) + stack:take_item() + turtle_invs:set_stack(turtle, slot, stack) + return true + end, + placeup = function(slot) + local info = get_turtle_info(turtle) + local ppos = v3add(info["spos"],{x=0,y=1,z=0}) + local dnode = minetest.env:get_node(ppos) + if (not turtle_can_go(dnode.name)) or dnode.name=="ignore" then return false end + local stack = turtle_invs:get_stack(turtle,slot) + if stack:is_empty() or minetest.registered_nodes[stack:get_name()]==nil then return false end + minetest.env:set_node(ppos, {name=stack:get_name()}) + stack:take_item() + turtle_invs:set_stack(turtle, slot, stack) + return true + end, + placedown = function(slot) + local info = get_turtle_info(turtle) + local ppos = v3add(info["spos"],{x=0,y=-1,z=0}) + local dnode = minetest.env:get_node(ppos) + if (not turtle_can_go(dnode.name)) or dnode.name=="ignore" then return false end + local stack = turtle_invs:get_stack(turtle,slot) + if stack:is_empty() or minetest.registered_nodes[stack:get_name()]==nil then return false end + minetest.env:set_node(ppos, {name=stack:get_name()}) + stack:take_item() + turtle_invs:set_stack(turtle, slot, stack) + return true + end, + getstack = function(slot) + local s = turtle_invs:get_stack(turtle,slot):to_table() + if s==nil then return {name="", count=0} end + return s + end, + moveto = function(slot1,slot2, nmax) + local stack1 = turtle_invs:get_stack(turtle,slot1) + local stack2 = turtle_invs:get_stack(turtle,slot2) + local move + if nmax==0 then nmax = stack1:get_count() end + if stack2:is_empty() then + move = math.min(stack1:get_count(), nmax) + local taken = stack1:take_item(move) + stack2:add_item(taken) + else + if stack1:get_name()~=stack2:get_name() then return 0 end + move = math.min(stack1:get_count(), stack2:get_free_space(), nmax) + local taken = stack1:take_item(move) + stack2:add_item(taken) + end + turtle_invs:set_stack(turtle,slot1,stack1) + turtle_invs:set_stack(turtle,slot2,stack2) + return move + end, + refuel = function(slot, nmax) + local info = get_turtle_info(turtle) + local stack = turtle_invs:get_stack(turtle, slot) + local fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = {stack}}) + if fuel.time<=0 then return false end + if nmax==nil then nmax=stack:get_count() end + local count = math.min(stack:get_count(), nmax) + local fs = stack:to_table() + fs["count"]=1 + local fstack = ItemStack(fs) + local fuel, afterfuel + fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = {fstack}}) + info["fuel"]=info["fuel"]+FUEL_EFFICIENCY*count*fuel.time + stack:take_item(count) + if afterfuel~=nil then + afterfuel = afterfuel.items[1] + end + if afterfuel~=nil then + afterfuel = stack_set_count(afterfuel, afterfuel:get_count()*count) + end + if afterfuel~=nil then + local leftover = stack:add_item(ItemStack(afterfuel)) + turtle_invs:set_stack(turtle, slot, stack) + local leftover2 = turtle_invs:add_item(turtle, leftover) + minetest.env:add_item(info["spos"],leftover2) + else + turtle_invs:set_stack(turtle, slot, stack) + end + end, + get_fuel_time = function() + local info = get_turtle_info(turtle) + return info["fuel"] + end, + craft = function(nmax) + local info = get_turtle_info(turtle) + local invl = turtle_invs:get_list(turtle) + local recipe = {} + local craftmax=nmax + for i=1,16 do + recipe[i]=ItemStack({name=invl[i]:get_name(),count=1}) + if invl[i]:get_count()>0 then + craftmax=math.min(craftmax, invl[i]:get_count()) + end + end + local result,new=minetest.get_craft_result({method="normal",width=4,items=recipe}) + if result.item:is_empty() then return 0 end + result=result.item + for i=1,16 do + invl[i]:take_item(craftmax) + turtle_invs:set_stack(turtle, i, invl[i]) + end + result = stack_set_count(result, result:get_count()*craftmax) + local leftover = turtle_invs:add_item(turtle,result) + minetest.env:add_item(info["spos"],leftover) + for i=1,16 do + local s=stack_set_count(new.items[i], new.items[i]:get_count()*craftmax) + if s~=nil then + local leftover = turtle_invs:add_item(turtle,s) + minetest.env:add_item(info["spos"],leftover) + end + end + end, + drop = function(slot) + local info = get_turtle_info(turtle) + local stack = turtle_invs:get_stack(turtle, slot) + turtle_invs:set_stack(turtle, slot, ItemStack("")) + local spos = info["spos"] + local item = tube_item({x=spos.x,y=spos.y,z=spos.z},stack) + item:get_luaentity().start_pos = {x=spos.x,y=spos.y,z=spos.z} + item:setvelocity(getv(info["dir"])) + end, + dropup = function(slot) + local info = get_turtle_info(turtle) + local stack = turtle_invs:get_stack(turtle, slot) + turtle_invs:set_stack(turtle, slot, ItemStack("")) + local spos = info["spos"] + local item = tube_item({x=spos.x,y=spos.y,z=spos.z},stack) + item:get_luaentity().start_pos = {x=spos.x,y=spos.y,z=spos.z} + item:setvelocity({x=0,y=1,z=0}) + end, + dropdown = function(slot) + local info = get_turtle_info(turtle) + local stack = turtle_invs:get_stack(turtle, slot) + turtle_invs:set_stack(turtle, slot, ItemStack("")) + local spos = info["spos"] + local item = tube_item({x=spos.x,y=spos.y,z=spos.z},stack) + item:get_luaentity().start_pos = {x=spos.x,y=spos.y,z=spos.z} + item:setvelocity({x=0,y=-1,z=0}) + end, + suck = function() + local info = get_turtle_info(turtle) + local frompos=v3add(info["spos"],getv(info["dir"])) + local fromnode=minetest.env:get_node(frompos) + local frominv + if not (minetest.registered_nodes[fromnode.name].tube and + minetest.registered_nodes[fromnode.name].tube.input_inventory) then + for _,object in ipairs(minetest.env:get_objects_inside_radius(frompos, 1)) do + if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" then + if object:get_luaentity().itemstring ~= "" then + local leftover = turtle_invs:add_item(turtle,ItemStack(object:get_luaentity().itemstring)) + minetest.env:add_item(info["spos"],leftover) + object:get_luaentity().itemstring = "" + object:remove() + return + end + end + end + return + end + local frommeta=minetest.env:get_meta(frompos) + local frominvname=minetest.registered_nodes[fromnode.name].tube.input_inventory + local frominv=frommeta:get_inventory() + for spos,stack in ipairs(frominv:get_list(frominvname)) do + if stack:get_name()~="" then + local leftover = turtle_invs:add_item(turtle,stack) + frominv:set_stack(frominvname, spos ,leftover) + return + end + end + end, + suckup = function() + local info = get_turtle_info(turtle) + local frompos=v3add(info["spos"],{x=0,y=1,z=0}) + local fromnode=minetest.env:get_node(frompos) + local frominv + if not (minetest.registered_nodes[fromnode.name].tube and + minetest.registered_nodes[fromnode.name].tube.input_inventory) then + for _,object in ipairs(minetest.env:get_objects_inside_radius(frompos, 1)) do + if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" then + if object:get_luaentity().itemstring ~= "" then + local leftover = turtle_invs:add_item(turtle,ItemStack(object:get_luaentity().itemstring)) + minetest.env:add_item(info["spos"],leftover) + object:get_luaentity().itemstring = "" + object:remove() + return + end + end + end + return + end + local frommeta=minetest.env:get_meta(frompos) + local frominvname=minetest.registered_nodes[fromnode.name].tube.input_inventory + local frominv=frommeta:get_inventory() + for spos,stack in ipairs(frominv:get_list(frominvname)) do + if stack:get_name()~="" then + local leftover = turtle_invs:add_item(turtle,stack) + frominv:set_stack(frominvname, spos, leftover) + return + end + end + end, + suckdown = function() + local info = get_turtle_info(turtle) + local frompos=v3add(info["spos"],{x=0,y=-1,z=0}) + local fromnode=minetest.env:get_node(frompos) + local frominv + if not (minetest.registered_nodes[fromnode.name].tube and + minetest.registered_nodes[fromnode.name].tube.input_inventory) then + for _,object in ipairs(minetest.env:get_objects_inside_radius(frompos, 1)) do + if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" then + if object:get_luaentity().itemstring ~= "" then + local leftover = turtle_invs:add_item(turtle,ItemStack(object:get_luaentity().itemstring)) + minetest.env:add_item(info["spos"],leftover) + object:get_luaentity().itemstring = "" + object:remove() + return + end + end + end + return + end + local frommeta=minetest.env:get_meta(frompos) + local frominvname=minetest.registered_nodes[fromnode.name].tube.input_inventory + local frominv=frommeta:get_inventory() + for spos,stack in ipairs(frominv:get_list(frominvname)) do + if stack:get_name()~="" then + local leftover = turtle_invs:add_item(turtle,stack) + frominv:set_stack(frominvname, spos, leftover) + return + end + end + end, + } +end + + + +local create_environment = function(turtle, mem, event) + -- Gather variables for the environment + return { + print = safe_print, + interrupt = getinterrupt(turtle), + turtle = get_turtle_funcs(turtle), + mem = mem, + tostring = tostring, + tonumber = tonumber, + string = { + byte = string.byte, + char = string.char, + find = string.find, + format = string.format, + gmatch = string.gmatch, + gsub = string.gsub, + len = string.len, + lower = string.lower, + match = string.match, + 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 = { + insert = table.insert, + maxn = table.maxn, + remove = table.remove, + sort = table.sort + }, + event = event, + } +end + +local create_sandbox = function (code, env) + -- Create Sandbox + if code:byte(1) == 27 then + return _, "You Hacker You! Don't use binary code!" + end + f, msg = loadstring(code) + if not f then return _, msg end + setfenv(f, env) + return f +end + +local do_overheat = function (turtle) + -- Overheat protection + heat(turtle) + turtle_add_update(0.5,{turtle=turtle, type="cool"}) + if overheat(turtle) then + --TODO + return true + end +end + +local load_memory = function(turtle) + local info = get_turtle_info(turtle) + return info["memory"] or {} +end + +local save_memory = function(turtle, mem) + local info = get_turtle_info(turtle) + info["memory"] = mem +end + +local interrupt_allow = function (turtle, event) + if event.type ~= "interrupt" then return true end + local info = get_turtle_info(turtle) + local interrupts = info["interrupts"] or {} + local search = safe_serialize(event.iid) + for _, i in ipairs(interrupts) do + if safe_serialize(i) == search then + return true + end + end + + return false +end + +---------------------- +-- Parsing function -- +---------------------- + +turtle_update = function (turtle, event) + local info = get_turtle_info(turtle) + if not interrupt_allow(turtle, event) then return end + if do_overheat(turtle) then return end + + -- load code & mem from memory + local mem = load_memory(turtle) + local code = info["code"] + + -- make sure code is ok and create environment + local prohibited = code_prohibited(code) + if prohibited then return prohibited end + local env = create_environment(turtle, mem, event) + + -- create the sandbox and execute code + local chunk, msg = create_sandbox (code, env) + if not chunk then return msg end + local success, msg = pcall(f) + if not success then + print(msg) + update_formspec(turtle, code, msg, info["filename"], nil, "") + end + + save_memory(turtle, mem) +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname:sub(1,6)~="turtle" then return end + update_formspec(formname, fields.code, "", fields.filename, player, fields.exit) + if fields.program~=nil or fields.exit~=nil then + local err = turtle_update(formname, {type="program"}) + if err then print(err) end + update_formspec(formname, fields.code, err, fields.filename, player, fields.exit) + end + if fields.save then + if fields.filename:sub(1,1)=="." then return end -- Not allowed to save because could change the user's files (including the mod's files, dangerous) + local fn = minetest.get_modpath("turtle").."/progs/"..fields.filename..".lua" + local f = io.open(fn, "w") + f:write(fields.code) + f:close() + end + if fields.open then + local fn = minetest.get_modpath("turtle").."/progs/"..fields.filename..".lua" + local f = io.open(fn, "r") + local code + if f==nil then + code="" + else + code = f:read("*all") + f:close() + end + if code==nil then code="" end + update_formspec(formname, code, "", fields.filename, player) + end +end) + +minetest.register_craftitem("turtle:turtle",{ + description="Turtle", + image = "turtle_turtle_inv.png", + on_place = function(itemstack, placer, pointed_thing) + if pointed_thing.type~="node" then return end + local obj = minetest.env:add_entity(pointed_thing.above, "turtle:turtle") + itemstack:take_item() + return itemstack + end +}) + +minetest.register_craft( { + output = "turtle:turtle", + recipe = { + { "default:diamond", "default:pick_mese", "default:diamond" }, + { "default:diamond", "default:mese", "default:diamond" }, + { "default:mese", "default:diamond", "default:mese" }, + }, +}) + +turtle_invs = minetest.create_detached_inventory("turtle:invs") +for turtle,i in pairs(turtle_infos) do + turtle_invs:set_size(turtle,16) + for l,stack in pairs(deserialize_inv(i["inventory"])) do + turtle_invs:set_stack(turtle, l, stack) + end +end + +minetest.register_entity("turtle:turtle", { + physical = true, + force_load = TURTLES_FORCE_LOAD, + collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5}, + visual = "mesh", + mesh="turtle.x", + textures = {"default_wood.png","default_wood.png"}, + visual_size = {x=1, y=1}, + on_activate = function(self, staticdata) + local info + if staticdata==nil or staticdata=="" then + self.n=get_turtle_id() + info=get_turtle_info(self.n) + turtle_invs:set_size(self.n,16) + info["turtle"]=self + info["spos"]=round_pos(self.object:getpos()) + info["dir"]=0 + info["fuel"]=0 + info["formspec"]= "size[9,10]".. + "textarea[0.3,0;4.7,5;code;;]".. + "list[detached:turtle:invs;"..self.n..";4.8,0;4,4;]".. + "image_button[0,4.6;2.5,1;turtle_execute.png;program;]".. + "image_button_exit[8.72,-0.25;0.425,0.4;turtle_close.png;exit;]".. + "label[4.6,4;]".. + "list[current_player;main;0.5,6;8,4;]".. + "field[3,5;4,1;filename;Filename:;]".. + "button[7,4.65;1,1;open;Open]".. + "button[8,4.65;1,1;save;Save]" + else + self.n=staticdata + info=get_turtle_info(self.n) + info["turtle"]=self + end + end, + on_step = function(self, dtime) + local info=get_turtle_info(self.n) + if info["rotate"] then + self.object:setyaw(self.object:getyaw()+info["rotate"]*dtime) + end + end, + on_rightclick = function(self, clicker) + minetest.show_formspec(clicker:get_player_name(), self.n, get_turtle_info(self.n)["formspec"]) + end, + on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir) + self.object:remove() + minetest.env:add_item(turtle_infos[self.n]["spos"],"turtle:turtle") + for i=1,16 do + turtle_invs:set_stack(self.n, i, ItemStack("")) + end + turtle_infos[self.n] = nil + end, + get_staticdata = function(self) + return self.n + end, +})