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 -- Inventory
function robot.select(index) function robot.select(slot)
return component.computer.select(index) return component.computer.select(slot)
end end
function robot.count() function robot.count(slot)
return component.computer.count() return component.computer.count(slot)
end end
function robot.space() function robot.space(slot)
return component.computer.select() return component.computer.space(slot)
end end
function robot.compareTo(index) function robot.compareTo(slot)
return component.computer.compareTo(index) return component.computer.compareTo(slot)
end end
function robot.transferTo(index, count) function robot.transferTo(slot, count)
return component.computer.transferTo(index, count) return component.computer.transferTo(slot, count)
end end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------

View File

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

View File

@ -16,7 +16,6 @@ object Recipes {
val craftingTable = new ItemStack(Block.workbench) val craftingTable = new ItemStack(Block.workbench)
val diamond = new ItemStack(Item.diamond) val diamond = new ItemStack(Item.diamond)
val dispenser = new ItemStack(Block.dispenser) val dispenser = new ItemStack(Block.dispenser)
val dropper = new ItemStack(Block.dropper)
val emerald = new ItemStack(Item.emerald) val emerald = new ItemStack(Item.emerald)
val enderPearl = new ItemStack(Item.enderPearl) val enderPearl = new ItemStack(Item.enderPearl)
val glass = new ItemStack(Block.glass) val glass = new ItemStack(Block.glass)
@ -461,11 +460,11 @@ object Recipes {
'b', pcb) 'b', pcb)
addRecipe(Items.crafting.createItemStack(), addRecipe(Items.crafting.createItemStack(),
"idi", "ipi",
"cwc", "cwc",
"ibi", "ibi",
'i', ironIngot, 'i', ironIngot,
'd', dropper, 'p', piston,
'c', chip1, 'c', chip1,
'w', craftingTable, 'w', craftingTable,
'b', pcb) '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 * robot's current position and rotation in the world. Use this to trigger
* events involving the robot that require a player entity, and for * events involving the robot that require a player entity, and for
* interacting with the robots' inventory. * 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. * @return the fake player for the robot.
*/ */

View File

@ -3,7 +3,7 @@
* <p/> * <p/>
* There are several parts to this API: * There are several parts to this API:
* <dl> * <dl>
* <dt>The {@link Driver} API</dt> * <dt>The {@link li.cil.oc.api.Driver} API</dt>
* <dd> * <dd>
* This API is used to provide glue code to the mod that allows it to interact * 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 * 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 * {@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. * in a computer, however, you will always have to provide a driver.
* </dd> * </dd>
* <dt>The {@link FileSystem} API</dt> * <dt>The {@link li.cil.oc.api.FileSystem} API</dt>
* <dd> * <dd>
* This API provides facilities that make it easier to create file systems that * 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 * can be interacted with from Lua programs via the file system driver that
* comes with the mod. * comes with the mod.
* </dd> * </dd>
* <dt>The {@link Network} API</dt> * <dt>The {@link li.cil.oc.api.Network} API</dt>
* <dd> * <dd>
* This API provides interfaces that allow interacting with the internal network * This API provides interfaces that allow interacting with the internal network
* and creating nodes, components and power connectors for said network. If you * 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 { items.zipWithIndex collect {
case (Some(stack), slot) => (stack, slot) case (Some(stack), slot) => (stack, slot)
} map { } map {
case (stack, slot) => { case (stack, slot) =>
val slotNbt = new NBTTagCompound() val slotNbt = new NBTTagCompound()
slotNbt.setByte("slot", slot.toByte) slotNbt.setByte("slot", slot.toByte)
slotNbt.setNewCompoundTag("item", stack.writeToNBT) 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") node.sendToReachable("computer.stopped")
start() start()
// Resume from pauses based on sleep or signal underflow. // 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) switchTo(Computer.State.Yielded)
}
// Resume in case we paused because the game was paused. // Resume in case we paused because the game was paused.
case Computer.State.Paused => case Computer.State.Paused =>
if (remainingPause > 0) { if (remainingPause > 0) {
@ -274,7 +273,7 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
switchTo(state.top) // Trigger execution if necessary. switchTo(state.top) // Trigger execution if necessary.
} }
// Perform a synchronized call (message sending). // Perform a synchronized call (message sending).
case Computer.State.SynchronizedCall => { case Computer.State.SynchronizedCall =>
// These three asserts are all guaranteed by run(). // These three asserts are all guaranteed by run().
assert(lua.getTop == 2) assert(lua.getTop == 2)
assert(lua.isThread(1)) 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) OpenComputers.log.log(Level.WARNING, "Faulty Lua implementation for synchronized calls.", e)
crash("protocol error") crash("protocol error")
} }
}
case _ => // Nothing special to do, just avoid match errors. 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. // Delay execution for a second to allow the world around us to settle.
pause(Settings.get.startupDelay) pause(Settings.get.startupDelay)
} catch { } catch {
case e: LuaRuntimeException => { case e: LuaRuntimeException =>
OpenComputers.log.warning("Could not unpersist computer.\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat ")) OpenComputers.log.warning("Could not unpersist computer.\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat "))
state.push(Computer.State.Stopping) state.push(Computer.State.Stopping)
}
} }
} }
else close() // Clean up in case we got a weird state stack. 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) nbt.setInteger("remainingPause", remainingPause)
message.foreach(nbt.setString("message", _)) message.foreach(nbt.setString("message", _))
} catch { } catch {
case e: LuaRuntimeException => { case e: LuaRuntimeException =>
OpenComputers.log.warning("Could not persist computer.\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat ")) OpenComputers.log.warning("Could not persist computer.\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat "))
nbt.removeTag("state") nbt.removeTag("state")
}
} }
// Limit memory again. // Limit memory again.
@ -639,16 +635,14 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
lua.newTable() lua.newTable()
var count = 0 var count = 0
value.foreach { value.foreach {
case (x, index) => { case (x, index) => x match {
x match { case (entry: ScalaNumber) =>
case (entry: ScalaNumber) => pushResult(lua, entry.underlying())
pushResult(lua, entry.underlying()) case (entry) =>
case (entry) => pushResult(lua, entry.asInstanceOf[AnyRef])
pushResult(lua, entry.asInstanceOf[AnyRef]) }
}
lua.rawSet(-2, index + 1) lua.rawSet(-2, index + 1)
count = count + 1 count = count + 1
}
} }
lua.pushString("n") lua.pushString("n")
lua.pushInteger(count) lua.pushInteger(count)
@ -1045,10 +1039,9 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
return true return true
} }
catch { catch {
case ex: Throwable => { case ex: Throwable =>
OpenComputers.log.log(Level.WARNING, "Failed initializing computer.", ex) OpenComputers.log.log(Level.WARNING, "Failed initializing computer.", ex)
close() close()
}
} }
false false
} }

View File

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

View File

@ -50,7 +50,7 @@ abstract class GraphicsCard extends ManagedComponent {
val address = args.checkString(0) val address = args.checkString(0)
node.network.node(address) match { node.network.node(address) match {
case null => Array(Unit, "invalid address") 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) screenAddress = Option(address)
screenInstance = Some(node.host.asInstanceOf[Buffer]) screenInstance = Some(node.host.asInstanceOf[Buffer])
screen(s => { screen(s => {
@ -62,7 +62,6 @@ abstract class GraphicsCard extends ManagedComponent {
s.background = 0x000000 s.background = 0x000000
result(true) result(true)
}) })
}
case _ => Array(Unit, "not a screen") 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.entity.{EntityLivingBase, Entity}
import net.minecraft.inventory.{IInventory, ISidedInventory} import net.minecraft.inventory.{IInventory, ISidedInventory}
import net.minecraft.item.{ItemStack, ItemBlock} 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.common.ForgeDirection
import net.minecraftforge.fluids.FluidRegistry import net.minecraftforge.fluids.FluidRegistry
import scala.Some import scala.Some
@ -50,19 +50,27 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) with RobotConte
result(selectedSlot - actualSlot(0) + 1) result(selectedSlot - actualSlot(0) + 1)
} }
@LuaCallback("count") @LuaCallback(value = "count", direct = true)
def count(context: Context, args: Arguments): Array[AnyRef] = def count(context: Context, args: Arguments): Array[AnyRef] = {
result(stackInSlot(selectedSlot) match { 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 Some(stack) => stack.stackSize
case _ => 0 case _ => 0
}) })
}
@LuaCallback("space") @LuaCallback(value = "space", direct = true)
def space(context: Context, args: Arguments): Array[AnyRef] = def space(context: Context, args: Arguments): Array[AnyRef] = {
result(stackInSlot(selectedSlot) match { val slot =
case Some(stack) => robot.getInventoryStackLimit - stack.stackSize 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 case _ => robot.getInventoryStackLimit
}) })
}
@LuaCallback("compareTo") @LuaCallback("compareTo")
def compareTo(context: Context, args: Arguments): Array[AnyRef] = { 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) = { private def blockContent(side: ForgeDirection) = {
val (bx, by, bz) = (x + side.offsetX, y + side.offsetY, z + side.offsetZ) player.closestEntity[EntityLivingBase](side) match {
val id = world.getBlockId(bx, by, bz) case Some(entity) =>
val block = Block.blocksList(id) (true, "entity")
if (id == 0 || block == null || block.isAirBlock(world, bx, by, bz)) { case _ =>
player.closestEntity[Entity]() match { val (bx, by, bz) = (x + side.offsetX, y + side.offsetY, z + side.offsetZ)
case Some(entity) => (true, "entity") val id = world.getBlockId(bx, by, bz)
case _ => (false, "air") 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 (FluidRegistry.lookupFluidForBlock(block) != null || block.isInstanceOf[BlockFluid]) {
} (false, "liquid")
else if (block.isBlockReplaceable(world, bx, by, bz)) { }
(false, "replaceable") else if (block.isBlockReplaceable(world, bx, by, bz)) {
} (false, "replaceable")
else { }
(true, "solid") 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) def oldNodeB = node(nodeB)
oldNodeA.edges.find(_.isBetween(oldNodeA, oldNodeB)) match { oldNodeA.edges.find(_.isBetween(oldNodeA, oldNodeB)) match {
case Some(edge) => { case Some(edge) =>
handleSplit(edge.remove()) handleSplit(edge.remove())
if (edge.left.data.reachability == Visibility.Neighbors) if (edge.left.data.reachability == Visibility.Neighbors)
edge.right.data.onDisconnect(edge.left.data) edge.right.data.onDisconnect(edge.left.data)
if (edge.right.data.reachability == Visibility.Neighbors) if (edge.right.data.reachability == Visibility.Neighbors)
edge.left.data.onDisconnect(edge.right.data) edge.left.data.onDisconnect(edge.right.data)
true true
}
case _ => false // That connection doesn't exists. case _ => false // That connection doesn't exists.
} }
} }
def remove(node: MutableNode) = { def remove(node: MutableNode) = {
data.remove(node.address) match { data.remove(node.address) match {
case Some(entry) => { case Some(entry) =>
node.network = null node.network = null
val subGraphs = entry.remove() val subGraphs = entry.remove()
val targets = Iterable(node) ++ (entry.data.reachability match { 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) handleSplit(subGraphs)
targets.foreach(_.asInstanceOf[MutableNode].onDisconnect(node)) targets.foreach(_.asInstanceOf[MutableNode].onDisconnect(node))
true true
}
case _ => false case _ => false
} }
} }

View File

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

View File

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

View File

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