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
forge.version=11.14.3.1450
oc.version=1.5.15
oc.version=1.5.16
oc.subversion=dev
ae2.version=rv2-beta-26

View File

@ -126,10 +126,10 @@ opencomputers {
# happen.
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
# 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
# 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 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() {
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.util.ExtendedEnumFacing._
import li.cil.oc.util.ExtendedWorld._
import li.cil.oc.util.RotationHelper
import net.minecraft.block.state.IBlockState
import net.minecraft.entity.Entity
import net.minecraft.util.EnumFacing
@ -15,69 +16,9 @@ trait Rotatable extends RotationAware with internal.Rotatable {
// Lookup tables
// ----------------------------------------------------------------------- //
/**
* 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(EnumFacing.UP, EnumFacing.NORTH, EnumFacing.DOWN)
private val pitch2Direction = Array(D.up, D.north, D.down)
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
private val yaw2Direction = Array(EnumFacing.SOUTH, EnumFacing.WEST, EnumFacing.NORTH, EnumFacing.EAST)
// ----------------------------------------------------------------------- //
// Accessors
@ -148,17 +89,9 @@ trait Rotatable extends RotationAware with internal.Rotatable {
else false
}
override def toLocal(value: EnumFacing) = if (value != null) this.synchronized {
updateTranslation()
cachedTranslation(value.ordinal)
}
else null
override def toLocal(value: EnumFacing) = if (value == null) null else RotationHelper.toLocal(pitch, yaw, value)
override def toGlobal(value: EnumFacing) = if (value != null) this.synchronized {
updateTranslation()
cachedInverseTranslation(value.ordinal)
}
else null
override def toGlobal(value: EnumFacing) = if (value == null) null else RotationHelper.toGlobal(pitch, yaw, value)
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. */
protected def updateTranslation(): Unit = if (cacheDirty) {
cacheDirty = false
val newTranslation = translations(pitch.ordinal)(yaw.ordinal - 2)
if (cachedTranslation != newTranslation) {
cachedTranslation = newTranslation
cachedInverseTranslation = invert(cachedTranslation)
if (world != null) {
onRotationChanged()
}
protected def updateTranslation(): Unit = {
if (world != null) {
onRotationChanged()
}
}
@ -196,7 +122,6 @@ trait Rotatable extends RotationAware with internal.Rotatable {
def setState(newState: IBlockState): Boolean = {
if (oldState.hashCode() != newState.hashCode()) {
world.setBlockState(getPos, newState)
cacheDirty = true
true
}
else false
@ -209,7 +134,4 @@ trait Rotatable extends RotationAware with internal.Rotatable {
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
}
if (changed) {
cacheDirty = true
updateTranslation()
}
changed

View File

@ -21,6 +21,7 @@ object Mods {
val AppliedEnergistics2 = new SimpleMod(IDs.AppliedEnergistics2, version = "@[rv1,)", providesPower = true)
val BattleGear2 = new SimpleMod(IDs.BattleGear2)
val BetterRecords = new SimpleMod(IDs.BetterRecords)
val BloodMagic = new SimpleMod(IDs.BloodMagic)
val BluePower = new SimpleMod(IDs.BluePower, version = "@[0.2.928,)")
val BuildCraft = new SimpleMod(IDs.BuildCraft)
@ -39,7 +40,7 @@ object Mods {
val ElectricalAge = new SimpleMod(IDs.ElectricalAge, providesPower = true)
val EnderIO = new SimpleMod(IDs.EnderIO)
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 Forestry = new SimpleMod(IDs.Forestry)
val ForgeMultipart = new SimpleMod(IDs.ForgeMultipart)
@ -85,6 +86,7 @@ object Mods {
val Proxies = Array(
// integration.appeng.ModAppEng,
// integration.betterrecords.ModBetterRecords,
// integration.bloodmagic.ModBloodMagic,
// integration.bluepower.ModBluePower,
// integration.buildcraft.library.ModBuildCraftAPILibrary,
@ -157,6 +159,7 @@ object Mods {
object IDs {
final val AppliedEnergistics2 = "appliedenergistics2"
final val BattleGear2 = "battlegear2"
final val BetterRecords = "betterrecords"
final val BloodMagic = "AWWayofTime"
final val BluePower = "bluepowerAPI"
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.EnvironmentHost
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.server.component
import net.minecraft.item.ItemStack
@ -15,7 +15,9 @@ object DriverUpgradePiston extends Item with HostAware with EnvironmentAware {
api.Items.get(Constants.ItemName.PistonUpgrade))
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
}

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] = {
val address = args.checkString(0)
val reset = args.optBoolean(1, true)
node.network.node(address) match {
case null => result(Unit, "invalid address")
case node: Node if node.host.isInstanceOf[TextBuffer] =>
screenAddress = Option(address)
screenInstance = Some(node.host.asInstanceOf[TextBuffer])
screen(s => {
val (gmw, gmh) = maxResolution
val smw = s.getMaximumWidth
val smh = s.getMaximumHeight
s.setResolution(math.min(gmw, smw), math.min(gmh, smh))
s.setColorDepth(ColorDepth.values.apply(math.min(maxDepth.ordinal, s.getMaximumColorDepth.ordinal)))
s.setForegroundColor(0xFFFFFF)
s.setBackgroundColor(0x000000)
if (reset) {
val (gmw, gmh) = maxResolution
val smw = s.getMaximumWidth
val smh = s.getMaximumHeight
s.setResolution(math.min(gmw, smw), math.min(gmh, smh))
s.setColorDepth(ColorDepth.values.apply(math.min(maxDepth.ordinal, s.getMaximumColorDepth.ordinal)))
s.setForegroundColor(0xFFFFFF)
s.setBackgroundColor(0x000000)
}
else context.pause(0.2) // To discourage outputting "in realtime" to multiple screens using one GPU.
result(true)
})
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.api.Network
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.Callback
import li.cil.oc.api.machine.Context
import li.cil.oc.api.network.Visibility
import li.cil.oc.api.prefab
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedWorld._
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).
withComponent("piston").
withConnector().
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] = {
val hostPos = BlockPosition(host)
val blockPos = hostPos.offset(host.facing)
if (!host.world.isAirBlock(blockPos) && node.tryChangeBuffer(-Settings.get.pistonCost) && Blocks.piston.doMove(host.world, hostPos.toBlockPos, host.facing, true)) {
val side = pushDirection(args, 0)
val hostPos = pushOrigin(side)
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.playSoundEffect(host.xPosition, host.yPosition, host.zPosition, "tile.piston.out", 0.5f, host.world.rand.nextFloat() * 0.25f + 0.6f)
context.pause(0.5)
@ -32,3 +39,21 @@ class UpgradePiston(val host: Rotatable with EnvironmentHost) extends prefab.Man
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.")
def transferTo(context: Context, args: Arguments): Array[AnyRef] = {
val slot = args.checkSlot(inventory, 0)
val count = args.optionalItemCount(1)
val count = args.optItemCount(1)
if (slot == selectedSlot || count == 0) {
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.")
def drop(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
val count = args.optionalItemCount(1)
val count = args.optItemCount(1)
val stack = inventory.getStackInSlot(selectedSlot)
if (stack != null && stack.stackSize > 0) {
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.")
def suck(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
val count = args.optionalItemCount(1)
val count = args.optItemCount(1)
val blockPos = position.offset(facing)
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.""")
def dropIntoSlot(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
val count = args.optionalItemCount(2)
val count = args.optItemCount(2)
val stack = inventory.getStackInSlot(selectedSlot)
if (stack != null && stack.stackSize > 0) {
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.""")
def suckFromSlot(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
val count = args.optionalItemCount(2)
val count = args.optItemCount(2)
withInventory(facing, inventory => {
val slot = args.checkSlot(inventory, 1)
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.")
def transferFluidTo(context: Context, args: Arguments): Array[AnyRef] = {
val index = args.checkTank(tank, 0)
val count = args.optionalFluidCount(1)
val count = args.optFluidCount(1)
if (index == selectedTank || count == 0) {
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.""")
def drain(context: Context, args: Arguments): Array[AnyRef] = {
val amount = args.optionalFluidCount(0)
val amount = args.optFluidCount(0)
Option(tank.getFluidTank(selectedTank)) match {
case Some(into) => inventory.getStackInSlot(selectedSlot) match {
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.""")
def fill(context: Context, args: Arguments): Array[AnyRef] = {
val amount = args.optionalFluidCount(0)
val amount = args.optFluidCount(0)
Option(tank.getFluidTank(selectedTank)) match {
case Some(from) => inventory.getStackInSlot(selectedSlot) match {
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.")
def drain(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
val count = args.optionalFluidCount(1)
val count = args.optFluidCount(1)
getTank(selectedTank) match {
case Some(tank) =>
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.")
def fill(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
val count = args.optionalFluidCount(1)
val count = args.optFluidCount(1)
getTank(selectedTank) match {
case Some(tank) =>
val amount = math.min(count, tank.getFluidAmount)

View File

@ -12,17 +12,13 @@ object ExtendedArguments {
implicit def extendedArguments(args: Arguments): ExtendedArguments = new ExtendedArguments(args)
class ExtendedArguments(val args: Arguments) {
def optionalItemCount(n: Int) =
if (args.count > n && args.checkAny(n) != null) {
math.max(0, math.min(64, args.checkInteger(n)))
}
else 64
def optItemCount(index: Int, default: Int = 64) =
if (!isDefined(index) || !hasValue(index)) default
else math.max(0, math.min(64, args.checkInteger(index)))
def optionalFluidCount(n: Int) =
if (args.count > n && args.checkAny(n) != null) {
math.max(0, args.checkInteger(n))
}
else 1000
def optFluidCount(index: Int, default: Int = 1000) =
if (!isDefined(index) || !hasValue(index)) default
else math.max(0, args.checkInteger(index))
def checkSlot(inventory: IInventory, n: Int) = {
val slot = args.checkInteger(n) - 1
@ -32,9 +28,9 @@ object ExtendedArguments {
slot
}
def optSlot(inventory: IInventory, n: Int, default: Int) = {
if (n >= 0 && n < args.count()) checkSlot(inventory, n)
else default
def optSlot(inventory: IInventory, index: Int, default: Int) = {
if (!isDefined(index)) default
else checkSlot(inventory, index)
}
def checkTank(multi: MultiTank, n: Int) = {
@ -45,14 +41,26 @@ object ExtendedArguments {
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*) = {
val side = args.checkInteger(n)
def optSideForMovement(index: Int, default: EnumFacing) =
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) {
throw new IllegalArgumentException("invalid side")
}
@ -60,6 +68,15 @@ object ExtendedArguments {
if (allowed.isEmpty || (allowed contains direction)) direction
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 scala.collection.mutable
object RotationHelper {
def fromYaw(yaw: Float) = {
(yaw / 360 * 4).round & 3 match {
@ -11,4 +13,81 @@ object RotationHelper {
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
}
}