Merge branch 'master-MC1.7.10' of github.com:MightyPirates/OpenComputers into master-MC1.8

Conflicts:
	src/main/scala/li/cil/oc/common/item/Tablet.scala
	src/main/scala/li/cil/oc/common/tileentity/traits/Rotatable.scala
	src/main/scala/li/cil/oc/integration/Mods.scala
	src/main/scala/li/cil/oc/server/component/UpgradePiston.scala
	src/main/scala/li/cil/oc/util/ExtendedArguments.scala
This commit is contained in:
Florian Nücke 2015-08-12 19:06:28 -07:00
commit 929d9f58fe
19 changed files with 243 additions and 137 deletions

View File

@ -1,7 +1,7 @@
minecraft.version=1.8 minecraft.version=1.8
forge.version=11.14.3.1450 forge.version=11.14.3.1450
oc.version=1.5.15 oc.version=1.5.16
oc.subversion=dev oc.subversion=dev
ae2.version=rv2-beta-26 ae2.version=rv2-beta-26

View File

@ -126,10 +126,10 @@ opencomputers {
# happen. # happen.
startupDelay: 0.25 startupDelay: 0.25
# The maximum size of the byte array that can be stored on EEPROMs. # The maximum size of the byte array that can be stored on EEPROMs as executable data..
eepromSize: 4096 eepromSize: 4096
# The maximum size of the byte array that can be stored on EEPROMs. # The maximum size of the byte array that can be stored on EEPROMs as configuration data.
eepromDataSize: 256 eepromDataSize: 256
# The number of components the different CPU tiers support. This list # The number of components the different CPU tiers support. This list

View File

@ -241,9 +241,11 @@ class TabletWrapper(var stack: ItemStack, var player: EntityPlayer) extends Comp
override def facing = RotationHelper.fromYaw(player.rotationYaw) override def facing = RotationHelper.fromYaw(player.rotationYaw)
override def toLocal(value: EnumFacing) = value // -T-O-D-O- do we care? no we don't override def toLocal(value: EnumFacing) =
RotationHelper.toLocal(EnumFacing.NORTH, facing, value)
override def toGlobal(value: EnumFacing) = value // -T-O-D-O- do we care? no we don't override def toGlobal(value: EnumFacing) =
RotationHelper.toGlobal(EnumFacing.NORTH, facing, value)
def readFromNBT() { def readFromNBT() {
if (stack.hasTagCompound) { if (stack.hasTagCompound) {

View File

@ -5,6 +5,7 @@ import li.cil.oc.common.block
import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.ExtendedEnumFacing._ import li.cil.oc.util.ExtendedEnumFacing._
import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.ExtendedWorld._
import li.cil.oc.util.RotationHelper
import net.minecraft.block.state.IBlockState import net.minecraft.block.state.IBlockState
import net.minecraft.entity.Entity import net.minecraft.entity.Entity
import net.minecraft.util.EnumFacing import net.minecraft.util.EnumFacing
@ -15,69 +16,9 @@ trait Rotatable extends RotationAware with internal.Rotatable {
// Lookup tables // Lookup tables
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
/** private val pitch2Direction = Array(EnumFacing.UP, EnumFacing.NORTH, EnumFacing.DOWN)
* Translates forge directions based on the block's pitch and yaw. The base
* forward direction is facing south with no pitch. The outer array is for
* the three different pitch states, the inner for the four different yaw
* states.
*/
private val translations = Array(
// Pitch = Down
Array(
// Yaw = North
Array(D.south, D.north, D.up, D.down, D.east, D.west),
// Yaw = South
Array(D.south, D.north, D.down, D.up, D.west, D.east),
// Yaw = West
Array(D.south, D.north, D.west, D.east, D.up, D.down),
// Yaw = East
Array(D.south, D.north, D.east, D.west, D.down, D.up)),
// Pitch = Up
Array(
// Yaw = North
Array(D.north, D.south, D.down, D.up, D.east, D.west),
// Yaw = South
Array(D.north, D.south, D.up, D.down, D.west, D.east),
// Yaw = West
Array(D.north, D.south, D.west, D.east, D.down, D.up),
// Yaw = East
Array(D.north, D.south, D.east, D.west, D.up, D.down)),
// Pitch = Forward (North|East|South|West)
Array(
// Yaw = North
Array(D.down, D.up, D.south, D.north, D.east, D.west),
// Yaw = South
Array(D.down, D.up, D.north, D.south, D.west, D.east),
// Yaw = West
Array(D.down, D.up, D.west, D.east, D.south, D.north),
// Yaw = East
Array(D.down, D.up, D.east, D.west, D.north, D.south)))
private val pitch2Direction = Array(D.up, D.north, D.down) private val yaw2Direction = Array(EnumFacing.SOUTH, EnumFacing.WEST, EnumFacing.NORTH, EnumFacing.EAST)
private val yaw2Direction = Array(D.south, D.west, D.north, D.east)
/** Shortcuts for forge directions to make the above more readable. */
private object D {
val down = EnumFacing.DOWN
val up = EnumFacing.UP
val north = EnumFacing.NORTH
val south = EnumFacing.SOUTH
val west = EnumFacing.WEST
val east = EnumFacing.EAST
}
// ----------------------------------------------------------------------- //
// State
// ----------------------------------------------------------------------- //
/** Translation for facings based on current pitch and yaw. */
private var cachedTranslation: Array[EnumFacing] = null
/** Translation from local to global coordinates. */
private var cachedInverseTranslation: Array[EnumFacing] = null
protected var cacheDirty = true
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
// Accessors // Accessors
@ -148,17 +89,9 @@ trait Rotatable extends RotationAware with internal.Rotatable {
else false else false
} }
override def toLocal(value: EnumFacing) = if (value != null) this.synchronized { override def toLocal(value: EnumFacing) = if (value == null) null else RotationHelper.toLocal(pitch, yaw, value)
updateTranslation()
cachedTranslation(value.ordinal)
}
else null
override def toGlobal(value: EnumFacing) = if (value != null) this.synchronized { override def toGlobal(value: EnumFacing) = if (value == null) null else RotationHelper.toGlobal(pitch, yaw, value)
updateTranslation()
cachedInverseTranslation(value.ordinal)
}
else null
def validFacings = Array(EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.WEST, EnumFacing.EAST) def validFacings = Array(EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.WEST, EnumFacing.EAST)
@ -177,16 +110,9 @@ trait Rotatable extends RotationAware with internal.Rotatable {
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
/** Updates cached translation array and sends notification to clients. */ /** Updates cached translation array and sends notification to clients. */
protected def updateTranslation(): Unit = if (cacheDirty) { protected def updateTranslation(): Unit = {
cacheDirty = false if (world != null) {
onRotationChanged()
val newTranslation = translations(pitch.ordinal)(yaw.ordinal - 2)
if (cachedTranslation != newTranslation) {
cachedTranslation = newTranslation
cachedInverseTranslation = invert(cachedTranslation)
if (world != null) {
onRotationChanged()
}
} }
} }
@ -196,7 +122,6 @@ trait Rotatable extends RotationAware with internal.Rotatable {
def setState(newState: IBlockState): Boolean = { def setState(newState: IBlockState): Boolean = {
if (oldState.hashCode() != newState.hashCode()) { if (oldState.hashCode() != newState.hashCode()) {
world.setBlockState(getPos, newState) world.setBlockState(getPos, newState)
cacheDirty = true
true true
} }
else false else false
@ -209,7 +134,4 @@ trait Rotatable extends RotationAware with internal.Rotatable {
case _ => false case _ => false
} }
} }
private def invert(t: Array[EnumFacing]) =
t.indices.map(i => EnumFacing.getFront(t.indexOf(EnumFacing.getFront(i)))).toArray
} }

View File

@ -86,7 +86,6 @@ trait RotatableTile extends Rotatable {
_yaw = yaw _yaw = yaw
} }
if (changed) { if (changed) {
cacheDirty = true
updateTranslation() updateTranslation()
} }
changed changed

View File

@ -21,6 +21,7 @@ object Mods {
val AppliedEnergistics2 = new SimpleMod(IDs.AppliedEnergistics2, version = "@[rv1,)", providesPower = true) val AppliedEnergistics2 = new SimpleMod(IDs.AppliedEnergistics2, version = "@[rv1,)", providesPower = true)
val BattleGear2 = new SimpleMod(IDs.BattleGear2) val BattleGear2 = new SimpleMod(IDs.BattleGear2)
val BetterRecords = new SimpleMod(IDs.BetterRecords)
val BloodMagic = new SimpleMod(IDs.BloodMagic) val BloodMagic = new SimpleMod(IDs.BloodMagic)
val BluePower = new SimpleMod(IDs.BluePower, version = "@[0.2.928,)") val BluePower = new SimpleMod(IDs.BluePower, version = "@[0.2.928,)")
val BuildCraft = new SimpleMod(IDs.BuildCraft) val BuildCraft = new SimpleMod(IDs.BuildCraft)
@ -39,7 +40,7 @@ object Mods {
val ElectricalAge = new SimpleMod(IDs.ElectricalAge, providesPower = true) val ElectricalAge = new SimpleMod(IDs.ElectricalAge, providesPower = true)
val EnderIO = new SimpleMod(IDs.EnderIO) val EnderIO = new SimpleMod(IDs.EnderIO)
val EnderStorage = new SimpleMod(IDs.EnderStorage) val EnderStorage = new SimpleMod(IDs.EnderStorage)
val ExtraCells = new SimpleMod(IDs.ExtraCells, version = "@[2.2.73,)") val ExtraCells = new SimpleMod(IDs.ExtraCells, version = "@[2.2.73,)")
val Factorization = new SimpleMod(IDs.Factorization, providesPower = true) val Factorization = new SimpleMod(IDs.Factorization, providesPower = true)
val Forestry = new SimpleMod(IDs.Forestry) val Forestry = new SimpleMod(IDs.Forestry)
val ForgeMultipart = new SimpleMod(IDs.ForgeMultipart) val ForgeMultipart = new SimpleMod(IDs.ForgeMultipart)
@ -85,6 +86,7 @@ object Mods {
val Proxies = Array( val Proxies = Array(
// integration.appeng.ModAppEng, // integration.appeng.ModAppEng,
// integration.betterrecords.ModBetterRecords,
// integration.bloodmagic.ModBloodMagic, // integration.bloodmagic.ModBloodMagic,
// integration.bluepower.ModBluePower, // integration.bluepower.ModBluePower,
// integration.buildcraft.library.ModBuildCraftAPILibrary, // integration.buildcraft.library.ModBuildCraftAPILibrary,
@ -157,6 +159,7 @@ object Mods {
object IDs { object IDs {
final val AppliedEnergistics2 = "appliedenergistics2" final val AppliedEnergistics2 = "appliedenergistics2"
final val BattleGear2 = "battlegear2" final val BattleGear2 = "battlegear2"
final val BetterRecords = "betterrecords"
final val BloodMagic = "AWWayofTime" final val BloodMagic = "AWWayofTime"
final val BluePower = "bluepowerAPI" final val BluePower = "bluepowerAPI"
final val BuildCraft = "BuildCraft|Core" final val BuildCraft = "BuildCraft|Core"

View File

@ -0,0 +1,40 @@
package li.cil.oc.integration.betterrecords
import java.util
import li.cil.oc.api.driver.Converter
import li.cil.oc.util.ExtendedNBT._
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraftforge.common.util.Constants.NBT
import scala.collection.convert.WrapAsScala._
object ConverterRecord extends Converter {
final val UrlRecordClassName = "com.codingforcookies.betterrecords.src.items.ItemURLRecord"
final val UrlMultiRecordClassName = "com.codingforcookies.betterrecords.src.items.ItemURLMultiRecord"
final val FreqCrystalClassName = "com.codingforcookies.betterrecords.src.items.ItemFreqCrystal"
override def convert(value: scala.Any, output: util.Map[AnyRef, AnyRef]): Unit = value match {
case stack: ItemStack if stack.getItem != null && stack.getItem.getClass.getName == UrlRecordClassName && stack.hasTagCompound =>
convertRecord(stack.getTagCompound, output)
case stack: ItemStack if stack.getItem != null && stack.getItem.getClass.getName == UrlMultiRecordClassName && stack.hasTagCompound =>
output += "songs" -> stack.getTagCompound.getTagList("songs", NBT.TAG_COMPOUND).map((nbt: NBTTagCompound) => convertRecord(nbt, new util.HashMap[AnyRef, AnyRef]()))
case stack: ItemStack if stack.getItem != null && stack.getItem.getClass.getName == FreqCrystalClassName && stack.hasTagCompound =>
convertRecord(stack.getTagCompound, output)
case _ =>
}
private def convertRecord(nbt: NBTTagCompound, output: util.Map[AnyRef, AnyRef]) = {
if (nbt.hasKey("url", NBT.TAG_STRING))
output += "url" -> nbt.getString("url")
if (nbt.hasKey("name", NBT.TAG_STRING))
output += "filename" -> nbt.getString("name")
if (nbt.hasKey("author", NBT.TAG_STRING))
output += "author" -> nbt.getString("author")
if (nbt.hasKey("local", NBT.TAG_STRING))
output += "title" -> nbt.getString("local")
output
}
}

View File

@ -0,0 +1,13 @@
package li.cil.oc.integration.betterrecords
import li.cil.oc.api.Driver
import li.cil.oc.integration.ModProxy
import li.cil.oc.integration.Mods
object ModBetterRecords extends ModProxy {
override def getMod = Mods.BetterRecords
override def initialize() {
Driver.add(ConverterRecord)
}
}

View File

@ -5,7 +5,7 @@ import li.cil.oc.api
import li.cil.oc.api.driver.EnvironmentAware import li.cil.oc.api.driver.EnvironmentAware
import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.api.driver.EnvironmentHost
import li.cil.oc.api.driver.item.HostAware import li.cil.oc.api.driver.item.HostAware
import li.cil.oc.api.internal.Rotatable import li.cil.oc.api.internal
import li.cil.oc.common.Slot import li.cil.oc.common.Slot
import li.cil.oc.server.component import li.cil.oc.server.component
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
@ -15,7 +15,9 @@ object DriverUpgradePiston extends Item with HostAware with EnvironmentAware {
api.Items.get(Constants.ItemName.PistonUpgrade)) api.Items.get(Constants.ItemName.PistonUpgrade))
override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = host match { override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = host match {
case rotatable: Rotatable with EnvironmentHost => new component.UpgradePiston(rotatable) case host: internal.Drone => new component.UpgradePiston.Drone(host)
case host: internal.Tablet => new component.UpgradePiston.Tablet(host)
case host: internal.Rotatable with EnvironmentHost => new component.UpgradePiston.Rotatable(host)
case _ => null case _ => null
} }

View File

@ -55,22 +55,26 @@ abstract class GraphicsCard extends prefab.ManagedEnvironment {
} }
} }
@Callback(doc = """function(address:string):boolean -- Binds the GPU to the screen with the specified address.""") @Callback(doc = """function(address:string[, reset:boolean=true]):boolean -- Binds the GPU to the screen with the specified address and resets screen settings if `reset` is true.""")
def bind(context: Context, args: Arguments): Array[AnyRef] = { def bind(context: Context, args: Arguments): Array[AnyRef] = {
val address = args.checkString(0) val address = args.checkString(0)
val reset = args.optBoolean(1, true)
node.network.node(address) match { node.network.node(address) match {
case null => result(Unit, "invalid address") case null => result(Unit, "invalid address")
case node: Node if node.host.isInstanceOf[TextBuffer] => case node: Node if node.host.isInstanceOf[TextBuffer] =>
screenAddress = Option(address) screenAddress = Option(address)
screenInstance = Some(node.host.asInstanceOf[TextBuffer]) screenInstance = Some(node.host.asInstanceOf[TextBuffer])
screen(s => { screen(s => {
val (gmw, gmh) = maxResolution if (reset) {
val smw = s.getMaximumWidth val (gmw, gmh) = maxResolution
val smh = s.getMaximumHeight val smw = s.getMaximumWidth
s.setResolution(math.min(gmw, smw), math.min(gmh, smh)) val smh = s.getMaximumHeight
s.setColorDepth(ColorDepth.values.apply(math.min(maxDepth.ordinal, s.getMaximumColorDepth.ordinal))) s.setResolution(math.min(gmw, smw), math.min(gmh, smh))
s.setForegroundColor(0xFFFFFF) s.setColorDepth(ColorDepth.values.apply(math.min(maxDepth.ordinal, s.getMaximumColorDepth.ordinal)))
s.setBackgroundColor(0x000000) s.setForegroundColor(0xFFFFFF)
s.setBackgroundColor(0x000000)
}
else context.pause(0.2) // To discourage outputting "in realtime" to multiple screens using one GPU.
result(true) result(true)
}) })
case _ => result(Unit, "not a screen") case _ => result(Unit, "not a screen")

View File

@ -3,27 +3,34 @@ package li.cil.oc.server.component
import li.cil.oc.Settings import li.cil.oc.Settings
import li.cil.oc.api.Network import li.cil.oc.api.Network
import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.api.driver.EnvironmentHost
import li.cil.oc.api.internal.Rotatable import li.cil.oc.api.internal
import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Arguments
import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Callback
import li.cil.oc.api.machine.Context import li.cil.oc.api.machine.Context
import li.cil.oc.api.network.Visibility import li.cil.oc.api.network.Visibility
import li.cil.oc.api.prefab import li.cil.oc.api.prefab
import li.cil.oc.util.BlockPosition import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.ExtendedWorld._
import net.minecraft.init.Blocks import net.minecraft.init.Blocks
import net.minecraft.util.EnumFacing
class UpgradePiston(val host: Rotatable with EnvironmentHost) extends prefab.ManagedEnvironment { abstract class UpgradePiston(val host: EnvironmentHost) extends prefab.ManagedEnvironment {
override val node = Network.newNode(this, Visibility.Network). override val node = Network.newNode(this, Visibility.Network).
withComponent("piston"). withComponent("piston").
withConnector(). withConnector().
create() create()
@Callback(doc = """function():boolean -- Tries to push the block in front of the container of the upgrade.""") def pushDirection(args: Arguments, index: Int): EnumFacing
def pushOrigin(side: EnumFacing) = BlockPosition(host)
@Callback(doc = """function([side:number]):boolean -- Tries to push the block on the specified side of the container of the upgrade. Defaults to front.""")
def push(context: Context, args: Arguments): Array[AnyRef] = { def push(context: Context, args: Arguments): Array[AnyRef] = {
val hostPos = BlockPosition(host) val side = pushDirection(args, 0)
val blockPos = hostPos.offset(host.facing) val hostPos = pushOrigin(side)
if (!host.world.isAirBlock(blockPos) && node.tryChangeBuffer(-Settings.get.pistonCost) && Blocks.piston.doMove(host.world, hostPos.toBlockPos, host.facing, true)) { val blockPos = hostPos.offset(side)
if (!host.world.isAirBlock(blockPos) && node.tryChangeBuffer(-Settings.get.pistonCost) && Blocks.piston.doMove(host.world, hostPos.toBlockPos, side, true)) {
host.world.setBlockToAir(blockPos) host.world.setBlockToAir(blockPos)
host.world.playSoundEffect(host.xPosition, host.yPosition, host.zPosition, "tile.piston.out", 0.5f, host.world.rand.nextFloat() * 0.25f + 0.6f) host.world.playSoundEffect(host.xPosition, host.yPosition, host.zPosition, "tile.piston.out", 0.5f, host.world.rand.nextFloat() * 0.25f + 0.6f)
context.pause(0.5) context.pause(0.5)
@ -32,3 +39,21 @@ class UpgradePiston(val host: Rotatable with EnvironmentHost) extends prefab.Man
else result(false) else result(false)
} }
} }
object UpgradePiston {
class Drone(drone: internal.Drone) extends UpgradePiston(drone) {
override def pushDirection(args: Arguments, index: Int) = args.optSide(index, EnumFacing.SOUTH, EnumFacing.values: _*)
}
class Tablet(tablet: internal.Tablet) extends Rotatable(tablet) {
override def pushOrigin(side: EnumFacing) =
if (side == EnumFacing.DOWN && tablet.player.getEyeHeight > 1) super.pushOrigin(side).offset(EnumFacing.DOWN)
else super.pushOrigin(side)
}
class Rotatable(val rotatable: internal.Rotatable with EnvironmentHost) extends UpgradePiston(rotatable) {
override def pushDirection(args: Arguments, index: Int) = rotatable.toGlobal(args.optSideForAction(index, EnumFacing.SOUTH))
}
}

View File

@ -50,7 +50,7 @@ trait InventoryControl extends InventoryAware {
@Callback(doc = "function(toSlot:number[, amount:number]):boolean -- Move up to the specified amount of items from the selected slot into the specified slot.") @Callback(doc = "function(toSlot:number[, amount:number]):boolean -- Move up to the specified amount of items from the selected slot into the specified slot.")
def transferTo(context: Context, args: Arguments): Array[AnyRef] = { def transferTo(context: Context, args: Arguments): Array[AnyRef] = {
val slot = args.checkSlot(inventory, 0) val slot = args.checkSlot(inventory, 0)
val count = args.optionalItemCount(1) val count = args.optItemCount(1)
if (slot == selectedSlot || count == 0) { if (slot == selectedSlot || count == 0) {
result(true) result(true)
} }

View File

@ -33,7 +33,7 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
@Callback(doc = "function(side:number[, count:number=64]):boolean -- Drops items from the selected slot towards the specified side.") @Callback(doc = "function(side:number[, count:number=64]):boolean -- Drops items from the selected slot towards the specified side.")
def drop(context: Context, args: Arguments): Array[AnyRef] = { def drop(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0) val facing = checkSideForAction(args, 0)
val count = args.optionalItemCount(1) val count = args.optItemCount(1)
val stack = inventory.getStackInSlot(selectedSlot) val stack = inventory.getStackInSlot(selectedSlot)
if (stack != null && stack.stackSize > 0) { if (stack != null && stack.stackSize > 0) {
val blockPos = position.offset(facing) val blockPos = position.offset(facing)
@ -69,7 +69,7 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
@Callback(doc = "function(side:number[, count:number=64]):boolean -- Suck up items from the specified side.") @Callback(doc = "function(side:number[, count:number=64]):boolean -- Suck up items from the specified side.")
def suck(context: Context, args: Arguments): Array[AnyRef] = { def suck(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0) val facing = checkSideForAction(args, 0)
val count = args.optionalItemCount(1) val count = args.optItemCount(1)
val blockPos = position.offset(facing) val blockPos = position.offset(facing)
if (InventoryUtils.inventoryAt(blockPos).exists(inventory => { if (InventoryUtils.inventoryAt(blockPos).exists(inventory => {

View File

@ -14,7 +14,7 @@ trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideR
@Callback(doc = """function(facing:number, slot:number[, count:number]):boolean -- Drops the selected item stack into the specified slot of an inventory.""") @Callback(doc = """function(facing:number, slot:number[, count:number]):boolean -- Drops the selected item stack into the specified slot of an inventory.""")
def dropIntoSlot(context: Context, args: Arguments): Array[AnyRef] = { def dropIntoSlot(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0) val facing = checkSideForAction(args, 0)
val count = args.optionalItemCount(2) val count = args.optItemCount(2)
val stack = inventory.getStackInSlot(selectedSlot) val stack = inventory.getStackInSlot(selectedSlot)
if (stack != null && stack.stackSize > 0) { if (stack != null && stack.stackSize > 0) {
withInventory(facing, inventory => { withInventory(facing, inventory => {
@ -43,7 +43,7 @@ trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideR
@Callback(doc = """function(facing:number, slot:number[, count:number]):boolean -- Sucks items from the specified slot of an inventory.""") @Callback(doc = """function(facing:number, slot:number[, count:number]):boolean -- Sucks items from the specified slot of an inventory.""")
def suckFromSlot(context: Context, args: Arguments): Array[AnyRef] = { def suckFromSlot(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0) val facing = checkSideForAction(args, 0)
val count = args.optionalItemCount(2) val count = args.optItemCount(2)
withInventory(facing, inventory => { withInventory(facing, inventory => {
val slot = args.checkSlot(inventory, 1) val slot = args.checkSlot(inventory, 1)
if (InventoryUtils.extractFromInventorySlot(InventoryUtils.insertIntoInventory(_, this.inventory, slots = Option(insertionSlots)), inventory, facing.getOpposite, slot, count)) { if (InventoryUtils.extractFromInventorySlot(InventoryUtils.insertIntoInventory(_, this.inventory, slots = Option(insertionSlots)), inventory, facing.getOpposite, slot, count)) {

View File

@ -53,7 +53,7 @@ trait TankControl extends TankAware {
@Callback(doc = "function(index:number[, count:number=1000]):boolean -- Move the specified amount of fluid from the selected tank into the specified tank.") @Callback(doc = "function(index:number[, count:number=1000]):boolean -- Move the specified amount of fluid from the selected tank into the specified tank.")
def transferFluidTo(context: Context, args: Arguments): Array[AnyRef] = { def transferFluidTo(context: Context, args: Arguments): Array[AnyRef] = {
val index = args.checkTank(tank, 0) val index = args.checkTank(tank, 0)
val count = args.optionalFluidCount(1) val count = args.optFluidCount(1)
if (index == selectedTank || count == 0) { if (index == selectedTank || count == 0) {
result(true) result(true)
} }

View File

@ -35,7 +35,7 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware
@Callback(doc = """function([amount:number]):boolean -- Transfers fluid from a tank in the selected inventory slot to the selected tank.""") @Callback(doc = """function([amount:number]):boolean -- Transfers fluid from a tank in the selected inventory slot to the selected tank.""")
def drain(context: Context, args: Arguments): Array[AnyRef] = { def drain(context: Context, args: Arguments): Array[AnyRef] = {
val amount = args.optionalFluidCount(0) val amount = args.optFluidCount(0)
Option(tank.getFluidTank(selectedTank)) match { Option(tank.getFluidTank(selectedTank)) match {
case Some(into) => inventory.getStackInSlot(selectedSlot) match { case Some(into) => inventory.getStackInSlot(selectedSlot) match {
case stack: ItemStack => case stack: ItemStack =>
@ -77,7 +77,7 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware
@Callback(doc = """function([amount:number]):boolean -- Transfers fluid from the selected tank to a tank in the selected inventory slot.""") @Callback(doc = """function([amount:number]):boolean -- Transfers fluid from the selected tank to a tank in the selected inventory slot.""")
def fill(context: Context, args: Arguments): Array[AnyRef] = { def fill(context: Context, args: Arguments): Array[AnyRef] = {
val amount = args.optionalFluidCount(0) val amount = args.optFluidCount(0)
Option(tank.getFluidTank(selectedTank)) match { Option(tank.getFluidTank(selectedTank)) match {
case Some(from) => inventory.getStackInSlot(selectedSlot) match { case Some(from) => inventory.getStackInSlot(selectedSlot) match {
case stack: ItemStack => case stack: ItemStack =>

View File

@ -36,7 +36,7 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted {
@Callback(doc = "function(side:boolean[, amount:number=1000]):boolean, number or string -- Drains the specified amount of fluid from the specified side. Returns the amount drained, or an error message.") @Callback(doc = "function(side:boolean[, amount:number=1000]):boolean, number or string -- Drains the specified amount of fluid from the specified side. Returns the amount drained, or an error message.")
def drain(context: Context, args: Arguments): Array[AnyRef] = { def drain(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0) val facing = checkSideForAction(args, 0)
val count = args.optionalFluidCount(1) val count = args.optFluidCount(1)
getTank(selectedTank) match { getTank(selectedTank) match {
case Some(tank) => case Some(tank) =>
val space = tank.getCapacity - tank.getFluidAmount val space = tank.getCapacity - tank.getFluidAmount
@ -97,7 +97,7 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted {
@Callback(doc = "function(side:number[, amount:number=1000]):boolean, number of string -- Eject the specified amount of fluid to the specified side. Returns the amount ejected or an error message.") @Callback(doc = "function(side:number[, amount:number=1000]):boolean, number of string -- Eject the specified amount of fluid to the specified side. Returns the amount ejected or an error message.")
def fill(context: Context, args: Arguments): Array[AnyRef] = { def fill(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0) val facing = checkSideForAction(args, 0)
val count = args.optionalFluidCount(1) val count = args.optFluidCount(1)
getTank(selectedTank) match { getTank(selectedTank) match {
case Some(tank) => case Some(tank) =>
val amount = math.min(count, tank.getFluidAmount) val amount = math.min(count, tank.getFluidAmount)

View File

@ -12,17 +12,13 @@ object ExtendedArguments {
implicit def extendedArguments(args: Arguments): ExtendedArguments = new ExtendedArguments(args) implicit def extendedArguments(args: Arguments): ExtendedArguments = new ExtendedArguments(args)
class ExtendedArguments(val args: Arguments) { class ExtendedArguments(val args: Arguments) {
def optionalItemCount(n: Int) = def optItemCount(index: Int, default: Int = 64) =
if (args.count > n && args.checkAny(n) != null) { if (!isDefined(index) || !hasValue(index)) default
math.max(0, math.min(64, args.checkInteger(n))) else math.max(0, math.min(64, args.checkInteger(index)))
}
else 64
def optionalFluidCount(n: Int) = def optFluidCount(index: Int, default: Int = 1000) =
if (args.count > n && args.checkAny(n) != null) { if (!isDefined(index) || !hasValue(index)) default
math.max(0, args.checkInteger(n)) else math.max(0, args.checkInteger(index))
}
else 1000
def checkSlot(inventory: IInventory, n: Int) = { def checkSlot(inventory: IInventory, n: Int) = {
val slot = args.checkInteger(n) - 1 val slot = args.checkInteger(n) - 1
@ -32,9 +28,9 @@ object ExtendedArguments {
slot slot
} }
def optSlot(inventory: IInventory, n: Int, default: Int) = { def optSlot(inventory: IInventory, index: Int, default: Int) = {
if (n >= 0 && n < args.count()) checkSlot(inventory, n) if (!isDefined(index)) default
else default else checkSlot(inventory, index)
} }
def checkTank(multi: MultiTank, n: Int) = { def checkTank(multi: MultiTank, n: Int) = {
@ -45,14 +41,26 @@ object ExtendedArguments {
tank tank
} }
def checkSideForAction(n: Int) = checkSide(n, EnumFacing.SOUTH, EnumFacing.UP, EnumFacing.DOWN) def checkSideForAction(index: Int) = checkSide(index, EnumFacing.SOUTH, EnumFacing.UP, EnumFacing.DOWN)
def checkSideForMovement(n: Int) = checkSide(n, EnumFacing.SOUTH, EnumFacing.NORTH, EnumFacing.UP, EnumFacing.DOWN) def optSideForAction(index: Int, default: EnumFacing) =
if (!isDefined(index)) default
else checkSideForAction(index)
def checkSideForFace(n: Int, facing: EnumFacing) = checkSide(n, EnumFacing.values.filter(_ != facing.getOpposite): _*) def checkSideForMovement(index: Int) = checkSide(index, EnumFacing.SOUTH, EnumFacing.NORTH, EnumFacing.UP, EnumFacing.DOWN)
def checkSide(n: Int, allowed: EnumFacing*) = { def optSideForMovement(index: Int, default: EnumFacing) =
val side = args.checkInteger(n) if (!isDefined(index)) default
else checkSideForMovement(index)
def checkSideForFace(index: Int, facing: EnumFacing) = checkSide(index, EnumFacing.values.filter(_ != facing.getOpposite): _*)
def optSideForFace(index: Int, default: EnumFacing) =
if (!isDefined(index)) default
else checkSideForAction(index)
def checkSide(index: Int, allowed: EnumFacing*) = {
val side = args.checkInteger(index)
if (side < 0 || side > 5) { if (side < 0 || side > 5) {
throw new IllegalArgumentException("invalid side") throw new IllegalArgumentException("invalid side")
} }
@ -60,6 +68,15 @@ object ExtendedArguments {
if (allowed.isEmpty || (allowed contains direction)) direction if (allowed.isEmpty || (allowed contains direction)) direction
else throw new IllegalArgumentException("unsupported side") else throw new IllegalArgumentException("unsupported side")
} }
def optSide(index: Int, default: EnumFacing, allowed: EnumFacing*) = {
if (!isDefined(index)) default
else checkSide(index, allowed: _*)
}
private def isDefined(index: Int) = index >= 0 && index < args.count()
private def hasValue(index: Int) = args.checkAny(index) != null
} }
} }

View File

@ -2,6 +2,8 @@ package li.cil.oc.util
import net.minecraft.util.EnumFacing import net.minecraft.util.EnumFacing
import scala.collection.mutable
object RotationHelper { object RotationHelper {
def fromYaw(yaw: Float) = { def fromYaw(yaw: Float) = {
(yaw / 360 * 4).round & 3 match { (yaw / 360 * 4).round & 3 match {
@ -11,4 +13,81 @@ object RotationHelper {
case 3 => EnumFacing.EAST case 3 => EnumFacing.EAST
} }
} }
def toLocal(pitch: EnumFacing, yaw: EnumFacing, value: EnumFacing) =
translationFor(pitch, yaw)(value.ordinal)
def toGlobal(pitch: EnumFacing, yaw: EnumFacing, value: EnumFacing) =
inverseTranslationFor(pitch, yaw)(value.ordinal)
def translationFor(pitch: EnumFacing, yaw: EnumFacing) =
translationCache.
getOrElseUpdate(pitch, mutable.Map.empty).
getOrElseUpdate(yaw, translations(pitch.ordinal)(yaw.ordinal - 2))
def inverseTranslationFor(pitch: EnumFacing, yaw: EnumFacing) =
inverseTranslationCache.
getOrElseUpdate(pitch, mutable.Map.empty).
getOrElseUpdate(yaw, {
val t = translationFor(pitch, yaw)
t.indices.
map(EnumFacing.getFront).
map(t.indexOf).
map(EnumFacing.getFront).
toArray
})
// ----------------------------------------------------------------------- //
private val translationCache = mutable.Map.empty[EnumFacing, mutable.Map[EnumFacing, Array[EnumFacing]]]
private val inverseTranslationCache = mutable.Map.empty[EnumFacing, mutable.Map[EnumFacing, Array[EnumFacing]]]
/**
* Translates forge directions based on the block's pitch and yaw. The base
* forward direction is facing south with no pitch. The outer array is for
* the three different pitch states, the inner for the four different yaw
* states.
*/
private val translations = Array(
// Pitch = Down
Array(
// Yaw = North
Array(D.south, D.north, D.up, D.down, D.east, D.west),
// Yaw = South
Array(D.south, D.north, D.down, D.up, D.west, D.east),
// Yaw = West
Array(D.south, D.north, D.west, D.east, D.up, D.down),
// Yaw = East
Array(D.south, D.north, D.east, D.west, D.down, D.up)),
// Pitch = Up
Array(
// Yaw = North
Array(D.north, D.south, D.down, D.up, D.east, D.west),
// Yaw = South
Array(D.north, D.south, D.up, D.down, D.west, D.east),
// Yaw = West
Array(D.north, D.south, D.west, D.east, D.down, D.up),
// Yaw = East
Array(D.north, D.south, D.east, D.west, D.up, D.down)),
// Pitch = Forward (North|East|South|West)
Array(
// Yaw = North
Array(D.down, D.up, D.south, D.north, D.east, D.west),
// Yaw = South
Array(D.down, D.up, D.north, D.south, D.west, D.east),
// Yaw = West
Array(D.down, D.up, D.west, D.east, D.south, D.north),
// Yaw = East
Array(D.down, D.up, D.east, D.west, D.north, D.south)))
/** Shortcuts for forge directions to make the above more readable. */
private object D {
val down = EnumFacing.DOWN
val up = EnumFacing.UP
val north = EnumFacing.NORTH
val south = EnumFacing.SOUTH
val west = EnumFacing.WEST
val east = EnumFacing.EAST
}
} }