Skip to content
Permalink
Branch: master-MC1.7.10
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
358 lines (296 sloc) 14.4 KB
package li.cil.oc.common.tileentity
import java.util
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import li.cil.oc.Constants
import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute
import li.cil.oc.api.driver.DeviceInfo.DeviceClass
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.driver.DeviceInfo
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._
import li.cil.oc.common.item.data.PrintData
import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.ExtendedNBT._
import net.minecraft.inventory.ISidedInventory
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.AxisAlignedBB
import net.minecraftforge.common.util.ForgeDirection
import scala.collection.convert.WrapAsJava._
class Printer extends traits.Environment with traits.Inventory with traits.Rotatable with SidedEnvironment with traits.StateAware with ISidedInventory with DeviceInfo {
val node = api.Network.newNode(this, Visibility.Network).
withComponent("printer3d").
withConnector(Settings.get.bufferConverter).
create()
val maxAmountMaterial = 256000
var amountMaterial = 0
val maxAmountInk = 100000
var amountInk = 0
var data = new PrintData()
var isActive = false
var limit = 0
var output: Option[ItemStack] = None
var totalRequiredEnergy = 0.0
var requiredEnergy = 0.0
val slotMaterial = 0
val slotInk = 1
val slotOutput = 2
private final lazy val deviceInfo = Map(
DeviceAttribute.Class -> DeviceClass.Printer,
DeviceAttribute.Description -> "3D Printer",
DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor,
DeviceAttribute.Product -> "Omni-Materializer T6.1"
)
override def getDeviceInfo: util.Map[String, String] = deviceInfo
// ----------------------------------------------------------------------- //
@SideOnly(Side.CLIENT)
override def canConnect(side: ForgeDirection) = side != ForgeDirection.UP
override def sidedNode(side: ForgeDirection) = if (side != ForgeDirection.UP) node else null
override def getCurrentState = {
if (isPrinting) util.EnumSet.of(api.util.StateAware.State.IsWorking)
else if (canPrint) util.EnumSet.of(api.util.StateAware.State.CanWork)
else util.EnumSet.noneOf(classOf[api.util.StateAware.State])
}
// ----------------------------------------------------------------------- //
def canPrint = data.stateOff.nonEmpty && data.stateOff.size <= Settings.get.maxPrintComplexity && data.stateOn.size <= Settings.get.maxPrintComplexity
def isPrinting = output.isDefined
def progress = (1 - requiredEnergy / totalRequiredEnergy) * 100
def timeRemaining = (requiredEnergy / Settings.get.assemblerTickAmount / 20).toInt
// ----------------------------------------------------------------------- //
@Callback(doc = """function() -- Resets the configuration of the printer and stop printing (current job will finish).""")
def reset(context: Context, args: Arguments): Array[Object] = {
data = new PrintData()
isActive = false // Needs committing.
null
}
@Callback(doc = """function(value:string) -- Set a label for the block being printed.""")
def setLabel(context: Context, args: Arguments): Array[Object] = {
data.label = Option(args.optString(0, null)).map(_.take(24))
if (data.label.fold(false)(_.isEmpty)) data.label = None
isActive = false // Needs committing.
null
}
@Callback(doc = """function():string -- Get the current label for the block being printed.""")
def getLabel(context: Context, args: Arguments): Array[Object] = {
result(data.label.orNull)
}
@Callback(doc = """function(value:string) -- Set a tooltip for the block being printed.""")
def setTooltip(context: Context, args: Arguments): Array[Object] = {
data.tooltip = Option(args.optString(0, null)).map(_.take(128))
if (data.tooltip.fold(false)(_.isEmpty)) data.tooltip = None
isActive = false // Needs committing.
null
}
@Callback(doc = """function():string -- Get the current tooltip for the block being printed.""")
def getTooltip(context: Context, args: Arguments): Array[Object] = {
result(data.tooltip.orNull)
}
@Callback(doc = """function(value:number) -- Set what light level the printed block should have.""")
def setLightLevel(context: Context, args: Arguments): Array[Object] = {
data.lightLevel = args.checkInteger(0) max 0 min Settings.get.maxPrintLightLevel
isActive = false // Needs committing.
null
}
@Callback(doc = """function():number -- Get which light level the printed block should have.""")
def getLightLevel(context: Context, args: Arguments): Array[Object] = {
result(data.lightLevel)
}
@Callback(doc = """function(value:boolean or number) -- Set whether the printed block should emit redstone when in its active state.""")
def setRedstoneEmitter(context: Context, args: Arguments): Array[Object] = {
if (args.isBoolean(0)) data.redstoneLevel = if (args.checkBoolean(0)) 15 else 0
else data.redstoneLevel = args.checkInteger(0) max 0 min 15
isActive = false // Needs committing.
null
}
@Callback(doc = """function():boolean, number -- Get whether the printed block should emit redstone when in its active state.""")
def isRedstoneEmitter(context: Context, args: Arguments): Array[Object] = {
result(data.emitRedstone, data.redstoneLevel)
}
@Callback(doc = """function(value:boolean) -- Set whether the printed block should automatically return to its off state.""")
def setButtonMode(context: Context, args: Arguments): Array[Object] = {
data.isButtonMode = args.checkBoolean(0)
isActive = false // Needs committing.
null
}
@Callback(doc = """function():boolean -- Get whether the printed block should automatically return to its off state.""")
def isButtonMode(context: Context, args: Arguments): Array[Object] = {
result(data.isButtonMode)
}
@Callback(doc = """function(collideOff:boolean, collideOn:boolean) -- Set whether the printed block should be collidable or not.""")
def setCollidable(context: Context, args: Arguments): Array[Object] = {
val (collideOff, collideOn) = (args.checkBoolean(0), args.checkBoolean(1))
data.noclipOff = !collideOff
data.noclipOn = !collideOn
null
}
@Callback(doc = """function():boolean, boolean -- Get whether the printed block should be collidable or not.""")
def isCollidable(context: Context, args: Arguments): Array[Object] = {
result(!data.noclipOff, !data.noclipOn)
}
@Callback(doc = """function(minX:number, minY:number, minZ:number, maxX:number, maxY:number, maxZ:number, texture:string[, state:boolean=false][,tint:number]) -- Adds a shape to the printers configuration, optionally specifying whether it is for the off or on state.""")
def addShape(context: Context, args: Arguments): Array[Object] = {
if (data.stateOff.size > Settings.get.maxPrintComplexity || data.stateOn.size > Settings.get.maxPrintComplexity) {
return result(Unit, "model too complex")
}
val minX = (args.checkInteger(0) max 0 min 16) / 16f
val minY = (args.checkInteger(1) max 0 min 16) / 16f
val minZ = (16 - (args.checkInteger(2) max 0 min 16)) / 16f
val maxX = (args.checkInteger(3) max 0 min 16) / 16f
val maxY = (args.checkInteger(4) max 0 min 16) / 16f
val maxZ = (16 - (args.checkInteger(5) max 0 min 16)) / 16f
val texture = args.checkString(6).take(64)
val state = if (args.isBoolean(7)) args.checkBoolean(7) else false
val tint = if (args.isInteger(7)) Option(args.checkInteger(7)) else if (args.isInteger(8)) Option(args.checkInteger(8)) else None
if (minX == maxX) throw new IllegalArgumentException("empty block")
if (minY == maxY) throw new IllegalArgumentException("empty block")
if (minZ == maxZ) throw new IllegalArgumentException("empty block")
val list = if (state) data.stateOn else data.stateOff
list += new PrintData.Shape(AxisAlignedBB.getBoundingBox(
math.min(minX, maxX),
math.min(minY, maxY),
math.min(minZ, maxZ),
math.max(maxX, minX),
math.max(maxY, minY),
math.max(maxZ, minZ)),
texture, tint)
isActive = false // Needs committing.
world.markBlockForUpdate(x, y, z)
result(true)
}
@Callback(doc = """function():number -- Get the number of shapes in the current configuration.""")
def getShapeCount(context: Context, args: Arguments): Array[Object] = result(data.stateOff.size, data.stateOn.size)
@Callback(doc = """function():number -- Get the maximum allowed number of shapes.""")
def getMaxShapeCount(context: Context, args: Arguments): Array[Object] = result(Settings.get.maxPrintComplexity)
@Callback(doc = """function([count:number]):boolean -- Commit and begin printing the current configuration.""")
def commit(context: Context, args: Arguments): Array[Object] = {
if (!canPrint) {
return result(Unit, "model invalid")
}
limit = (args.optDouble(0, 1) max 0 min Integer.MAX_VALUE).toInt
isActive = limit > 0
result(true)
}
@Callback(doc = """function(): string, number or boolean -- The current state of the printer, `busy' or `idle', followed by the progress or model validity, respectively.""")
def status(context: Context, args: Arguments): Array[Object] = {
if (isPrinting) result("busy", progress)
else if (canPrint) result("idle", true)
else result("idle", false)
}
// ----------------------------------------------------------------------- //
override def canUpdate = isServer
override def updateEntity() {
super.updateEntity()
def canMergeOutput = {
val presentStack = getStackInSlot(slotOutput)
val outputStack = data.createItemStack()
presentStack == null || (presentStack.isItemEqual(outputStack) && ItemStack.areItemStackTagsEqual(presentStack, outputStack))
}
if (isActive && output.isEmpty && canMergeOutput) {
PrintData.computeCosts(data) match {
case Some((materialRequired, inkRequired)) =>
totalRequiredEnergy = Settings.get.printCost
requiredEnergy = totalRequiredEnergy
if (amountMaterial >= materialRequired && amountInk >= inkRequired) {
amountMaterial -= materialRequired
amountInk -= inkRequired
limit -= 1
output = Option(data.createItemStack())
if (limit < 1) isActive = false
ServerPacketSender.sendPrinting(this, printing = true)
}
case _ =>
isActive = false
data = new PrintData()
}
}
if (output.isDefined) {
val want = math.max(1, math.min(requiredEnergy, Settings.get.printerTickAmount))
val have = want + (if (Settings.get.ignorePower) 0 else node.changeBuffer(-want))
requiredEnergy -= have
if (requiredEnergy <= 0) {
val result = getStackInSlot(slotOutput)
if (result == null) {
setInventorySlotContents(slotOutput, output.get)
}
else if (result.stackSize < result.getMaxStackSize && canMergeOutput /* Should never fail, but just in case... */ ) {
result.stackSize += 1
markDirty()
}
else {
return
}
requiredEnergy = 0
output = None
}
ServerPacketSender.sendPrinting(this, have > 0.5 && output.isDefined)
}
val inputValue = PrintData.materialValue(getStackInSlot(slotMaterial))
if (inputValue > 0 && maxAmountMaterial - amountMaterial >= inputValue) {
val material = decrStackSize(slotMaterial, 1)
if (material != null) {
amountMaterial += inputValue
}
}
val inkValue = PrintData.inkValue(getStackInSlot(slotInk))
if (inkValue > 0 && maxAmountInk - amountInk >= inkValue) {
val material = decrStackSize(slotInk, 1)
if (material != null) {
amountInk += inkValue
if (material.getItem.hasContainerItem(material)) {
setInventorySlotContents(slotInk, material.getItem.getContainerItem(material))
}
}
}
}
override def readFromNBTForServer(nbt: NBTTagCompound) {
super.readFromNBTForServer(nbt)
amountMaterial = nbt.getInteger(Settings.namespace + "amountMaterial")
amountInk = nbt.getInteger(Settings.namespace + "amountInk")
data.load(nbt.getCompoundTag(Settings.namespace + "data"))
isActive = nbt.getBoolean(Settings.namespace + "active")
limit = nbt.getInteger(Settings.namespace + "limit")
if (nbt.hasKey(Settings.namespace + "output")) {
output = Option(ItemStack.loadItemStackFromNBT(nbt.getCompoundTag(Settings.namespace + "output")))
}
totalRequiredEnergy = nbt.getDouble(Settings.namespace + "total")
requiredEnergy = nbt.getDouble(Settings.namespace + "remaining")
}
override def writeToNBTForServer(nbt: NBTTagCompound) {
super.writeToNBTForServer(nbt)
nbt.setInteger(Settings.namespace + "amountMaterial", amountMaterial)
nbt.setInteger(Settings.namespace + "amountInk", amountInk)
nbt.setNewCompoundTag(Settings.namespace + "data", data.save)
nbt.setBoolean(Settings.namespace + "active", isActive)
nbt.setInteger(Settings.namespace + "limit", limit)
output.foreach(stack => nbt.setNewCompoundTag(Settings.namespace + "output", stack.writeToNBT))
nbt.setDouble(Settings.namespace + "total", totalRequiredEnergy)
nbt.setDouble(Settings.namespace + "remaining", requiredEnergy)
}
@SideOnly(Side.CLIENT) override
def readFromNBTForClient(nbt: NBTTagCompound) {
super.readFromNBTForClient(nbt)
data.load(nbt.getCompoundTag(Settings.namespace + "data"))
requiredEnergy = nbt.getDouble("remaining")
}
override def writeToNBTForClient(nbt: NBTTagCompound) {
super.writeToNBTForClient(nbt)
nbt.setNewCompoundTag(Settings.namespace + "data", data.save)
nbt.setDouble("remaining", requiredEnergy)
}
// ----------------------------------------------------------------------- //
override def getSizeInventory = 3
override def isItemValidForSlot(slot: Int, stack: ItemStack) =
if (slot == slotMaterial)
PrintData.materialValue(stack) > 0
else if (slot == slotInk)
PrintData.inkValue(stack) > 0
else false
// ----------------------------------------------------------------------- //
override def getAccessibleSlotsFromSide(side: Int): Array[Int] = Array(slotMaterial, slotInk, slotOutput)
override def canExtractItem(slot: Int, stack: ItemStack, side: Int): Boolean = !isItemValidForSlot(slot, stack)
override def canInsertItem(slot: Int, stack: ItemStack, side: Int): Boolean = slot != slotOutput
}
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.