APIs and examples and more item moving logic. Crafting (sort-of, still WIP)!
parent
fff5b15a78
commit
72068abf28
|
@ -1,2 +1,4 @@
|
|||
*.iml
|
||||
.idea
|
||||
.nogit
|
||||
.nogit.*
|
73
API.md
73
API.md
|
@ -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)`
|
||||
|
|
15
README.md
15
README.md
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue