APIs and examples and more item moving logic. Crafting (sort-of, still WIP)!

master
zaners123 2022-01-04 00:01:42 -08:00
parent fff5b15a78
commit 72068abf28
4 changed files with 354 additions and 164 deletions

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
*.iml
.idea
.nogit
.nogit.*

73
API.md
View File

@ -1,6 +1,8 @@
# ComputerTest API
This api is two parts, with notes/documentation above, and functions below
This api is two parts, with notes/documentation above, and functions below.
If you're confused by Minetest terminology (node, ItemStack, etc), read [lua_api.txt](https://raw.githubusercontent.com/minetest/minetest/master/doc/lua_api.txt)
## Init Function
See README.md for basic setup.
@ -32,28 +34,61 @@ turtle:turnRight()
## The turtle's inventory
The turtle has 16 'turtleslots' numbered 1-16.
The turtle has 16 'turtleslots' numbered 1-16. The top-left is slot 1, to the right is slot 2. The bottom left is slot 16.
## Config
Edit init.lua in the config object at the top to change the config.
Actions take effect on restart/reload.
## Yielding
Many functions (ex: build,mine,scan,move) yield to prevent lag, balance the mod, and to prevent hanging.
You MUST have a yield in all time-consuming processes. For example:
```lua
function init(turtle)
while (true) do
turtle:yield("Being a good turtle")
end
end
```
If this function didn't have the yield, it would crash the server (freeze the server thread)
If you don't yield, and your turtle takes too long, the entire server will lag/halt.
## 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.
See above for the importance of yielding!
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
###`turtle:build[direction](turtleslot)
- Returns true if built
Places the block that's in the turtleslot.
Example: `turtle:buildForward(1)` builds the item in the top-left slot right in front of the turtle
###`turtle:scan[direction]()
- Returns scan result
Returns the node at the given position as table in the format`{name="node_name", param1=0, param2=0}`
Returns `nil` for unloaded areas
Example `turtle:scanLeft().name=='default:stone_with_gold` tells you there's gold ore to your left
## Inventory/Fuel functions
### `turtle:useFuel()`
@ -70,6 +105,34 @@ Takes fuel from this turtleslot and increases the turtle's fuel
Moves one block in that direction, yields, and consumes fuel
###`turtle:itemPush[direction](filterList, isWhitelist, listname)
The opposite of itemSuck. Push everything from the turtle's inventory that matches some filter into the nearby block inventory
Example: `turtle:itemPushRight({"default:coal_lump","wood","tree"},true,'fuel')` takes all coal lumps,wood-group, or tree-group items from the turtle. Puts them into the furnace-to-the-right's fuel slot.
Example: `turtle:itemPushForward()` pushes everything into the front chest
###`turtle:itemSuck[direction](filterList, isWhitelist, listname)`
The opposite of itemPush. Takes everything from the node inventory that matches some filter into the turtle's inventory
Example: `turtle:itemSuckRight({"default:sand","tree"},false)` takes everything except sand or tree-group things (since false means blacklist)
Example: `turtle:itemSuckForward()` takes everything from the front chest
Example: `turtle:itemSuckBackward({},'false','dst')` takes output from the behind furnace (since src is input and dst is output)
###`itemPushTurtleslot[direciton](turtleslot, listname)`
Takes an item from a slot and puts it into the adjacent node
###`itemGet(turtleslot)`
Get the ItemStack inside a turtleslot
Example: `turtle:itemGet(4 ):get_name()=="default:stone""` tells you if there is stone in slot 4 (the top-right slot)
Example: `turtle:itemGet(13):get_count() > 10` tells you if there is more than 10 items in slot 13 (the bottom-left slot)
###`itemDropTurtleslot[direction](turtleslot)`
Drops an item on the ground. Go to [itemPush](itemPush) for putting into chests
## Info functions
###`turtle:setName(name)`

View File

@ -6,10 +6,10 @@ A ComputerCraft-inspired mod for Minetest!
## Working Features
- LUA can be uploaded and ran on a turtle (see [/examples]())
- Actions like mining, moving and turning the turtle
- Turtles take time to do actions such as moving,mining,turning,etc
- LUA can be uploaded and ran on a turtle. See the [API](API.md) and the [examples](/examples/EXAMPLES.md)
- The turtle can do actions like mining, moving, turning, inventory management, and more!
- A "computertest" privilege, to limit turtle usage to trusted users (so only users with this privilege can use/edit turtles)
- Turtles consume fuel after completing hard actions such as mining (configurable)
## An example of how to use this mod
@ -39,16 +39,15 @@ end
## Other Information
The API.md contains important documentation for programming.
The [API.md](API.md) contains important documentation for programming.
EXAMPLES.md contains some fun examples
[EXAMPLES.md](examples/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!
Anyone interested in adding these features can! in entity/turtle.lua, I'd be interested in any great working pull requests!
### Features to Add
- Add dumping into chests to create fully-auto mining
- Inventory management commands, such as crafting, sorting, and dropping
- Inventory management commands, such as crafting and sorting
- 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.

View File

@ -13,6 +13,39 @@ 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
---@param item string
---@param filterList table of strings
---@param isWhitelist boolean true=whitelist filtering, false=blacklist filtering
---@return boolean true if item in filter
local function infilter(item, filterList, isWhitelist)
for _, filterItemname in pairs(filterList) do
local isInGroup = minetest.get_item_group(item, filterItemname)
if item== filterItemname or (isInGroup ~= 0 and isInGroup ~= nil) then
return isWhitelist
end
end
return not isWhitelist
end
-- advtrains inventory serialization helper (c) 2017 orwell96
local function serializeInventory(inv)
local data = {}
for listName, listStack in pairs(inv:get_lists()) do
data[listName]={}
for index, item in ipairs(listStack) do
local itemString =item:to_string()
data[listName][index] = itemString
end
end
return minetest.serialize(data)
end
local function deserializeInventory(inv, str)
local data = minetest.deserialize(str)
if data then
inv:set_lists(data)
return true
end
return false
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
local function isForm(name)
@ -22,15 +55,15 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
if isForm(FORMNAME_TURTLE_INVENTORY) then
local id = tonumber(string.sub(formname,1+string.len(FORMNAME_TURTLE_INVENTORY)))
local turtle = getTurtle(id)
if (fields.upload_code=="Upload Code") then
if fields.upload_code=="Upload Code" then
minetest.show_formspec(player:get_player_name(),FORMNAME_TURTLE_UPLOAD..id,turtle:get_formspec_upload());
elseif (fields.open_terminal=="Open Terminal") then
elseif fields.open_terminal=="Open Terminal" then
minetest.show_formspec(player:get_player_name(),FORMNAME_TURTLE_TERMINAL..id,turtle:get_formspec_terminal());
elseif (fields.factory_reset=="Factory Reset") then
elseif fields.factory_reset=="Factory Reset" then
return not turtle:upload_code_to_turtle(player,"",false)
end
elseif isForm(FORMNAME_TURTLE_TERMINAL) then
if (fields.terminal_out ~= nil) then
if fields.terminal_out ~= nil then
return true
end
local id = tonumber(string.sub(formname,1+string.len(FORMNAME_TURTLE_TERMINAL)))
@ -42,7 +75,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end
command = "function init(turtle) return "..command.." end"
local commandResult = turtle:upload_code_to_turtle(player,command, true)
if (commandResult==nil) then
if commandResult == nil then
minetest.close_formspec(player:get_player_name(),FORMNAME_TURTLE_TERMINAL..id)
return true
end
@ -51,7 +84,7 @@ 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
if fields.button_upload == nil or fields.upload == nil then
return true
end
local turtle = getTurtle(id)
@ -70,10 +103,13 @@ minetest.register_globalstep(function(dtime)
for _,turtle in pairs(computertest.turtles) do
if turtle.coroutine then
if coroutine.status(turtle.coroutine)=="suspended" then
if (turtle.fuel>0) then
--Auto-Refuel
if turtle.fuel<0 and turtle.autoRefuel then turtle:autoRefuel() end
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)
turtle:debug("coroutine stat "..dump(status).." said "..dump(result).."fuel="..turtle.fuel)
else
turtle:debug("No Fuel in turtle")
end
@ -105,6 +141,7 @@ local TurtleEntity = {
collisionbox = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 },
visual = "cube",
visual_size = { x = 0.9, y = 0.9 },
static_save = true, -- Make sure it gets saved statically
textures = {
"computertest_top.png",
"computertest_bottom.png",
@ -130,7 +167,7 @@ function TurtleEntity:setTurtleslot(turtleslot, stack)
end
function TurtleEntity:move(nodeLocation)
--Verify new pos is empty
if (nodeLocation == nil or minetest.get_node(nodeLocation).name~="air") then
if nodeLocation == nil or minetest.get_node(nodeLocation).name~="air" then
self:yield("Moving")
return false
end
@ -144,7 +181,7 @@ function TurtleEntity:mine(nodeLocation)
return false
end
local node = minetest.get_node(nodeLocation)
if (node.name=="air") then
if node.name == "air" then
return false
end
--Try sucking the inventory (in case it's a chest)
@ -182,37 +219,21 @@ function TurtleEntity:build(nodeLocation, turtleslot)
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
function TurtleEntity:scan(nodeLocation) return minetest.get_node_or_nil(nodeLocation) end
function TurtleEntity:itemDropTurtleslot(nodeLocation,turtleslot)
local drop_pos = minetest.find_node_near(nodeLocation, 1, {"air"}) or nodeLocation
local leftover = minetest.item_drop(self:getTurtleslot(turtleslot), nil, drop_pos)
self:setTurtleslot(turtleslot, leftover)
end
---Takes everything from block that matches list
--- @param filterlist - Something like {"default:stone","default:dirt"}
--- @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 {}
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
@ -220,14 +241,8 @@ function TurtleEntity:itemSuck(nodeLocation, filterlist, isWhitelist, listname)
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
for stackI,itemStack in pairs(listStacks) do
if intable(itemStack:get_name()) == isWhitelist then
if infilter(itemStack:get_name(),filterList, isWhitelist) then
local remainingItemStack = self.inv:add_item("main",itemStack)
nodeInventory:set_stack(listname, stackI, remainingItemStack)
suckedEverything = suckedEverything and remainingItemStack:is_empty()
@ -245,48 +260,66 @@ function TurtleEntity:itemSuck(nodeLocation, filterlist, isWhitelist, listname)
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)
--- Pushes a single turtleslot, unfiltered
--- @returns true if stack completely pushed
function TurtleEntity:itemPushTurtleslot(nodeLocation, turtleslot, 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)
return false--If 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
---Pushes everything in turtle that matches filter
--- @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 - Push to this listname. If nil, push to main
--- @return boolean true if able to push everything that matches the filter
function TurtleEntity:itemPush(nodeLocation, filterList, isWhitelist, listname)
local ret = true
listname = listname or "main"
filterList = filterList or {}
local nodeInventory = minetest.get_inventory({type="node", pos=nodeLocation}) -- InvRef
if not nodeInventory then
return false --No node inventory (Ex: Not a chest)
end
minetest.debug("Pushing into inventory")
for turtleslot = 1, TURTLE_INVENTORYSIZE do
local toPush = self:getTurtleslot(turtleslot)
minetest.debug("Topush: "..toPush:to_string())
if infilter(toPush:get_name(), filterList, isWhitelist) then
minetest.debug("Pushing into inventory, in filter")
local remainingItemStack = nodeInventory:add_item(listname,toPush)
self:setTurtleslot(turtleslot, remainingItemStack)
ret = ret and remainingItemStack:is_empty()
end
end
return ret
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
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
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
if run_for_result then
--TODO run subroutine once, if it returns a value, return that here
return "Ran"
end
@ -327,24 +360,34 @@ function TurtleEntity:get_formspec_upload()
end
--MAIN TURTLE ENTITY FUNCTIONS------------------------------------------
function TurtleEntity:on_activate(staticdata, dtime_s)
--TODO use staticdata to load previous state, such as inventory and whatnot
local data = minetest.deserialize(staticdata)
if type(data) ~= "table" or not data.complete then data = {} end
--Give ID
computertest.num_turtles = computertest.num_turtles+1
self.id = computertest.num_turtles
self.name = "Unnamed #"..self.id
self.name = data.name or "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
self.heading = data.heading or 0
self.previous_answers = data.previous_answers or {}
self.coroutine = data.coroutine or nil
self.fuel = data.fuel or computertest.config.fuel_initial
self.autoRefuel = data.autoRefuel or true
self.codeUncompiled = data.codeUncompiled or ""
--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
self.inv = minetest.create_detached_inventory(self.inv_name,{})
if self.inv == nil or self.inv == false then
error("Could not spawn inventory")
end
--Keep items from save
if data.inv ~= nil then
deserializeInventory(self.inv,data.inv)
end
self.inv:set_size("main", TURTLE_INVENTORYSIZE)
-- Add to turtle list
computertest.turtles[self.id] = self
end
@ -352,7 +395,7 @@ function TurtleEntity:on_rightclick(clicker)
if not clicker or not clicker:is_player() then
return
end
if (not has_computertest_priv(clicker))then
if not has_computertest_priv(clicker) then
minetest.show_formspec(clicker:get_player_name(),FORMNAME_TURTLE_NOPRIV,FORM_NOPRIV);
return
end
@ -360,15 +403,19 @@ function TurtleEntity:on_rightclick(clicker)
self:get_formspec_inventory())
end
function TurtleEntity:get_staticdata()
minetest.debug("Deleting/Forgetting all data of turtle "..self.name)
local static = {}
-- TODO convert inventory and internal code to string and back somehow, or else it'll be deleted every time the entity gets unloaded
--static = self -- Would this work..? no.
static.complete = true
return minetest.serialize(static)
minetest.debug("Serializing turtle "..self.name)
return minetest.serialize({
id = self.id,
name = self.name,
heading = self.heading,
previous_answers = self.previous_answers,
coroutine = nil,--self.coroutine,
fuel = self.fuel,
autoRefuel = self.autoRefuel,
inv = serializeInventory(self.inv),
codeUncompiled = self.codeUncompiled,
complete = true,
})
end
--MAIN PLAYER INTERFACE (CALL THESE)------------------------------------------
function TurtleEntity:getLoc() return self.object:get_pos() end
@ -378,19 +425,13 @@ function TurtleEntity:getLocRelative(numForward,numUp,numRight)
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
if self:getHeading()%4==0 then new_pos.z=pos.z-numForward;new_pos.x=pos.x-numRight; end
if self:getHeading()%4==1 then new_pos.x=pos.x+numForward;new_pos.z=pos.z-numRight; end
if self:getHeading()%4==2 then new_pos.z=pos.z+numForward;new_pos.x=pos.x+numRight; end
if self:getHeading()%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
@ -398,50 +439,84 @@ function TurtleEntity:useFuel()
end
end
--- From 0 to 3
function TurtleEntity:set_heading(heading)
function TurtleEntity:setHeading(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
self:yield("Turning",true)
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
function TurtleEntity:getHeading() return self.heading end
function TurtleEntity:turnLeft() return self:setHeading(self:getHeading()+1) end
function TurtleEntity:turnRight() return self:setHeading(self:getHeading()-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
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
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
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
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
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
---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
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
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
function TurtleEntity:itemDropTurtleslotForward (turtleslot) return self:itemDropTurtleslot(self:getLocForward() , turtleslot) end
function TurtleEntity:itemDropTurtleslotBackward(turtleslot) return self:itemDropTurtleslot(self:getLocBackward() , turtleslot) end
function TurtleEntity:itemDropTurtleslotUp (turtleslot) return self:itemDropTurtleslot(self:getLocUp() , turtleslot) end
function TurtleEntity:itemDropTurtleslotDown (turtleslot) return self:itemDropTurtleslot(self:getLocDown() , turtleslot) end
function TurtleEntity:itemDropTurtleslotRight (turtleslot) return self:itemDropTurtleslot(self:getLocRight() , turtleslot) end
function TurtleEntity:itemDropTurtleslotLeft (turtleslot) return self:itemDropTurtleslot(self:getLocLeft() , turtleslot) end
function TurtleEntity:itemPushForward (filterList, isWhitelist, listname) return self:itemPush(self:getLocForward() , filterList, isWhitelist, listname) end
function TurtleEntity:itemPushBackward (filterList, isWhitelist, listname) return self:itemPush(self:getLocBackward() , filterList, isWhitelist, listname) end
function TurtleEntity:itemPushUp (filterList, isWhitelist, listname) return self:itemPush(self:getLocUp() , filterList, isWhitelist, listname) end
function TurtleEntity:itemPushDown (filterList, isWhitelist, listname) return self:itemPush(self:getLocDown() , filterList, isWhitelist, listname) end
function TurtleEntity:itemPushRight (filterList, isWhitelist, listname) return self:itemPush(self:getLocRight() , filterList, isWhitelist, listname) end
function TurtleEntity:itemPushLeft (filterList, isWhitelist, listname) return self:itemPush(self:getLocLeft() , filterList, isWhitelist, listname) end
function TurtleEntity:itemSuckForward (filterList, isWhitelist, listname) return self:itemSuck(self:getLocForward() , filterList, isWhitelist, listname) end
function TurtleEntity:itemSuckBackward (filterList, isWhitelist, listname) return self:itemSuck(self:getLocBackward() , filterList, isWhitelist, listname) end
function TurtleEntity:itemSuckUp (filterList, isWhitelist, listname) return self:itemSuck(self:getLocUp() , filterList, isWhitelist, listname) end
function TurtleEntity:itemSuckDown (filterList, isWhitelist, listname) return self:itemSuck(self:getLocDown() , filterList, isWhitelist, listname) end
function TurtleEntity:itemSuckRight (filterList, isWhitelist, listname) return self:itemSuck(self:getLocRight() , filterList, isWhitelist, listname) end
function TurtleEntity:itemSuckLeft (filterList, isWhitelist, listname) return self:itemSuck(self:getLocLeft() , filterList, isWhitelist, listname) end
function TurtleEntity:itemPushTurtleslotForward (turtleslot, listname) return self:itemPushTurtleslot(self:getLocForward() , turtleslot, listname) end
function TurtleEntity:itemPushTurtleslotBackward(turtleslot, listname) return self:itemPushTurtleslot(self:getLocBackward() , turtleslot, listname) end
function TurtleEntity:itemPushTurtleslotUp (turtleslot, listname) return self:itemPushTurtleslot(self:getLocUp() , turtleslot, listname) end
function TurtleEntity:itemPushTurtleslotDown (turtleslot, listname) return self:itemPushTurtleslot(self:getLocDown() , turtleslot, listname) end
function TurtleEntity:itemPushTurtleslotRight (turtleslot, listname) return self:itemPushTurtleslot(self:getLocRight() , turtleslot, listname) end
function TurtleEntity:itemPushTurtleslotLeft (turtleslot, listname) return self:itemPushTurtleslot(self:getLocLeft() , turtleslot, listname) end
function TurtleEntity:yield(reason,useFuel)
-- Yield at least once
if (coroutine.running() == self.coroutine) then
if coroutine.running() == self.coroutine then
coroutine.yield(reason)
end
--Use a fuel if requested
@ -450,17 +525,11 @@ end
--Inventory Interface
-- MAIN INVENTORY COMMANDS--------------------------
--- TODO drops item onto ground
function TurtleEntity:itemDrop(itemslot)
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
function TurtleEntity:itemGet(turtleslot) return self:getTurtleslot(turtleslot) end
--- Swaps itemstacks in slots A and B
function TurtleEntity:itemMove(turtleslotA, turtleslotB)
function TurtleEntity:itemSwapTurtleslot(turtleslotA, turtleslotB)
if (not isValidInventoryIndex(turtleslotA)) or (not isValidInventoryIndex(turtleslotB)) then
self:yield("Inventorying")
return false
@ -476,44 +545,101 @@ function TurtleEntity:itemMove(turtleslotA, turtleslotB)
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
function TurtleEntity:itemSplitTurtleslot(turtleslotSrc, turtleslotDst, amount)
if (not isValidInventoryIndex(turtleslotSrc)) or (not isValidInventoryIndex(turtleslotDst)) or (not self:isTurtleslotEmpty(turtleslotDst)) then
self:yield("Inventorying")
return false
end
local stackToSplit = self:getTurtleslot(turtleslotSrc)
amount = math.min(math.floor(tonumber(amount or 1)), stackToSplit:get_count())
stackToSplit:set_count(stackToSplit:get_count() - amount)
self:setTurtleslot(turtleslotSrc, stackToSplit)
stackToSplit:set_count(amount)
self:setTurtleslot(turtleslotDst, stackToSplit)
self:yield("Inventorying")
return true
end
--- TODO craft using top right 3x3 grid, and put result in itemslotResult
function TurtleEntity:itemCraft(itemslotResult)
-- Use minetest.get_craft_result
function TurtleEntity:itemCraft(turtleslotResult)
if not isValidInventoryIndex(turtleslotResult) then
return false
end
local craftSquares = {2,3,4,6,7,8,10,11,12}
local output,decremented_input = minetest.get_craft_result({
method = "normal",
width = 3,
items = {
self:getTurtleslot(craftSquares[1]),self:getTurtleslot(craftSquares[2]),self:getTurtleslot(craftSquares[3]),
self:getTurtleslot(craftSquares[4]),self:getTurtleslot(craftSquares[5]),self:getTurtleslot(craftSquares[6]),
self:getTurtleslot(craftSquares[7]),self:getTurtleslot(craftSquares[8]),self:getTurtleslot(craftSquares[9]),
}
})
if output.item:is_empty() then
return false
end
--Put rest of ingredients back
for i, turtleslot in pairs(craftSquares) do
self:setTurtleslot(turtleslot,decremented_input.items[i])
end
-- Put output in output slot
local leftover_craft = self:getTurtleslot(turtleslotResult):add_item(output.item)
--TODO Deal with leftover craft and output.replacements
return true
end
--- @returns True if fuel was consumed. False if itemslot did not have fuel.
function TurtleEntity:itemRefuel(turtleslot)
if (not isValidInventoryIndex(turtleslot)) then
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
local fuel, afterfuel = minetest.get_craft_result({
method = "fuel",
width = 1,
items = {self:getTurtleslot(turtleslot)}
})
if fuel.time == 0 then
return false
end
self:setTurtleslot(turtleslot,nil)
self.fuel = self.fuel + quantity * burntime * computertest.config.fuel_multiplier
self:setTurtleslot(turtleslot, afterfuel.items[1])
--Process replacements (Such as buckets from lava buckets)
local replacements = fuel.replacements
if replacements[1] then
local leftover = self.inv:add_item("main", replacements[1])
if not leftover:is_empty() then
local pos = self:getLoc()
local above = vector.new(pos.x, pos.y + 1, pos.z)
local drop_pos = minetest.find_node_near(above, 1, {"air"}) or above
minetest.item_drop(replacements[1], nil, drop_pos)
end
end
self.fuel = self.fuel + fuel.time * 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:isTurtleslotEmpty(turtleslot) return self:getTurtleslot(turtleslot):is_empty() end
function TurtleEntity:setAutoRefuel(autoRefuel) self.autoRefuel = not not autoRefuel end
function TurtleEntity:autoRefuel()
for turtleslot = 1, 16 do
if turtle:getFuel() > 100 then
return true
end
turtle:itemRefuel(turtleslot)
end
return false
end
function TurtleEntity:setName(name)
self.name = minetest.formspec_escape(name)
self.nametag = self.name
end
function TurtleEntity:debug(string)
if computertest.config.debug then