APIs and examples and more item moving logic
parent
0cb5264e28
commit
0fc59a8d42
|
@ -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)
|
35
README.md
35
README.md
|
@ -11,6 +11,38 @@ A ComputerCraft-inspired mod for Minetest!
|
||||||
- Turtles take time to do actions such as moving,mining,turning,etc
|
- 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)
|
- 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!
|
## 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!
|
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
|
### Features to Add
|
||||||
|
|
||||||
- Add dumping into chests to create fully-auto mining
|
- 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
|
- 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.
|
- 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.
|
||||||
|
|
|
@ -13,6 +13,12 @@ minetest.register_node("computertest:turtle", {
|
||||||
},
|
},
|
||||||
groups = {oddly_breakable_by_hand=2},
|
groups = {oddly_breakable_by_hand=2},
|
||||||
paramtype2 = "facedir",
|
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)
|
on_construct = function(pos)
|
||||||
local turtle = minetest.add_entity(pos,"computertest:turtle")
|
local turtle = minetest.add_entity(pos,"computertest:turtle")
|
||||||
turtle = turtle:get_luaentity()
|
turtle = turtle:get_luaentity()
|
||||||
|
|
|
@ -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
|
local TURTLE_INVENTORYSIZE = 4*4
|
||||||
|
|
||||||
|
---@returns TurtleEntity of that ID
|
||||||
local function getTurtle(id) return computertest.turtles[id] end
|
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 isValidInventoryIndex(index) return 0 < index and index <= TURTLE_INVENTORYSIZE end
|
||||||
local function has_computertest_priv(player) return minetest.check_player_privs(player,"computertest") 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)
|
return not turtle:upload_code_to_turtle(player,"",false)
|
||||||
end
|
end
|
||||||
elseif isForm(FORMNAME_TURTLE_TERMINAL) then
|
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 id = tonumber(string.sub(formname,1+string.len(FORMNAME_TURTLE_TERMINAL)))
|
||||||
local turtle = getTurtle(id)
|
local turtle = getTurtle(id)
|
||||||
turtle.lastCommandRan = fields.terminal_in
|
turtle.lastCommandRan = fields.terminal_in
|
||||||
local command = 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"
|
command = "function init(turtle) return "..command.." end"
|
||||||
local commandResult = turtle:upload_code_to_turtle(player,command, true)
|
local commandResult = turtle:upload_code_to_turtle(player,command, true)
|
||||||
if (commandResult==nil) then
|
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());
|
minetest.show_formspec(player:get_player_name(),FORMNAME_TURTLE_TERMINAL..id,turtle:get_formspec_terminal());
|
||||||
elseif isForm(FORMNAME_TURTLE_UPLOAD) then
|
elseif isForm(FORMNAME_TURTLE_UPLOAD) then
|
||||||
local id = tonumber(string.sub(formname,1+string.len(FORMNAME_TURTLE_UPLOAD)))
|
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)
|
local turtle = getTurtle(id)
|
||||||
return not turtle:upload_code_to_turtle(player,fields.upload,false)
|
return not turtle:upload_code_to_turtle(player,fields.upload,false)
|
||||||
else
|
else
|
||||||
|
@ -58,16 +66,20 @@ end)
|
||||||
local timer = 0
|
local timer = 0
|
||||||
minetest.register_globalstep(function(dtime)
|
minetest.register_globalstep(function(dtime)
|
||||||
timer = timer + 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
|
for _,turtle in pairs(computertest.turtles) do
|
||||||
if turtle.coroutine then
|
if turtle.coroutine then
|
||||||
if coroutine.status(turtle.coroutine)=="suspended" then
|
if coroutine.status(turtle.coroutine)=="suspended" then
|
||||||
--TODO check for fuel here
|
if (turtle.fuel>0) then
|
||||||
local status, result = coroutine.resume(turtle.coroutine)
|
local status, result = coroutine.resume(turtle.coroutine)
|
||||||
minetest.log("coroutine stat "..dump(status).." said "..dump(result))
|
turtle:debug("coroutine stat "..dump(status).." said "..dump(result))
|
||||||
--elseif coroutine.status(turtle.coroutine)=="dead" then
|
turtle:debug("fuel="..turtle.fuel)
|
||||||
--minetest.log("turtle #"..id.." has coroutine, but it's already done running")
|
else
|
||||||
|
turtle:debug("No Fuel in turtle")
|
||||||
|
end
|
||||||
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
|
elseif turtle.code then
|
||||||
--minetest.log("turtle #"..id.." has no coroutine but has code! Making coroutine...")
|
--minetest.log("turtle #"..id.." has no coroutine but has code! Making coroutine...")
|
||||||
--TODO add some kinda timeout into coroutine
|
--TODO add some kinda timeout into coroutine
|
||||||
|
@ -83,9 +95,10 @@ minetest.register_globalstep(function(dtime)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
--Code responsible for generating turtle entity and turtle interface
|
--Code responsible for generating turtle entity and turtle interface
|
||||||
minetest.register_entity("computertest:turtle", {
|
|
||||||
|
local TurtleEntity = {
|
||||||
initial_properties = {
|
initial_properties = {
|
||||||
hp_max = 1,
|
hp_max = 20,
|
||||||
is_visible = true,
|
is_visible = true,
|
||||||
makes_footstep_sound = false,
|
makes_footstep_sound = false,
|
||||||
physical = true,
|
physical = true,
|
||||||
|
@ -101,260 +114,416 @@ minetest.register_entity("computertest:turtle", {
|
||||||
"computertest_front.png",
|
"computertest_front.png",
|
||||||
},
|
},
|
||||||
automatic_rotate = 0,
|
automatic_rotate = 0,
|
||||||
id = -1,
|
id = -1
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
|
||||||
--MAIN TURTLE USER INTERFACE------------------------------------------
|
--MAIN TURTLE HELPER FUNCTIONS------------------------------------------
|
||||||
get_formspec_inventory = function(turtle)
|
function TurtleEntity:getTurtleslot(turtleslot)
|
||||||
return "size[12,5;]"
|
if not isValidInventoryIndex(turtleslot) then return nil end
|
||||||
.."button[0,0;2,1;open_terminal;Open Terminal]"
|
return self.inv:get_stack("main",turtleslot)
|
||||||
.."button[2,0;2,1;upload_code;Upload Code]"
|
end
|
||||||
.."button[4,0;2,1;factory_reset;Factory Reset]"
|
function TurtleEntity:setTurtleslot(turtleslot, stack)
|
||||||
.."set_focus[open_terminal;true]"
|
if not isValidInventoryIndex(turtleslot) then return false end
|
||||||
.."list[".. turtle.inv_fullname..";main;8,1;4,4;]"
|
self.inv:set_stack("main", turtleslot,stack)
|
||||||
.."background[8,1;1,1;computertest_inventory.png]"
|
return true
|
||||||
.."list[current_player;main;0,1;8,4;]";
|
end
|
||||||
end,
|
function TurtleEntity:move(nodeLocation)
|
||||||
get_formspec_terminal = function(turtle)
|
--Verify new pos is empty
|
||||||
local previous_answers = turtle.previous_answers
|
if (nodeLocation == nil or minetest.get_node(nodeLocation).name~="air") then
|
||||||
local parsed_output = "";
|
self:yield("Moving")
|
||||||
for i=1, #previous_answers do parsed_output = parsed_output .. minetest.formspec_escape(previous_answers[i]).."," end
|
return false
|
||||||
--local saved_output = "";
|
end
|
||||||
--for i=1, #previous_answers do saved_output = saved_output .. minetest.formspec_escape(previous_answers[i]).."\n" end
|
--Take Action
|
||||||
return
|
self.object:set_pos(nodeLocation)
|
||||||
"size[12,9;]"
|
self:yield("Moving",true)
|
||||||
.."field_close_on_enter[terminal_in;false]"
|
return true
|
||||||
.."field[0,0;12,1;terminal_in;;"..minetest.formspec_escape(turtle.lastCommandRan or "").."]"
|
end
|
||||||
.."set_focus[terminal_in;true]"
|
function TurtleEntity:mine(nodeLocation)
|
||||||
.."textlist[0,1;12,8;terminal_out;"..parsed_output.."]";
|
if nodeLocation == nil then
|
||||||
end,
|
return false
|
||||||
get_formspec_upload = function(turtle)
|
end
|
||||||
--TODO could indicate if code is already uploaded
|
local node = minetest.get_node(nodeLocation)
|
||||||
return
|
if (node.name=="air") then
|
||||||
"size[12,9;]"
|
return false
|
||||||
.."button[0,0;2,1;button_upload;Upload Code to #"..turtle.id.."]"
|
end
|
||||||
.."field_close_on_enter[upload;false]"
|
--Try sucking the inventory (in case it's a chest)
|
||||||
.."textarea[0,1;12,8;upload;;"..minetest.formspec_escape(turtle.codeUncompiled or "").."]"
|
self:itemSuck(nodeLocation)
|
||||||
.."set_focus[upload;true]";
|
local drops = minetest.get_node_drops(node)
|
||||||
end,
|
--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)
|
||||||
---@return true on success
|
for _, iteminfo in pairs(drops) do
|
||||||
---
|
local stack = ItemStack(iteminfo)
|
||||||
upload_code_to_turtle = function(turtle,player, code_string,run_for_result)
|
if self.inv:room_for_item("main",stack) then
|
||||||
--Check permissions
|
self.inv:add_item("main",stack)
|
||||||
if (not has_computertest_priv(player)) then
|
end
|
||||||
--minetest.debug(player:get_player_name().." does not have computertest priv")
|
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
|
return false
|
||||||
end
|
end
|
||||||
local function sandbox(code)
|
for stackI,itemStack in pairs(listStacks) do
|
||||||
--TODO sandbox this!
|
if intable(itemStack:get_name()) == isWhitelist then
|
||||||
--Currently returns function that defines init and loop. In the future, this should probably just initialize it using some callbacks
|
local remainingItemStack = self.inv:add_item("main",itemStack)
|
||||||
if (code =="") then return nil end
|
nodeInventory:set_stack(listname, stackI, remainingItemStack)
|
||||||
return loadstring(code)
|
suckedEverything = suckedEverything and remainingItemStack:is_empty()
|
||||||
end
|
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
|
|
||||||
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
|
-- 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")
|
--static = self -- Would this work..? no.
|
||||||
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,
|
|
||||||
|
|
||||||
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,
|
static.complete = true
|
||||||
turnRight = function(turtle) return turtle:set_heading(turtle:get_heading()-1) end,
|
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,
|
function TurtleEntity:moveForward() return self:move(self:getLocForward()) end
|
||||||
mineBackward = function(turtle) return turtle:mine(turtle:get_nearby_pos (-1,0,0)) end,
|
function TurtleEntity:moveBackward() return self:move(self:getLocBackward()) end
|
||||||
mineRight = function(turtle) return turtle:mine(turtle:get_nearby_pos (0,1,0)) end,
|
function TurtleEntity:moveUp() return self:move(self:getLocUp()) end
|
||||||
mineLeft = function(turtle) return turtle:mine(turtle:get_nearby_pos (0,-1,0)) end,
|
function TurtleEntity:moveDown() return self:move(self:getLocDown()) end
|
||||||
mineUp = function(turtle) return turtle:mine(turtle:get_nearby_pos (0,0,1)) end,
|
function TurtleEntity:moveRight() return self:move(self:getLocRight()) end
|
||||||
mineDown = function(turtle) return turtle:mine(turtle:get_nearby_pos (0,0,-1)) end,
|
function TurtleEntity:moveLeft() return self:move(self:getLocLeft()) end
|
||||||
|
|
||||||
get_pos = function(turtle) return turtle.object:get_pos() end,
|
function TurtleEntity:mineForward() return self:mine(self:getLocForward()) end
|
||||||
get_fuel = function(turtle) return turtle.fuel 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
|
function TurtleEntity:buildForward(turtleslot) return self:build(self:getLocForward(),turtleslot) end
|
||||||
@returns true if it sucked everything up]]
|
function TurtleEntity:buildBackward(turtleslot) return self:build(self:getLocBackward(),turtleslot) end
|
||||||
suckBlock = function(turtle, nodeLocation)
|
function TurtleEntity:buildUp(turtleslot) return self:build(self:getLocUp(),turtleslot) end
|
||||||
local suckedEverything = true
|
function TurtleEntity:buildDown(turtleslot) return self:build(self:getLocDown(),turtleslot) end
|
||||||
local nodeInventory = minetest.get_inventory({type="node", pos=nodeLocation})
|
function TurtleEntity:buildRight(turtleslot) return self:build(self:getLocRight(),turtleslot) end
|
||||||
if not nodeInventory then
|
function TurtleEntity:buildLeft(turtleslot) return self:build(self:getLocLeft(),turtleslot) end
|
||||||
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,
|
|
||||||
|
|
||||||
-- MAIN INVENTORY COMMANDS--------------------------
|
---Scan gets the info of this node
|
||||||
--- TODO drops item onto ground
|
function TurtleEntity:scanForward() return self:scan(self:getLocForward()) end
|
||||||
itemDrop = function(turtle,itemslot)
|
function TurtleEntity:scanBackward() return self:scan(self:getLocBackward()) end
|
||||||
end,
|
function TurtleEntity:scanUp() return self:scan(self:getLocUp()) end
|
||||||
--- TODO Returns ItemStack on success or nil on failure
|
function TurtleEntity:scanDown() return self:scan(self:getLocDown()) end
|
||||||
---Ex: turtle:itemGet(3):get_name() -> "default:stone"
|
function TurtleEntity:scanRight() return self:scan(self:getLocRight()) end
|
||||||
itemGet = function(turtle,itemslot)
|
function TurtleEntity:scanLeft() return self:scan(self:getLocLeft()) end
|
||||||
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
|
|
||||||
|
|
||||||
local stackA = turtle.inv:get_stack("main",itemslotA)
|
function TurtleEntity:yield(reason,useFuel)
|
||||||
local stackB = turtle.inv:get_stack("main",itemslotB)
|
-- 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()))
|
--Inventory Interface
|
||||||
minetest.debug(dump(stackB:to_string()))
|
-- MAIN INVENTORY COMMANDS--------------------------
|
||||||
|
--- TODO drops item onto ground
|
||||||
|
function TurtleEntity:itemDrop(itemslot)
|
||||||
|
|
||||||
turtle.inv:set_stack("main",itemslotA,stackB)
|
end
|
||||||
turtle.inv:set_stack("main",itemslotB,stackA)
|
--- 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")
|
local stackA = self:getTurtleslot(turtleslotA)
|
||||||
return true
|
local stackB = self:getTurtleslot(turtleslotB)
|
||||||
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"}},
|
|
||||||
})]]
|
|
||||||
|
|
||||||
turtle.fuel = turtle.fuel + burntime * computertest.config.fuel_multiplier
|
self:setTurtleslot(turtleslotA,stackB)
|
||||||
end,
|
self:setTurtleslot(turtleslotB,stackA)
|
||||||
-- MAIN TURTLE INTERFACE END---------------------------------------
|
|
||||||
})
|
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)
|
|
@ -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
|
|
@ -1,17 +1,11 @@
|
||||||
--This simple example will make the turtle spin clockwise forever (or until it hits an obstacle)
|
--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
|
---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)
|
function init(turtle)
|
||||||
while true do
|
while true do
|
||||||
--All actions (moving, turning, mining, etc) take one "turtle_tick"
|
--Many actions (moving, turning, mining, etc) take one "turtle_tick"
|
||||||
-- to run to prevent destruction and increase realism
|
-- to run to prevent destruction and increase realism. They also use fuel.
|
||||||
turtle:moveForward()
|
turtle:moveForward()
|
||||||
turtle:turnRight()
|
turtle:turnRight()
|
||||||
end
|
end
|
||||||
|
|
|
@ -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 x<size-1 do
|
||||||
|
turtle:mineForward()
|
||||||
|
if turtle:moveForward() then x=x+1 end
|
||||||
|
end
|
||||||
|
while x>0 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
|
|
@ -3,29 +3,21 @@
|
||||||
|
|
||||||
--See example #1 on how to upload code
|
--See example #1 on how to upload code
|
||||||
function init(turtle)
|
function init(turtle)
|
||||||
local size = 3
|
local size = 5
|
||||||
local y=0
|
local z =0
|
||||||
while y<size do
|
while z <size do
|
||||||
local z=0
|
local x=0
|
||||||
while z <size do
|
while x<size do
|
||||||
local x=1
|
turtle:mineForward()
|
||||||
while x<size do
|
if turtle:moveForward() then x=x+1 end
|
||||||
turtle:mineForward()
|
|
||||||
if turtle:moveForward() then x=x+1 end
|
|
||||||
end
|
|
||||||
while x>0 do
|
|
||||||
if turtle:moveBackward() then x=x-1 end
|
|
||||||
end
|
|
||||||
turtle:mineRight()
|
|
||||||
if turtle:moveRight() then z = z +1 end
|
|
||||||
end
|
end
|
||||||
while z >0 do
|
while x>0 do
|
||||||
if turtle:moveLeft() then z = z -1 end
|
if turtle:moveBackward() then x=x-1 end
|
||||||
end
|
end
|
||||||
turtle:mineDown()
|
turtle:mineRight()
|
||||||
if turtle:moveDown() then y=y+1 end
|
if turtle:moveRight() then z = z +1 end
|
||||||
end
|
end
|
||||||
while y>0 do
|
while z >0 do
|
||||||
if turtle:moveUp() then y=y-1 end
|
if turtle:moveLeft() then z = z -1 end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -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
|
|
|
@ -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)
|
function init(turtle)
|
||||||
while true do
|
while true do
|
||||||
|
checkFuel(turtle)
|
||||||
turtle:itemMove(1,2)
|
turtle:itemMove(1,2)
|
||||||
|
turtle:turnRight()
|
||||||
|
turtle:buildForward(4)
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -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
|
|
@ -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
|
|
@ -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 y<toy do turtle:mineUp() ; turtle:moveUp() ; y=y+1 end
|
||||||
|
while x>tox do turtle:mineBackward() ; turtle:moveBackward() ; x=x-1 end
|
||||||
|
while x<tox do turtle:mineForward() ; turtle:moveForward() ; x=x+1 end
|
||||||
|
while z>toz do turtle:mineLeft() ; turtle:moveLeft() ; z=z-1 end
|
||||||
|
while z<toz do turtle:mineRight() ; turtle:moveRight() ; z=z+1 end
|
||||||
|
while y>toy 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
|
|
@ -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
|
|
@ -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
|
9
init.lua
9
init.lua
|
@ -1,9 +1,14 @@
|
||||||
computertest = {
|
computertest = {
|
||||||
config = {
|
config = {
|
||||||
--Turtles are yielded after calling long events, and resumed this often (in seconds)
|
--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 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 = {},
|
turtles = {},
|
||||||
num_turtles = 0,
|
num_turtles = 0,
|
||||||
|
|
Loading…
Reference in New Issue