fixed a typo in the robot lib; made count and space direct callbacks and allowing to specify a slot to check for (defaults to selected one); prioritizing living entities in robot detection and fixed entity detection sidedness; added simple excavation program; general cleanup

This commit is contained in:
Florian Nücke 2013-12-04 14:11:54 +01:00
parent f94022c61c
commit 3d343ec34f
15 changed files with 294 additions and 80 deletions

View File

@ -0,0 +1,214 @@
if not robot then
error("can only run on robots", 0)
end
local args, options = shell.parse(...)
if #args < 1 then
print("Usage: dig [-s] <size>")
print(" -s: shutdown when done.")
return
end
local size = tonumber(args[1])
if not size then
error("invalid size", 0)
end
local r = component.computer
local x, y, z, f = 0, 0, 0, 0
local delta = {[0] = function() x = x + 1 end, [1] = function() y = y + 1 end,
[2] = function() x = x - 1 end, [3] = function() y = y - 1 end}
local function turnRight()
robot.turnRight()
f = (f + 1) % 4
end
local function turnLeft()
robot.turnLeft()
f = (f - 1) % 4
end
local function turnTowards(side)
if f == side - 1 then
turnRight()
else
while f ~= side do
turnLeft()
end
end
end
local checkedDrop -- forward declaration
local function clearBlock(side, cannotRetry)
while r.suck(side) do
checkedDrop()
end
local result, reason = r.swing(side)
if result then
checkedDrop()
else
local _, what = r.detect(side)
if cannotRetry and what ~= "air" and what ~= "entity" then
return false
end
end
return true
end
local function tryMove(side)
side = side or sides.forward
local tries = 10
while not r.move(side) do
tries = tries - 1
if not clearBlock(side, tries < 1) then
return false
end
end
if side == sides.down then
z = z + 1
elseif side == sides.up then
z = z - 1
else
delta[f]()
end
return true
end
local function moveTo(tx, ty, tz, backwards)
local axes = {
function()
while z > tz do
tryMove(sides.up)
end
while z < tz do
tryMove(sides.down)
end
end,
function()
if y > ty then
turnTowards(3)
repeat tryMove() until y == ty
elseif y < ty then
turnTowards(1)
repeat tryMove() until y == ty
end
end,
function()
if x > tx then
turnTowards(2)
repeat tryMove() until x == tx
elseif x < tx then
turnTowards(0)
repeat tryMove() until x == tx
end
end
}
if backwards then
for axis = 3, 1, -1 do
axes[axis]()
end
else
for axis = 1, 3 do
axes[axis]()
end
end
end
function checkedDrop(force)
local empty = 0
for slot = 1, 16 do
if robot.count(slot) == 0 then
empty = empty + 1
end
end
if empty == 0 or force and empty < 16 then
local ox, oy, oz, of = x, y, z, f
moveTo(0, 0, 0)
turnTowards(2)
for slot = 1, 16 do
if robot.count(slot) > 0 then
robot.select(slot)
repeat robot.drop() until robot.count(slot) == 0
end
end
robot.select(1)
moveTo(ox, oy, oz, true)
turnTowards(of)
end
end
local function step()
clearBlock(sides.down)
if not tryMove() then
return false
end
clearBlock(sides.up)
return true
end
local function turn(i)
if i % 2 == 1 then
turnRight()
else
turnLeft()
end
end
local function digLayer()
--[[ We move in zig-zag lines, clearing three layers at a time. This means we
have to differentiate at the end of the last line between even and odd
sizes on which way to face for the next layer:
For either size we rotate once to the right. For even sizes this will
cause the next layer to be dug out rotated by ninety degrees. For odd
ones the return path is symmetrical, meaning we just turn around.
Examples for two layers:
s--x--x e--x--x s--x--x--x x--x x--x
| | | | | | |
x--x--x -> x--x--x x--x--x--x x x x x
| | | -> | | | |
x--x--e x--x--s x--x--x--x x x x x
| | | | |
e--x--x--x s x--x e
Legend: s = start, x = a position, e = end, - = a move
]]
for i = 1, size do
for j = 1, size - 1 do
if not step() then
return false
end
end
if i < size then
-- End of a normal line, move the "cap".
turn(i)
if not step() then
return false
end
turn(i)
else
turnRight()
if size % 2 == 1 then
turnRight()
end
for i = 1, 3 do
if not tryMove(sides.down) then
return false
end
end
end
end
return true
end
repeat until not digLayer()
moveTo(0, 0, 0)
turnTowards(0)
checkedDrop(true)
if options.s then
os.shutdown()
end

View File

@ -22,24 +22,24 @@ end
-------------------------------------------------------------------------------
-- Inventory
function robot.select(index)
return component.computer.select(index)
function robot.select(slot)
return component.computer.select(slot)
end
function robot.count()
return component.computer.count()
function robot.count(slot)
return component.computer.count(slot)
end
function robot.space()
return component.computer.select()
function robot.space(slot)
return component.computer.space(slot)
end
function robot.compareTo(index)
return component.computer.compareTo(index)
function robot.compareTo(slot)
return component.computer.compareTo(slot)
end
function robot.transferTo(index, count)
return component.computer.transferTo(index, count)
function robot.transferTo(slot, count)
return component.computer.transferTo(slot, count)
end
-------------------------------------------------------------------------------

View File

@ -25,7 +25,9 @@ local sides = {
negz = 2,
posz = 3,
negx = 4,
posx = 5
posx = 5,
forward = 3
}
-------------------------------------------------------------------------------

View File

@ -16,7 +16,6 @@ object Recipes {
val craftingTable = new ItemStack(Block.workbench)
val diamond = new ItemStack(Item.diamond)
val dispenser = new ItemStack(Block.dispenser)
val dropper = new ItemStack(Block.dropper)
val emerald = new ItemStack(Item.emerald)
val enderPearl = new ItemStack(Item.enderPearl)
val glass = new ItemStack(Block.glass)
@ -461,11 +460,11 @@ object Recipes {
'b', pcb)
addRecipe(Items.crafting.createItemStack(),
"idi",
"ipi",
"cwc",
"ibi",
'i', ironIngot,
'd', dropper,
'p', piston,
'c', chip1,
'w', craftingTable,
'b', pcb)

View File

@ -18,6 +18,11 @@ public interface RobotContext extends Context {
* robot's current position and rotation in the world. Use this to trigger
* events involving the robot that require a player entity, and for
* interacting with the robots' inventory.
* <p/>
* Note that the inventory of each robot is structured such that the first
* four slots are the "equipment" slots, from left to right, i.e. slot one
* is the tool slot, slot two is the card slot, three the disk slot and
* slot four is for upgrades. The inventory proper starts after that.
*
* @return the fake player for the robot.
*/

View File

@ -3,7 +3,7 @@
* <p/>
* There are several parts to this API:
* <dl>
* <dt>The {@link Driver} API</dt>
* <dt>The {@link li.cil.oc.api.Driver} API</dt>
* <dd>
* This API is used to provide glue code to the mod that allows it to interact
* with foreign objects. You need a driver if you wish to connect some object
@ -17,13 +17,13 @@
* {@link li.cil.oc.api.network.Environment}. For items that should be installed
* in a computer, however, you will always have to provide a driver.
* </dd>
* <dt>The {@link FileSystem} API</dt>
* <dt>The {@link li.cil.oc.api.FileSystem} API</dt>
* <dd>
* This API provides facilities that make it easier to create file systems that
* can be interacted with from Lua programs via the file system driver that
* comes with the mod.
* </dd>
* <dt>The {@link Network} API</dt>
* <dt>The {@link li.cil.oc.api.Network} API</dt>
* <dd>
* This API provides interfaces that allow interacting with the internal network
* and creating nodes, components and power connectors for said network. If you

View File

@ -119,11 +119,10 @@ trait Inventory extends TileEntity with IInventory with Persistable {
items.zipWithIndex collect {
case (Some(stack), slot) => (stack, slot)
} map {
case (stack, slot) => {
case (stack, slot) =>
val slotNbt = new NBTTagCompound()
slotNbt.setByte("slot", slot.toByte)
slotNbt.setNewCompoundTag("item", stack.writeToNBT)
}
})
}

View File

@ -260,9 +260,8 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
node.sendToReachable("computer.stopped")
start()
// Resume from pauses based on sleep or signal underflow.
case Computer.State.Sleeping if lastUpdate >= sleepUntil || !signals.isEmpty => {
case Computer.State.Sleeping if lastUpdate >= sleepUntil || !signals.isEmpty =>
switchTo(Computer.State.Yielded)
}
// Resume in case we paused because the game was paused.
case Computer.State.Paused =>
if (remainingPause > 0) {
@ -274,7 +273,7 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
switchTo(state.top) // Trigger execution if necessary.
}
// Perform a synchronized call (message sending).
case Computer.State.SynchronizedCall => {
case Computer.State.SynchronizedCall =>
// These three asserts are all guaranteed by run().
assert(lua.getTop == 2)
assert(lua.isThread(1))
@ -317,7 +316,6 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
OpenComputers.log.log(Level.WARNING, "Faulty Lua implementation for synchronized calls.", e)
crash("protocol error")
}
}
case _ => // Nothing special to do, just avoid match errors.
})
@ -496,10 +494,9 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
// Delay execution for a second to allow the world around us to settle.
pause(Settings.get.startupDelay)
} catch {
case e: LuaRuntimeException => {
case e: LuaRuntimeException =>
OpenComputers.log.warning("Could not unpersist computer.\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat "))
state.push(Computer.State.Stopping)
}
}
}
else close() // Clean up in case we got a weird state stack.
@ -571,10 +568,9 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
nbt.setInteger("remainingPause", remainingPause)
message.foreach(nbt.setString("message", _))
} catch {
case e: LuaRuntimeException => {
case e: LuaRuntimeException =>
OpenComputers.log.warning("Could not persist computer.\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat "))
nbt.removeTag("state")
}
}
// Limit memory again.
@ -639,16 +635,14 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
lua.newTable()
var count = 0
value.foreach {
case (x, index) => {
x match {
case (entry: ScalaNumber) =>
pushResult(lua, entry.underlying())
case (entry) =>
pushResult(lua, entry.asInstanceOf[AnyRef])
}
case (x, index) => x match {
case (entry: ScalaNumber) =>
pushResult(lua, entry.underlying())
case (entry) =>
pushResult(lua, entry.asInstanceOf[AnyRef])
}
lua.rawSet(-2, index + 1)
count = count + 1
}
}
lua.pushString("n")
lua.pushInteger(count)
@ -1045,10 +1039,9 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
return true
}
catch {
case ex: Throwable => {
case ex: Throwable =>
OpenComputers.log.log(Level.WARNING, "Failed initializing computer.", ex)
close()
}
}
false
}

View File

@ -196,13 +196,12 @@ class FileSystem(val fileSystem: api.fs.FileSystem, var label: Label) extends Ma
message.data match {
case Array() if message.name == "computer.stopped" || message.name == "computer.started" =>
owners.get(message.source.address) match {
case Some(set) => {
case Some(set) =>
set.foreach(handle => Option(fileSystem.getHandle(handle)) match {
case Some(file) => file.close()
case _ => // Invalid handle... huh.
})
set.clear()
}
case _ => // Computer had no open files.
}
case _ =>

View File

@ -50,7 +50,7 @@ abstract class GraphicsCard extends ManagedComponent {
val address = args.checkString(0)
node.network.node(address) match {
case null => Array(Unit, "invalid address")
case node: Node if node.host.isInstanceOf[Buffer] => {
case node: Node if node.host.isInstanceOf[Buffer] =>
screenAddress = Option(address)
screenInstance = Some(node.host.asInstanceOf[Buffer])
screen(s => {
@ -62,7 +62,6 @@ abstract class GraphicsCard extends ManagedComponent {
s.background = 0x000000
result(true)
})
}
case _ => Array(Unit, "not a screen")
}
}

View File

@ -10,7 +10,7 @@ import net.minecraft.entity.item.EntityItem
import net.minecraft.entity.{EntityLivingBase, Entity}
import net.minecraft.inventory.{IInventory, ISidedInventory}
import net.minecraft.item.{ItemStack, ItemBlock}
import net.minecraft.util.{Vec3, MovingObjectPosition, EnumMovingObjectType}
import net.minecraft.util.{MovingObjectPosition, EnumMovingObjectType}
import net.minecraftforge.common.ForgeDirection
import net.minecraftforge.fluids.FluidRegistry
import scala.Some
@ -50,19 +50,27 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) with RobotConte
result(selectedSlot - actualSlot(0) + 1)
}
@LuaCallback("count")
def count(context: Context, args: Arguments): Array[AnyRef] =
result(stackInSlot(selectedSlot) match {
@LuaCallback(value = "count", direct = true)
def count(context: Context, args: Arguments): Array[AnyRef] = {
val slot =
if (args.count > 0 && args.checkAny(0) != null) checkSlot(args, 0)
else selectedSlot
result(stackInSlot(slot) match {
case Some(stack) => stack.stackSize
case _ => 0
})
}
@LuaCallback("space")
def space(context: Context, args: Arguments): Array[AnyRef] =
result(stackInSlot(selectedSlot) match {
case Some(stack) => robot.getInventoryStackLimit - stack.stackSize
@LuaCallback(value = "space", direct = true)
def space(context: Context, args: Arguments): Array[AnyRef] = {
val slot =
if (args.count > 0 && args.checkAny(0) != null) checkSlot(args, 0)
else selectedSlot
result(stackInSlot(slot) match {
case Some(stack) => (robot.getInventoryStackLimit min stack.getMaxStackSize) - stack.stackSize
case _ => robot.getInventoryStackLimit
})
}
@LuaCallback("compareTo")
def compareTo(context: Context, args: Arguments): Array[AnyRef] = {
@ -480,23 +488,25 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) with RobotConte
// ----------------------------------------------------------------------- //
private def blockContent(side: ForgeDirection) = {
val (bx, by, bz) = (x + side.offsetX, y + side.offsetY, z + side.offsetZ)
val id = world.getBlockId(bx, by, bz)
val block = Block.blocksList(id)
if (id == 0 || block == null || block.isAirBlock(world, bx, by, bz)) {
player.closestEntity[Entity]() match {
case Some(entity) => (true, "entity")
case _ => (false, "air")
}
}
else if (FluidRegistry.lookupFluidForBlock(block) != null || block.isInstanceOf[BlockFluid]) {
(false, "liquid")
}
else if (block.isBlockReplaceable(world, bx, by, bz)) {
(false, "replaceable")
}
else {
(true, "solid")
player.closestEntity[EntityLivingBase](side) match {
case Some(entity) =>
(true, "entity")
case _ =>
val (bx, by, bz) = (x + side.offsetX, y + side.offsetY, z + side.offsetZ)
val id = world.getBlockId(bx, by, bz)
val block = Block.blocksList(id)
if (id == 0 || block == null || block.isAirBlock(world, bx, by, bz)) {
(false, "air")
}
else if (FluidRegistry.lookupFluidForBlock(block) != null || block.isInstanceOf[BlockFluid]) {
(false, "liquid")
}
else if (block.isBlockReplaceable(world, bx, by, bz)) {
(false, "replaceable")
}
else {
(true, "solid")
}
}
}

View File

@ -72,21 +72,20 @@ private class Network private(private val data: mutable.Map[String, Network.Vert
def oldNodeB = node(nodeB)
oldNodeA.edges.find(_.isBetween(oldNodeA, oldNodeB)) match {
case Some(edge) => {
case Some(edge) =>
handleSplit(edge.remove())
if (edge.left.data.reachability == Visibility.Neighbors)
edge.right.data.onDisconnect(edge.left.data)
if (edge.right.data.reachability == Visibility.Neighbors)
edge.left.data.onDisconnect(edge.right.data)
true
}
case _ => false // That connection doesn't exists.
}
}
def remove(node: MutableNode) = {
data.remove(node.address) match {
case Some(entry) => {
case Some(entry) =>
node.network = null
val subGraphs = entry.remove()
val targets = Iterable(node) ++ (entry.data.reachability match {
@ -97,7 +96,6 @@ private class Network private(private val data: mutable.Map[String, Network.Vert
handleSplit(subGraphs)
targets.foreach(_.asInstanceOf[MutableNode].onDisconnect(node))
true
}
case _ => false
}
}

View File

@ -165,17 +165,15 @@ object LuaStateFactory {
state.pushScalaFunction(lua => {
lua.getTop match {
case 0 => lua.pushNumber(random.nextDouble())
case 1 => {
case 1 =>
val u = lua.checkInteger(1)
lua.checkArg(1, 1 < u, "interval is empty")
lua.pushInteger(1 + random.nextInt(u))
}
case 2 => {
case 2 =>
val l = lua.checkInteger(1)
val u = lua.checkInteger(2)
lua.checkArg(1, l < u, "interval is empty")
lua.pushInteger(l + random.nextInt(u - (l - 1)))
}
case _ => throw new IllegalArgumentException("wrong number of arguments")
}
1
@ -256,11 +254,10 @@ object LuaStateFactory {
Some(state)
} catch {
case ex: Throwable => {
case ex: Throwable =>
ex.printStackTrace()
state.close()
return None
}
}
}
}

View File

@ -152,11 +152,10 @@ class TextBuffer(var width: Int, var height: Int, initialDepth: PackedColor.Dept
val ol = buffer(oy)
val oc = color(oy)
for (nx <- dx0 to dx1 by sx) nx - tx match {
case ox if ox >= 0 && ox < width => {
case ox if ox >= 0 && ox < width =>
changed = changed || (nl(nx) != ol(ox)) || (nc(nx) != oc(ox))
nl(nx) = ol(ox)
nc(nx) = oc(ox)
}
case _ => /* Got no source column. */
}
case _ => /* Got no source row. */

View File

@ -223,11 +223,11 @@ opencomputers {
# The time in seconds to pause execution after an item was
# successfully dropped from a robot's inventory.
drop: 0.1
drop: 0.25
# The time in seconds to pause execution after a robot successfully
# picked up an item after triggering a suck command.
suck: 0.1
suck: 0.25
}
}