diff --git a/API.md b/API.md new file mode 100644 index 0000000..719ad23 --- /dev/null +++ b/API.md @@ -0,0 +1,84 @@ +# ComputerTest API + +This api is two parts, with notes/documentation above, and functions below + +## Init Function +See README.md for basic setup. + +Once you have a turtle, you need an init function such as: +```lua +function init(turtle) + turtle:moveForward() +end +``` +## Directions +Many commands have directions. These come in sets like: +```lua +turtle:moveForward() +turtle:moveBackward() +turtle:moveRight() +turtle:moveLeft() +turtle:moveUp() +turtle:moveDown() +``` + +This will be referred to as `turtle:move[direction]()` + +Up and down are always the same, but orientation is changed with +```lua +turtle:turnLeft() +turtle:turnRight() +``` + +## The turtle's inventory + +The turtle has 16 'turtleslots' numbered 1-16. + +## Config + +Edit init.lua in the config object at the top to change the config. +Actions take effect on restart/reload. + +## Action Functions (do things) + +### `turtle:yield(reason)` +- Yields + +Many functions yield to prevent lag, balance the mod, and to prevent hanging. +You MUST have a yield in all time-consuming processes. +If you don't yield, and your turtle takes too long, the entire server will lag/halt. +The reason can be any string, and is visible in debug.txt (can turn on/off debugging in config) +###`turtle:mine[direction]()` +- Yields +- Uses Fuel +The turtle will mine the block next to it and take the items of the block. It will take items from chests, too +They return true if the block was removed + +## Inventory/Fuel functions + +### `turtle:useFuel()` +- Uses Fuel + Burns fuel. Once you run out of fuel, your turtle is stuck. +### `turtle:getFuel()` +Returns the number actions you can take until needing to refuel. +If this is zero, you won't be able to do much except refuel. +### `turtle:itemRefuel(turtleslot)` +Takes fuel from this turtleslot and increases the turtle's fuel +###`turtle:move[direction]()` +- Yields +- Uses Fuel + +Moves one block in that direction, yields, and consumes fuel + +## Info functions + +###`turtle:setName(name)` +Sets the turtle's name +###`turtle:debug(string)` +Prints string to debug.txt (can be disabled in config) + +## Other Functions + +I might be missing some, check out turtle.lua for more! + +All functions that start with TurtleEntity are callable (although some shouldn't be such as the ones marked as helper functions) \ No newline at end of file diff --git a/README.md b/README.md index a348ee9..348c40e 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,38 @@ A ComputerCraft-inspired mod for Minetest! - Turtles take time to do actions such as moving,mining,turning,etc - A "computertest" privilege, to limit turtle usage to trusted users (so only users with this privilege can use/edit turtles) +## An example of how to use this mod + +1. Install the ComputerTest mod +2. Get a turtle block using either + - The command `/giveme computertest:turtle` + - The creative menu + - The recipe + ``` +III +ICI +IMI +I = Steel Ingot , C = Chest , M = Mesa Block +``` +3. Place the turtle +4. Right click it and click "Upload Code" +5. Paste this into the large field (sometimes Minetest requires you to paste twice) and click "Upload Code" +```lua +function init(turtle) + while true do + turtle:moveForward() + turtle:turnRight() + end +end +``` +6. Watch as it spins around! + +## Other Information + +The API.md contains important documentation for programming. + +EXAMPLES.md contains some fun examples + ## Changes are Welcome! Anyone interested in adding these features can try in entity/turtle.lua, I'd be interested in any great working pull requests! @@ -18,6 +50,5 @@ Anyone interested in adding these features can try in entity/turtle.lua, I'd be ### Features to Add - Add dumping into chests to create fully-auto mining -- Add fuel, so everything consumes fuel until it runs out or refuels - Inventory management commands, such as crafting, sorting, and dropping -- The turtle code isn't sandboxed, so turtles could call dangerous functions. This has been mitigated by the "computertest" privilege, but proper sandboxing would work best. \ No newline at end of file +- The turtle code isn't sandboxed, so turtles could call dangerous functions. This has been mitigated by the "computertest" privilege, but proper sandboxing would work best. diff --git a/block/turtle.lua b/block/turtle.lua index a5a14fd..b6ab093 100644 --- a/block/turtle.lua +++ b/block/turtle.lua @@ -13,6 +13,12 @@ minetest.register_node("computertest:turtle", { }, groups = {oddly_breakable_by_hand=2}, paramtype2 = "facedir", + after_place_node = function(pos, placer) + if placer and placer:is_player() then + local meta = minetest.get_meta(pos) + meta:set_string("owner", placer:get_player_name()) + end + end, on_construct = function(pos) local turtle = minetest.add_entity(pos,"computertest:turtle") turtle = turtle:get_luaentity() diff --git a/entity/turtle.lua b/entity/turtle.lua index 74ad9fd..462b393 100644 --- a/entity/turtle.lua +++ b/entity/turtle.lua @@ -8,7 +8,9 @@ local FORM_NOPRIV = "size[9,1;]label[0,0;You do not have the 'computertest' priv local TURTLE_INVENTORYSIZE = 4*4 +---@returns TurtleEntity of that ID local function getTurtle(id) return computertest.turtles[id] end +---@returns true local function isValidInventoryIndex(index) return 0 < index and index <= TURTLE_INVENTORYSIZE end local function has_computertest_priv(player) return minetest.check_player_privs(player,"computertest") end @@ -28,12 +30,16 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) return not turtle:upload_code_to_turtle(player,"",false) end elseif isForm(FORMNAME_TURTLE_TERMINAL) then - if (fields.terminal_out ~= nil) then return true end + if (fields.terminal_out ~= nil) then + return true + end local id = tonumber(string.sub(formname,1+string.len(FORMNAME_TURTLE_TERMINAL))) local turtle = getTurtle(id) turtle.lastCommandRan = fields.terminal_in local command = fields.terminal_in - if command==nil or command=="" then return nil end + if command==nil or command=="" then + return nil + end command = "function init(turtle) return "..command.." end" local commandResult = turtle:upload_code_to_turtle(player,command, true) if (commandResult==nil) then @@ -45,7 +51,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) minetest.show_formspec(player:get_player_name(),FORMNAME_TURTLE_TERMINAL..id,turtle:get_formspec_terminal()); elseif isForm(FORMNAME_TURTLE_UPLOAD) then local id = tonumber(string.sub(formname,1+string.len(FORMNAME_TURTLE_UPLOAD))) - if (fields.button_upload == nil or fields.upload == nil) then return true end + if (fields.button_upload == nil or fields.upload == nil) then + return true + end local turtle = getTurtle(id) return not turtle:upload_code_to_turtle(player,fields.upload,false) else @@ -58,16 +66,20 @@ end) local timer = 0 minetest.register_globalstep(function(dtime) timer = timer + dtime - if (timer >= computertest.config.turtle_tick) then + while (timer >= computertest.config.turtle_tick) do for _,turtle in pairs(computertest.turtles) do if turtle.coroutine then if coroutine.status(turtle.coroutine)=="suspended" then - --TODO check for fuel here - local status, result = coroutine.resume(turtle.coroutine) - minetest.log("coroutine stat "..dump(status).." said "..dump(result)) - --elseif coroutine.status(turtle.coroutine)=="dead" then - --minetest.log("turtle #"..id.." has coroutine, but it's already done running") + if (turtle.fuel>0) then + local status, result = coroutine.resume(turtle.coroutine) + turtle:debug("coroutine stat "..dump(status).." said "..dump(result)) + turtle:debug("fuel="..turtle.fuel) + else + turtle:debug("No Fuel in turtle") + end end + --elseif coroutine.status(turtle.coroutine)=="dead" then + --minetest.log("turtle #"..id.." has coroutine, but it's already done running") elseif turtle.code then --minetest.log("turtle #"..id.." has no coroutine but has code! Making coroutine...") --TODO add some kinda timeout into coroutine @@ -83,9 +95,10 @@ minetest.register_globalstep(function(dtime) end end) --Code responsible for generating turtle entity and turtle interface -minetest.register_entity("computertest:turtle", { + +local TurtleEntity = { initial_properties = { - hp_max = 1, + hp_max = 20, is_visible = true, makes_footstep_sound = false, physical = true, @@ -101,260 +114,416 @@ minetest.register_entity("computertest:turtle", { "computertest_front.png", }, automatic_rotate = 0, - id = -1, + id = -1 }, +} - --MAIN TURTLE USER INTERFACE------------------------------------------ - get_formspec_inventory = function(turtle) - return "size[12,5;]" - .."button[0,0;2,1;open_terminal;Open Terminal]" - .."button[2,0;2,1;upload_code;Upload Code]" - .."button[4,0;2,1;factory_reset;Factory Reset]" - .."set_focus[open_terminal;true]" - .."list[".. turtle.inv_fullname..";main;8,1;4,4;]" - .."background[8,1;1,1;computertest_inventory.png]" - .."list[current_player;main;0,1;8,4;]"; - end, - get_formspec_terminal = function(turtle) - local previous_answers = turtle.previous_answers - local parsed_output = ""; - for i=1, #previous_answers do parsed_output = parsed_output .. minetest.formspec_escape(previous_answers[i]).."," end - --local saved_output = ""; - --for i=1, #previous_answers do saved_output = saved_output .. minetest.formspec_escape(previous_answers[i]).."\n" end - return - "size[12,9;]" - .."field_close_on_enter[terminal_in;false]" - .."field[0,0;12,1;terminal_in;;"..minetest.formspec_escape(turtle.lastCommandRan or "").."]" - .."set_focus[terminal_in;true]" - .."textlist[0,1;12,8;terminal_out;"..parsed_output.."]"; - end, - get_formspec_upload = function(turtle) - --TODO could indicate if code is already uploaded - return - "size[12,9;]" - .."button[0,0;2,1;button_upload;Upload Code to #"..turtle.id.."]" - .."field_close_on_enter[upload;false]" - .."textarea[0,1;12,8;upload;;"..minetest.formspec_escape(turtle.codeUncompiled or "").."]" - .."set_focus[upload;true]"; - end, - --- - ---@return true on success - --- - upload_code_to_turtle = function(turtle,player, code_string,run_for_result) - --Check permissions - if (not has_computertest_priv(player)) then - --minetest.debug(player:get_player_name().." does not have computertest priv") +--MAIN TURTLE HELPER FUNCTIONS------------------------------------------ +function TurtleEntity:getTurtleslot(turtleslot) + if not isValidInventoryIndex(turtleslot) then return nil end + return self.inv:get_stack("main",turtleslot) +end +function TurtleEntity:setTurtleslot(turtleslot, stack) + if not isValidInventoryIndex(turtleslot) then return false end + self.inv:set_stack("main", turtleslot,stack) + return true +end +function TurtleEntity:move(nodeLocation) + --Verify new pos is empty + if (nodeLocation == nil or minetest.get_node(nodeLocation).name~="air") then + self:yield("Moving") + return false + end + --Take Action + self.object:set_pos(nodeLocation) + self:yield("Moving",true) + return true +end +function TurtleEntity:mine(nodeLocation) + if nodeLocation == nil then + return false + end + local node = minetest.get_node(nodeLocation) + if (node.name=="air") then + return false + end + --Try sucking the inventory (in case it's a chest) + self:itemSuck(nodeLocation) + local drops = minetest.get_node_drops(node) + --TODO NOTE This violates spawn protection, but I know of no way to mine that abides by spawn protection AND picks up all items and contents (dig_node drops items and I don't know how to pick them up) + minetest.remove_node(nodeLocation) + for _, iteminfo in pairs(drops) do + local stack = ItemStack(iteminfo) + if self.inv:room_for_item("main",stack) then + self.inv:add_item("main",stack) + end + end + self:yield("Mining",true) + return true +end +function TurtleEntity:build(nodeLocation, turtleslot) + if nodeLocation == nil then return false end + if not isValidInventoryIndex(turtleslot) then return false end + + local node = minetest.get_node(nodeLocation) + if node.name~="air" then return false end + + --Build and consume item + local stack = self:getTurtleslot("main", turtleslot) + if stack:is_empty() then return false end + local newstack, position_placed = minetest.item_place_node(stack, nil, { type="node", under=nodeLocation, above=self:get_pos()}) + self.inv:set_stack("main", turtleslot,newstack) + + if position_placed == nil then + self:yield("Building") + return false + end + + self:yield("Building",true) + return true +end +function TurtleEntity:scan(nodeLocation) return minetest.get_node(nodeLocation) end + +--[[ Sucks inventory (chest, node, furnace, etc) at nodeLocation into turtle +@returns true if it sucked everything up]] +--function TurtleEntity:suckBlock(nodeLocation) +-- local suckedEverything = true +-- local nodeInventory = minetest.get_inventory({type="node", pos=nodeLocation}) +-- if not nodeInventory then +-- return true --No node inventory, nothing left to suck +-- end +-- for listname,listStacks in pairs(nodeInventory:get_lists()) do +-- for stackI,itemStack in pairs(listStacks) do +-- if self.inv:room_for_item("main",itemStack) then +-- local remainingItemStack = self.inv:add_item("main",itemStack) +-- nodeInventory:set_stack(listname, stackI, remainingItemStack) +-- else +-- suckedEverything = false +-- end +-- end +-- end +-- return suckedEverything +--end + +---Takes everything from block that matches list +--- @param filterlist - Something like {"default:stone","default:dirt"} +--- @param isWhitelist - If true, only take things in list. +--- If false, take everything EXCEPT the items in the list +--- @param listname - take only from specific listname. If nil, take from every list +--- @return boolean true unless any items can't fit +function TurtleEntity:itemSuck(nodeLocation, filterlist, isWhitelist, listname) + filterlist = filterlist or {} + local suckedEverything = true + local nodeInventory = minetest.get_inventory({type="node", pos=nodeLocation}) + if not nodeInventory then + return true --No node inventory, nothing left to suck + end + + local function suckList(listname, listStacks) + local function intable(item) + for _,x in pairs(filterlist) do + if item==x then return true end + end return false end - local function sandbox(code) - --TODO sandbox this! - --Currently returns function that defines init and loop. In the future, this should probably just initialize it using some callbacks - if (code =="") then return nil end - return loadstring(code) - end - turtle.codeUncompiled = code_string - turtle.coroutine = nil - turtle.code = sandbox(turtle.codeUncompiled) - if (run_for_result) then - --TODO run subroutine once, if it returns a value, return that here - return "Ran" - end - return turtle.code ~= nil - end, - --MAIN END TURTLE USER INTERFACE------------------------------------------ - --- From 0 to 3 - set_heading = function(turtle,heading) - heading = (tonumber(heading) or 0)%4 - if turtle.heading ~= heading then - turtle.heading = heading - turtle.object:set_yaw(turtle.heading * 3.14159265358979323/2) - if (coroutine.running() == turtle.coroutine) then turtle:yield("Turning",true) end - end - end, - get_heading = function(self) - return self.heading - end, - on_activate = function(turtle, staticdata, dtime_s) - --TODO use staticdata to load previous state, such as inventory and whatnot - --Give ID - computertest.num_turtles = computertest.num_turtles+1 - turtle.id = computertest.num_turtles - turtle.heading = 0 - turtle.previous_answers = {} - turtle.coroutine = nil - turtle.fuel = 100 - --Give her an inventory - turtle.inv_name = "computertest:turtle:".. turtle.id - turtle.inv_fullname = "detached:".. turtle.inv_name - local inv = minetest.create_detached_inventory(turtle.inv_name,{}) - if inv == nil or inv == false then error("Could not spawn inventory")end - inv:set_size("main", TURTLE_INVENTORYSIZE) - if turtle.inv ~= nil then inv.set_lists(turtle.inv) end - turtle.inv = inv - -- Add to turtle list - computertest.turtles[turtle.id] = turtle - end, - on_rightclick = function(self, clicker) - if not clicker or not clicker:is_player() then return end - if (not has_computertest_priv(clicker)) then - minetest.show_formspec(clicker:get_player_name(),FORMNAME_TURTLE_NOPRIV,FORM_NOPRIV); - return + for stackI,itemStack in pairs(listStacks) do + if intable(itemStack:get_name()) == isWhitelist then + local remainingItemStack = self.inv:add_item("main",itemStack) + nodeInventory:set_stack(listname, stackI, remainingItemStack) + suckedEverything = suckedEverything and remainingItemStack:is_empty() + end end + end + + if listname then + suckList(listname, nodeInventory:get_list(listname)) + else + for listname,listStacks in pairs(nodeInventory:get_lists()) do + suckList(listname, listStacks) + end + end + return suckedEverything +end + +function TurtleEntity:itemSuckForward(filterlist, isWhitelist, listname) return self:itemSuck(self:getLocForward(), filterlist, isWhitelist, listname) end + +---Takes everything from block that matches list +--- @param filterlist - Something like {"default:stone","default:dirt"} +--- @param isWhitelist - If true, only take things in list. +--- If false, take everything EXCEPT the items in the list +--- @param listname - take only from specific listname. If nil, take from every list +--- @return boolean true if stack completely pushed +function TurtleEntity:itemPush(nodeLocation, turtleslot, filterlist, isWhitelist, listname) + listname = listname or "main" + + local nodeInventory = minetest.get_inventory({type="node", pos=nodeLocation})--InvRef + if not nodeInventory then + return false --No node inventory (Ex: Not a chest) + end + + --Try putting my stack somewhere + local toPush = self:getTurtleslot(turtleslot) + local remainingItemStack = nodeInventory:add_item(listname,toPush) + self:setTurtleslot(turtleslot, remainingItemStack) + + return remainingItemStack:is_empty() +end +--- +---@returns true on success +--- +function TurtleEntity:upload_code_to_turtle(player, code_string,run_for_result) + --Check permissions + if (not has_computertest_priv(player)) then + --minetest.debug(player:get_player_name().." does not have computertest priv") + return false + end + local function sandbox(code) + --TODO sandbox this! + --Currently returns function that defines init and loop. In the future, this should probably just initialize it using some callbacks + if (code =="") then return nil end + return loadstring(code) + end + self.codeUncompiled = code_string + self.coroutine = nil + self.code = sandbox(self.codeUncompiled) + if (run_for_result) then + --TODO run subroutine once, if it returns a value, return that here + return "Ran" + end + return self.code ~= nil +end +--MAIN TURTLE USER INTERFACE------------------------------------------ +function TurtleEntity:get_formspec_inventory() + return "size[12,5;]" + .."button[0,0;2,1;open_terminal;Open Terminal]" + .."button[2,0;2,1;upload_code;Upload Code]" + .."button[4,0;2,1;factory_reset;Factory Reset]" + .."set_focus[open_terminal;true]" + .."list[".. self.inv_fullname..";main;8,1;4,4;]" + .."background[8,1;1,1;computertest_inventory.png]" + .."list[current_player;main;0,1;8,4;]"; +end +function TurtleEntity:get_formspec_terminal() + local previous_answers = self.previous_answers + local parsed_output = ""; + for i=1, #previous_answers do + parsed_output = parsed_output .. minetest.formspec_escape(previous_answers[i]).."," + end + return + "size[12,9;]" + .."field_close_on_enter[terminal_in;false]" + .."field[0,0;12,1;terminal_in;;"..minetest.formspec_escape(self.lastCommandRan or "").."]" + .."set_focus[terminal_in;true]" + .."textlist[0,1;12,8;terminal_out;"..parsed_output.."]"; +end +function TurtleEntity:get_formspec_upload() + --TODO could indicate if code is already uploaded + return + "size[12,9;]" + .."button[0,0;3,1;button_upload;Upload Code to '"..self.name.."']" + .."field_close_on_enter[upload;false]" + .."textarea[0,1;12,8;upload;;"..minetest.formspec_escape(self.codeUncompiled or "").."]" + .."set_focus[upload;true]"; +end +--MAIN TURTLE ENTITY FUNCTIONS------------------------------------------ +function TurtleEntity:on_activate(staticdata, dtime_s) + --TODO use staticdata to load previous state, such as inventory and whatnot + --Give ID + computertest.num_turtles = computertest.num_turtles+1 + self.id = computertest.num_turtles + self.name = "Unnamed #"..self.id + --self.owner = minetest.get_meta(pos):get_string("owner") + self.heading = 0 + self.previous_answers = {} + self.coroutine = nil + self.fuel = computertest.config.fuel_initial + --Give her an inventory + self.inv_name = "computertest:turtle:".. self.id + self.inv_fullname = "detached:".. self.inv_name + local inv = minetest.create_detached_inventory(self.inv_name,{}) + if inv == nil or inv == false then error("Could not spawn inventory")end + inv:set_size("main", TURTLE_INVENTORYSIZE) + if self.inv ~= nil then inv.set_lists(self.inv) end + self.inv = inv + -- Add to turtle list + computertest.turtles[self.id] = self +end +function TurtleEntity:on_rightclick(clicker) + if not clicker or not clicker:is_player() then + return + end + if (not has_computertest_priv(clicker))then + minetest.show_formspec(clicker:get_player_name(),FORMNAME_TURTLE_NOPRIV,FORM_NOPRIV); + return + end + minetest.show_formspec(clicker:get_player_name(), FORMNAME_TURTLE_INVENTORY.. self.id, + self:get_formspec_inventory()) +end +function TurtleEntity:get_staticdata() + minetest.debug("Deleting/Forgetting all data of turtle "..self.name) + local static = {} - minetest.show_formspec(clicker:get_player_name(), FORMNAME_TURTLE_INVENTORY.. self.id, - self:get_formspec_inventory()) - end, - get_staticdata = function(self) -- TODO convert inventory and internal code to string and back somehow, or else it'll be deleted every time the entity gets unloaded - minetest.debug("Deleting all data of turtle") - end, - turtle_move_withHeading = function (turtle,numForward,numRight,numUp) - local new_pos = turtle:get_nearby_pos(numForward,numRight,numUp) - --Verify new pos is empty - if (new_pos == nil or minetest.get_node(new_pos).name~="air") then - turtle:yield("Moving",true) - return false - end - --Take Action - turtle.object:set_pos(new_pos) - turtle:yield("Moving",true) - return true - end, - get_nearby_pos = function(turtle, numForward, numRight, numUp) - local pos = turtle:get_pos() - if pos==nil then return nil end -- To prevent unloaded turtles from trying to load things - local new_pos = vector.new(pos) - if turtle:get_heading()%4==0 then new_pos.z=pos.z-numForward;new_pos.x=pos.x-numRight; end - if turtle:get_heading()%4==1 then new_pos.x=pos.x+numForward;new_pos.z=pos.z-numRight; end - if turtle:get_heading()%4==2 then new_pos.z=pos.z+numForward;new_pos.x=pos.x+numRight; end - if turtle:get_heading()%4==3 then new_pos.x=pos.x-numForward;new_pos.z=pos.z+numRight; end - new_pos.y = pos.y + (numUp or 0) - return new_pos - end, - mine = function(turtle, nodeLocation) - if nodeLocation == nil then return false end - local node = minetest.get_node(nodeLocation) - if (node.name=="air") then return false end - --Try sucking the inventory (in case it's a chest) - turtle:suckBlock(nodeLocation) - local drops = minetest.get_node_drops(node) - --NOTE This violates spawn protection, but I know of no way to mine that abides by spawn protection AND picks up all items and contents (dig_node drops items and I don't know how to pick them up) - minetest.remove_node(nodeLocation) - for _, iteminfo in pairs(drops) do - local stack = ItemStack(iteminfo) - if turtle.inv:room_for_item("main",stack) then - turtle.inv:add_item("main",stack) - end - end - turtle:yield("Mining",true) - return true - end, - useFuel = function(turtle) - if turtle.fuel > 0 then - turtle.fuel = turtle.fuel - 1; - end - end, --- MAIN TURTLE INTERFACE --------------------------------------- - yield = function(turtle,reason,useFuel) - -- Yield at least once - if (coroutine.running() == turtle.coroutine) then - coroutine.yield(reason) - end - --Use a fuel if requested - if useFuel then turtle:useFuel() end - end, + --static = self -- Would this work..? no. - moveForward = function(turtle) return turtle:turtle_move_withHeading( 1, 0, 0) end, - moveBackward = function(turtle) return turtle:turtle_move_withHeading(-1, 0, 0) end, - moveRight = function(turtle) return turtle:turtle_move_withHeading( 0, 1, 0) end, - moveLeft = function(turtle) return turtle:turtle_move_withHeading( 0,-1, 0) end, - moveUp = function(turtle) return turtle:turtle_move_withHeading( 0, 0, 1) end, - moveDown = function(turtle) return turtle:turtle_move_withHeading( 0, 0,-1) end, - turnLeft = function(turtle) return turtle:set_heading(turtle:get_heading()+1) end, - turnRight = function(turtle) return turtle:set_heading(turtle:get_heading()-1) end, + static.complete = true + return minetest.serialize(static) +end +--MAIN PLAYER INTERFACE (CALL THESE)------------------------------------------ +function TurtleEntity:getLoc() return self.object:get_pos() end +function TurtleEntity:getLocRelative(numForward,numUp,numRight) + local pos = self:getLoc() + if pos==nil then + return nil -- To prevent unloaded turtles from trying to load things + end + local new_pos = vector.new(pos) + if self:get_heading()%4==0 then new_pos.z=pos.z-numForward;new_pos.x=pos.x-numRight; end + if self:get_heading()%4==1 then new_pos.x=pos.x+numForward;new_pos.z=pos.z-numRight; end + if self:get_heading()%4==2 then new_pos.z=pos.z+numForward;new_pos.x=pos.x+numRight; end + if self:get_heading()%4==3 then new_pos.x=pos.x-numForward;new_pos.z=pos.z+numRight; end + new_pos.y = pos.y + (numUp or 0) + return new_pos +end +function TurtleEntity:getLocForward() return self:getLocRelative( 1,0,0) end +function TurtleEntity:getLocBackward() return self:getLocRelative(-1,0,0) end +function TurtleEntity:getLocUp() return self:getLocRelative(0, 1,0) end +function TurtleEntity:getLocDown() return self:getLocRelative(0,-1,0) end +function TurtleEntity:getLocRight() return self:getLocRelative(0,0, 1) end +function TurtleEntity:getLocLeft() return self:getLocRelative(0,0,-1) end +---Consumes a fuel point +function TurtleEntity:useFuel() + if self.fuel > 0 then + self.fuel = self.fuel - 1; + end +end +--- From 0 to 3 +function TurtleEntity:set_heading(heading) + heading = (tonumber(heading) or 0)%4 + if self.heading ~= heading then + self.heading = heading + self.object:set_yaw(self.heading * 3.14159265358979323/2) + if (coroutine.running() == self.coroutine) then self:yield("Turning",true) end + end +end +function TurtleEntity:get_heading() return self.heading end +function TurtleEntity:turnLeft() return self:set_heading(self:get_heading()+1) end +function TurtleEntity:turnRight() return self:set_heading(self:get_heading()-1) end - mineForward = function(turtle) return turtle:mine(turtle:get_nearby_pos (1,0,0)) end, - mineBackward = function(turtle) return turtle:mine(turtle:get_nearby_pos (-1,0,0)) end, - mineRight = function(turtle) return turtle:mine(turtle:get_nearby_pos (0,1,0)) end, - mineLeft = function(turtle) return turtle:mine(turtle:get_nearby_pos (0,-1,0)) end, - mineUp = function(turtle) return turtle:mine(turtle:get_nearby_pos (0,0,1)) end, - mineDown = function(turtle) return turtle:mine(turtle:get_nearby_pos (0,0,-1)) end, +function TurtleEntity:moveForward() return self:move(self:getLocForward()) end +function TurtleEntity:moveBackward() return self:move(self:getLocBackward()) end +function TurtleEntity:moveUp() return self:move(self:getLocUp()) end +function TurtleEntity:moveDown() return self:move(self:getLocDown()) end +function TurtleEntity:moveRight() return self:move(self:getLocRight()) end +function TurtleEntity:moveLeft() return self:move(self:getLocLeft()) end - get_pos = function(turtle) return turtle.object:get_pos() end, - get_fuel = function(turtle) return turtle.fuel end, +function TurtleEntity:mineForward() return self:mine(self:getLocForward()) end +function TurtleEntity:mineBackward() return self:mine(self:getLocBackward()) end +function TurtleEntity:mineUp() return self:mine(self:getLocUp()) end +function TurtleEntity:mineDown() return self:mine(self:getLocDown()) end +function TurtleEntity:mineRight() return self:mine(self:getLocRight()) end +function TurtleEntity:mineLeft() return self:mine(self:getLocLeft()) end - --[[ Sucks inventory (chest, node, furnace, etc) at nodeLocation into turtle - @returns true if it sucked everything up]] - suckBlock = function(turtle, nodeLocation) - local suckedEverything = true - local nodeInventory = minetest.get_inventory({type="node", pos=nodeLocation}) - if not nodeInventory then - return false --No node inventory - end - for listName,listStacks in pairs(nodeInventory:get_lists()) do - for stackI,itemStack in pairs(listStacks) do - if turtle.inv:room_for_item("main",itemStack) then - local remainingItemStack = turtle.inv:add_item("main",itemStack) - nodeInventory:set_stack(listName, stackI, remainingItemStack) - else - suckedEverything = false - end - end - end - return suckedEverything - end, +function TurtleEntity:buildForward(turtleslot) return self:build(self:getLocForward(),turtleslot) end +function TurtleEntity:buildBackward(turtleslot) return self:build(self:getLocBackward(),turtleslot) end +function TurtleEntity:buildUp(turtleslot) return self:build(self:getLocUp(),turtleslot) end +function TurtleEntity:buildDown(turtleslot) return self:build(self:getLocDown(),turtleslot) end +function TurtleEntity:buildRight(turtleslot) return self:build(self:getLocRight(),turtleslot) end +function TurtleEntity:buildLeft(turtleslot) return self:build(self:getLocLeft(),turtleslot) end - -- MAIN INVENTORY COMMANDS-------------------------- - --- TODO drops item onto ground - itemDrop = function(turtle,itemslot) - end, - --- TODO Returns ItemStack on success or nil on failure - ---Ex: turtle:itemGet(3):get_name() -> "default:stone" - itemGet = function(turtle,itemslot) - if isValidInventoryIndex(itemslot) then - return turtle.inv:get_stack("main",itemslot) - end - return nil - end, - --- Swaps itemstacks in slots A and B - itemMove = function(turtle, itemslotA, itemslotB) - if (not isValidInventoryIndex(itemslotA)) or (not isValidInventoryIndex(itemslotB)) then - turtle:yield("Inventorying") - return false - end +---Scan gets the info of this node +function TurtleEntity:scanForward() return self:scan(self:getLocForward()) end +function TurtleEntity:scanBackward() return self:scan(self:getLocBackward()) end +function TurtleEntity:scanUp() return self:scan(self:getLocUp()) end +function TurtleEntity:scanDown() return self:scan(self:getLocDown()) end +function TurtleEntity:scanRight() return self:scan(self:getLocRight()) end +function TurtleEntity:scanLeft() return self:scan(self:getLocLeft()) end - local stackA = turtle.inv:get_stack("main",itemslotA) - local stackB = turtle.inv:get_stack("main",itemslotB) +function TurtleEntity:yield(reason,useFuel) + -- Yield at least once + if (coroutine.running() == self.coroutine) then + coroutine.yield(reason) + end + --Use a fuel if requested + if useFuel then self:useFuel() end +end - minetest.debug(dump(stackA:to_string())) - minetest.debug(dump(stackB:to_string())) +--Inventory Interface +-- MAIN INVENTORY COMMANDS-------------------------- +--- TODO drops item onto ground +function TurtleEntity:itemDrop(itemslot) - turtle.inv:set_stack("main",itemslotA,stackB) - turtle.inv:set_stack("main",itemslotB,stackA) +end +--- TODO Returns ItemStack on success or nil on failure +---Ex: turtle:itemGet(3):get_name() -> "default:stone" +function TurtleEntity:itemGet(turtleslot) + return self:getTurtleslot(turtleslot) +end +--- Swaps itemstacks in slots A and B +function TurtleEntity:itemMove(turtleslotA, turtleslotB) + if (not isValidInventoryIndex(turtleslotA)) or (not isValidInventoryIndex(turtleslotB)) then + self:yield("Inventorying") + return false + end - turtle:yield("Inventorying") - return true - end, - --- TODO Pushes item into forward-facing chest - --- TODO after getting this working, add a general function with whitelists - itemPush = function(turtle, itemslot, listname) - listname = listname or "main" - end, - --- TODO craft using top right 3x3 grid, and put result in itemslotResult - itemCraft = function(turtle,itemslotResult) - end, - itemRefuel = function(turtle,itemslot) - local burntime = 0--TODO get burntime --- If fuels are defined like this, how do I get the burntime back, given an item? ---[[ minetest.register_craft("",{ - type = "fuel", - recipe = "bucket:bucket_lava", - burntime = 60, - replacements = {{"bucket:bucket_lava", "bucket:bucket_empty"}}, - })]] + local stackA = self:getTurtleslot(turtleslotA) + local stackB = self:getTurtleslot(turtleslotB) - turtle.fuel = turtle.fuel + burntime * computertest.config.fuel_multiplier - end, - -- MAIN TURTLE INTERFACE END--------------------------------------- -}) + self:setTurtleslot(turtleslotA,stackB) + self:setTurtleslot(turtleslotB,stackA) + + self:yield("Inventorying") + return true +end + +function TurtleEntity:itemPushForward(turtleslot, listname) return self:itemPush(turtleslot, self:getLocForward(), listname) end +function TurtleEntity:itemPushBackward(turtleslot, listname) return self:itemPush(turtleslot, self:getLocBackward(), listname) end +function TurtleEntity:itemPushUp(turtleslot, listname) return self:itemPush(turtleslot, self:getLocUp(), listname) end +function TurtleEntity:itemPushDown(turtleslot, listname) return self:itemPush(turtleslot, self:getLocDown(), listname) end +function TurtleEntity:itemPushRight(turtleslot, listname) return self:itemPush(turtleslot, self:getLocRight(), listname) end +function TurtleEntity:itemPushLeft(turtleslot, listname) return self:itemPush(turtleslot, self:getLocLeft(), listname) end + +--- TODO craft using top right 3x3 grid, and put result in itemslotResult +function TurtleEntity:itemCraft(itemslotResult) +-- Use minetest.get_craft_result +end +--- @returns True if fuel was consumed. False if itemslot did not have fuel. +function TurtleEntity:itemRefuel(turtleslot) + if (not isValidInventoryIndex(turtleslot)) then + return false + end + + local stack = self:getTurtleslot(turtleslot) + + local burntime = 0 + local quantity = stack:get_count() + + --TODO how do you get burntime? + --minetest.debug(dump(minetest.get_all_craft_recipes("default:coal_lump"))) + if (stack:get_name() == "default:coal_lump") then burntime = 40 end + + if (burntime <= 0) then + return false + end + + self:setTurtleslot(turtleslot,nil) + self.fuel = self.fuel + quantity * burntime * computertest.config.fuel_multiplier + self:yield("Fueling") + return true +end + +function TurtleEntity:getFuel() return self.fuel end +function TurtleEntity:setName(name) self.name = minetest.formspec_escape(name) end + +function TurtleEntity:debug(string) + if computertest.config.debug then + minetest.debug("computertest turtle #"..self.id..": "..string) + end +end +function TurtleEntity:dump(object) return dump(object) end + +-- MAIN TURTLE INTERFACE END--------------------------------------- +-- MAIN TURTLE INTERFACE END--------------------------------------- +-- MAIN TURTLE INTERFACE END--------------------------------------- + +minetest.register_entity("computertest:turtle", TurtleEntity) \ No newline at end of file diff --git a/examples/0-api.lua b/examples/0-api.lua new file mode 100644 index 0000000..891105e --- /dev/null +++ b/examples/0-api.lua @@ -0,0 +1,53 @@ + +-- Generally speaking, "do action" functions return true on success and false on failure +-- See example #1 for information on how to run code + +function init(turtle) + ---set_name can be used in identification + turtle:set_name("Mining Turtle") + + ---Move commands move the turtle relative to its heading, and take one "turtle_tick" + ---They return true if the turtle successfully moved + turtle:moveForward() + turtle:moveBackward() + turtle:moveRight() + turtle:moveLeft() + turtle:moveUp() + turtle:moveDown() + ---Turn commands turn the turtle 90 degrees, and take one "turtle_tick" + turtle:turnLeft() + turtle:turnRight() + ---Mine commands break and take the items of the block, take one "turtle_tick", and use fuel + ---They return true if the block was removed + turtle:mineForward() + turtle:mineBackward() + turtle:mineRight() + turtle:mineLeft() + turtle:mineUp() + turtle:mineDown() + + ---Informational commands happen instantly (don't yield) + turtle:get_pos()--Returns something like {x = -277,y = 6,z = -1558} + ---More turtle commands can be viewed in entity/turtle.lua (only use the ones in the interface) + + ---For inventory commands, remember that lua starts from one (itemslots go from 1-16) + turtle:itemMove(1,2) -- Swap item in slot 1 with item in slot two + turtle:itemRefuel(5) -- Takes fuel from slot five. The turtle needs fuel to do actions + + --- Build commands take an Itemstack and place it as a Node. Give them an inventory itemnumber (1-16) + --- Build commands yield and use fuel + turtle:buildForward(1) -- Places the block in slot one infront of it + turtle:buildBackward(1) + turtle:buildLeft(5) -- Places the block in slot five (under slot one) to the left of it + turtle:buildRight(1) + turtle:buildUp(1) + turtle:buildDown(1) + + + ---IMPORTANT: + ---Yield waits one "turtle_tick" (the time this takes can be changed in the config). It does not use fuel. + ---You MUST yield in anything that could be an infinite loop, or else your turtle will deadlock and die + --- Infinite loops, such as "while true do end", MUST be replaced with "while true do turtle:yield() end" + --- or else the server thread will freeze (This is VERY bad, the server would need restarting) + turtle:yield(reason_string) -- Reason string can be something like "Ran out of Fuel" +end \ No newline at end of file diff --git a/examples/1-spin.lua b/examples/1-spin.lua index 6eab797..ee393d6 100644 --- a/examples/1-spin.lua +++ b/examples/1-spin.lua @@ -1,17 +1,11 @@ --This simple example will make the turtle spin clockwise forever (or until it hits an obstacle) ---1. Install the ComputerTest mod ---2. Get a turtle block (craftable from one dirt) ---3. Place the block ---4. Click "Upload Code" ---5. Paste this into the large field (sometimes Minetest requires you to paste twice) and click "Upload Code" ---6. Watch as it digs a hole! ---The turtle calls init(turtle) on startup ----@param turtle self This is the turtle, and has many functions (see example 3 for more) +---@param turtle self This is the turtle, and has many functions (see the API example for more) function init(turtle) while true do - --All actions (moving, turning, mining, etc) take one "turtle_tick" - -- to run to prevent destruction and increase realism + --Many actions (moving, turning, mining, etc) take one "turtle_tick" + -- to run to prevent destruction and increase realism. They also use fuel. turtle:moveForward() turtle:turnRight() end diff --git a/examples/2-quarry-answers.lua b/examples/2-quarry-answers.lua new file mode 100644 index 0000000..67b6281 --- /dev/null +++ b/examples/2-quarry-answers.lua @@ -0,0 +1,66 @@ +--WARNING this is the answers to example #2, look at example #2 first! +--WARNING this is the answers to example #2, look at example #2 first! +--WARNING this is the answers to example #2, look at example #2 first! +--WARNING this is the answers to example #2, look at example #2 first! +--WARNING this is the answers to example #2, look at example #2 first! + +--One solution is to do the same thing, just with more loops +local function quarry_with_loops(turtle, size) + local x,y,z = 0,0,0 + while true do + while true do + while x0 do + if turtle:moveBackward() then x=x-1 end + end + if z==size then break end + turtle:mineRight() + if turtle:moveRight() then z=z+1 end + end + while z>0 do + if turtle:moveLeft() then z=z-1 end + end + if y==size then break end + turtle:mineDown() + if turtle:moveDown() then y=y+1 end + end + while y>0 do + if turtle:moveUp() then y=y-1 end + end +end + +local function quarry_with_lambdas(turtle, size) + local function quarryLine(moveIn,moveOut,lineEnd) + for _ = 1, size-1 do moveIn() end + lineEnd() + for _ = 1, size-1 do moveOut() end + end + local quarryX = (function() + quarryLine( + (function() turtle:mineForward() turtle:moveForward() end), + (function()turtle:moveBackward()end), + (function()end)) + end) + local quarryY = (function() + quarryLine( + (function() quarryX()turtle:mineRight();turtle:moveRight();end), + (function()turtle:moveLeft()end), + (function()quarryX()end)) + end) + local quarryZ = (function() + quarryLine( + (function() quarryY() turtle:mineDown() turtle:moveDown() end), + (function() turtle:moveUp() end), + (function()quarryY()end)) + end ) + quarryZ() +end +function init(turtle) + quarry_with_lambdas(turtle,3) + turtle:turnRight() + turtle:turnRight() + quarry_with_loops(turtle,3) +end \ No newline at end of file diff --git a/examples/2-quarry.lua b/examples/2-quarry.lua index df318f1..066f40f 100644 --- a/examples/2-quarry.lua +++ b/examples/2-quarry.lua @@ -3,29 +3,21 @@ --See example #1 on how to upload code function init(turtle) - local size = 3 - local y=0 - while y0 do - if turtle:moveBackward() then x=x-1 end - end - turtle:mineRight() - if turtle:moveRight() then z = z +1 end + local size = 5 + local z =0 + while z 0 do - if turtle:moveLeft() then z = z -1 end + while x>0 do + if turtle:moveBackward() then x=x-1 end end - turtle:mineDown() - if turtle:moveDown() then y=y+1 end + turtle:mineRight() + if turtle:moveRight() then z = z +1 end end - while y>0 do - if turtle:moveUp() then y=y-1 end + while z >0 do + if turtle:moveLeft() then z = z -1 end end end \ No newline at end of file diff --git a/examples/3-api.lua b/examples/3-api.lua deleted file mode 100644 index f0a7568..0000000 --- a/examples/3-api.lua +++ /dev/null @@ -1,31 +0,0 @@ - --- Generally speaking, "do action" functions return true on success and false on failure - -function init(turtle) - ---Yield waits one "turtle_tick" - turtle:yield() - ---Move commands move the turtle relative to its heading, and take one "turtle_tick" - ---They return true if the turtle successfully moved - turtle:moveForward() - turtle:moveBackward() - turtle:moveRight() - turtle:moveLeft() - turtle:moveUp() - turtle:moveDown() - ---Turn commands turn the turtle 90 degrees, and take one "turtle_tick" - turtle:turnLeft() - turtle:turnRight() - ---Mine commands break and take the items of the block, and take one "turtle_tick" - ---They return true if the block was removed - turtle:mineForward() - turtle:mineBackward() - turtle:mineRight() - turtle:mineLeft() - turtle:mineUp() - turtle:mineDown() - ---Informational commands happen instantly (don't yield) - turtle:get_pos()--Returns something like {x = -277,y = 6,z = -1558} - ---More turtle commands can be viewed in entity/turtle.lua (only use the ones in the interface) - - ---For inventory commands, remember that lua starts from one (itemslots go from 1-16) -end \ No newline at end of file diff --git a/examples/4-items.lua b/examples/4-items.lua index 3c26688..77ff18c 100644 --- a/examples/4-items.lua +++ b/examples/4-items.lua @@ -1,5 +1,20 @@ +--If you're low on fuel, grab fuel from somewhere +local function checkFuel(turtle) + while turtle:getFuel() < 10 do + for i = 1, 16 do + turtle:itemRefuel(i) + end + --If you're completely out of fuel, then wait + --(If you don't do this, the turtle will deadlock all turtles) + if turtle:getFuel() < 10 then turtle:yield("No fuel") end + end +end + function init(turtle) while true do + checkFuel(turtle) turtle:itemMove(1,2) + turtle:turnRight() + turtle:buildForward(4) end end \ No newline at end of file diff --git a/examples/5-build_cube_with_lambdas.lua b/examples/5-build_cube_with_lambdas.lua new file mode 100644 index 0000000..70d57b9 --- /dev/null +++ b/examples/5-build_cube_with_lambdas.lua @@ -0,0 +1,29 @@ +function buildcube(turtle, size) + local function buildLine(moveIn,moveOut,each) + for _ = 1, size-1 do moveIn() end + for _ = size-1,1,-1 do + each() + moveOut() + end + each() + end + local buildX = (function() + buildLine((function()turtle:moveForward()end), + (function()turtle:moveBackward()end), + (function()turtle:buildForward(1)end)) + end) + local buildY = (function() + buildLine((function()turtle:moveRight()end), + (function()turtle:moveLeft()end), + (function()buildX()end)) + end) + local buildZ = (function() + buildLine((function()turtle:moveUp()end), + (function()turtle:moveDown()end), + (function()buildY()end)) + end) + buildZ() +end +function init(turtle) + buildcube(turtle,3) +end \ No newline at end of file diff --git a/examples/6-build-sphere.lua b/examples/6-build-sphere.lua new file mode 100644 index 0000000..7b9ca87 --- /dev/null +++ b/examples/6-build-sphere.lua @@ -0,0 +1,51 @@ +local function buildAny(turtle) + local ret = false + for i = 1, 16 do + ret = ret or turtle:buildForward(i) + end + return ret +end +---Builds a sphere centered on the turtle +local function buildSphere(turtle, radius) + local x,y,z = 0,0,0 + local function shouldBuildSphere(x,y,z) return x*x + y*y + z*z <= radius * radius end + local function moreLeftToBuild() + for x2=x,radius do + if shouldBuildSphere(x2,y,z,radius) then return true end + end + return false + end + turtle:moveBackward() + for _=1,radius do + turtle:moveLeft();z=z-1 + turtle:moveBackward();x=x-1 + turtle:moveDown();y=y-1 + end + while true do + while true do + while x<=radius and moreLeftToBuild() do + if turtle:moveForward() then x=x+1 end + end + while x>-radius do + if turtle:moveBackward() then x=x-1 end + if shouldBuildSphere(x,y,z) then + while not buildAny(turtle) do + turtle:yield("NO BLOCKS") + end + end + end + if z==radius then break end + if turtle:moveRight() then z=z+1 end + end + if y==radius then break end + while z>-radius do + if turtle:moveLeft() then z=z-1 end + end + if turtle:moveUp() then y=y+1 end + end +end + +function init(turtle) + for _=1,21 do turtle:moveUp() end + buildSphere(turtle,20) +end \ No newline at end of file diff --git a/examples/7-lumber.lua b/examples/7-lumber.lua new file mode 100644 index 0000000..5ed7dba --- /dev/null +++ b/examples/7-lumber.lua @@ -0,0 +1,77 @@ +--- Mines an entire forest + +local x,y,z = 0,0,0 +local turtle = nil + +function dumpItems() + local sx,sy,sz = x,y,z -- save + moveTo(0,0,0) + turtle:turnLeft() + turtle:turnLeft() + for i = 1, 16 do turtle:itemPushForward(i) end + turtle:turnLeft() + turtle:turnLeft() + moveTo(sx,sy,sz) +end + +function moveTo(tox,toy,toz) + while ytox do turtle:mineBackward() ; turtle:moveBackward() ; x=x-1 end + while xtoz do turtle:mineLeft() ; turtle:moveLeft() ; z=z-1 end + while ztoy do turtle:mineDown() ; turtle:moveDown() ; y=y-1 end + x=tox; y=toy; z=toz +end + +function scan() + local scanlist = {} + table.insert(scanlist,{0,0,0}) + local function processScan(point,name) + local names = {'default:tree', 'default:leaves', + 'default:jungletree','default:jungleleaves', + 'default:pine_tree','default:pine_needles', + 'default:acacia_tree','default:acacia_leaves', + 'default:aspen_tree','default:aspen_leaves'} + local function has(array,value) + for k,v in pairs(array) do + if value==v then + return true + end + end + return false + end + + if has(names,name) then + turtle:debug("name: "..turtle:dump(name)) + table.insert(scanlist,point) + end + end + local function mineHere() + local point = table.remove(scanlist,#scanlist) + moveTo(point[1],point[2],point[3]) + + processScan({point[1]-1,point[2],point[3]},turtle:scanBackward().name) + processScan({point[1]+1,point[2],point[3]},turtle:scanForward() .name) + + processScan({point[1],point[2]-1,point[3]},turtle:scanDown() .name) + processScan({point[1],point[2]+1,point[3]},turtle:scanUp() .name) + + processScan({point[1],point[2],point[3]-1},turtle:scanLeft() .name) + processScan({point[1],point[2],point[3]+1},turtle:scanRight() .name) + + table.sort(scanlist,(function(a,b) return a[1] < b[1] end)) + end + while next(scanlist) do + mineHere() + end + moveTo(0,0,0) +end + +function init(turtleL) + turtle = turtleL + while true do + scan() + turtle:yield("Done Scanning") + end +end \ No newline at end of file diff --git a/examples/8-helper-functions.lua b/examples/8-helper-functions.lua new file mode 100644 index 0000000..561aab2 --- /dev/null +++ b/examples/8-helper-functions.lua @@ -0,0 +1,29 @@ +---Dumps everything from the turtle forward if it's in the list +--- @param list - Something like {"default:stone","default:dirt"} +--- @param isWhitelist - If true, only dump things in list. +--- If false, dump everything EXCEPT the items in the list +--- @param listname @see itemPush +--- @return boolean true unless any items can't fit +local function itemPushForwardFiltered(turtle, list, isWhitelist, listname) + local success = true + local function intable(item) + for _,x in pairs(list) do + if item==x then return true end + end + return false + end + for turtleslot = 1, 16 do + if intable(turtle:itemGet(turtleslot):get_name()) == isWhitelist then + success = success and turtle:itemPushForward(turtleslot,listname) + end + end + return success +end +function init(turtle) + while true do + itemPushForwardFiltered(turtle,{"default:cobble","default:sand"}, true, "src") + itemPushForwardFiltered(turtle,{"default:coal_lump"}, true, "fuel") + turtle:itemSuckForward({"default:stone","default:glass"},true,"dst") + turtle:yield("Waiting") + end +end \ No newline at end of file diff --git a/examples/EXAMPLES.md b/examples/EXAMPLES.md new file mode 100644 index 0000000..fae188a --- /dev/null +++ b/examples/EXAMPLES.md @@ -0,0 +1,13 @@ +# Examples +See README.md for how to run examples. + +# 0-api.lua + +The 0-api.lua contains all of the things a turtle can do! + +# 2-quarry.lua + +This contains a practice on learning how to write turtle code. +See 2-quarry-answers.lua when you're done! + +# 7-lumber.lua will walk down trees and mine them \ No newline at end of file diff --git a/init.lua b/init.lua index fce34d0..9182700 100644 --- a/init.lua +++ b/init.lua @@ -1,9 +1,14 @@ computertest = { config = { --Turtles are yielded after calling long events, and resumed this often (in seconds) - turtle_tick = 0.5, + --This is how long timed turtle actions take, such as mining, moving, and placing + turtle_tick = .5,--Default: 0.5 seconds --Fuel is measured in burntime seconds. A 1 second fuel allows this many actions - fuel_multiplier = 50, + fuel_multiplier = 50,--Default: 50 actions per fuel second + --Fuel inside of turtles when they spawn + fuel_initial = 10000000000,--Default: 1000 actions + --Allows turtle:debug(string) to go to debug.txt + debug = true, }, turtles = {}, num_turtles = 0,