blob: f9347674dd9708329eb6d755da8045fe02cb8470 [file] [log] [blame] [raw]
package li.cil.oc.integration.fmp
import java.lang
import codechicken.lib.data.MCDataInput
import codechicken.lib.data.MCDataOutput
import codechicken.lib.raytracer.ExtendedMOP
import codechicken.lib.vec.Cuboid6
import codechicken.lib.vec.Vector3
import codechicken.multipart.IRedstonePart
import codechicken.multipart.TCuboidPart
import codechicken.multipart.TNormalOcclusion
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import li.cil.oc.Settings
import li.cil.oc.api.Items
import li.cil.oc.client.renderer.block.Print
import li.cil.oc.common.block.Print
import li.cil.oc.common.item.data.PrintData
import li.cil.oc.common.tileentity
import li.cil.oc.integration.Mods
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedAABB
import li.cil.oc.util.ExtendedAABB._
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.ExtendedWorld._
import mods.immibis.redlogic.api.wiring.IRedstoneEmitter
import net.minecraft.client.renderer.OpenGlHelper
import net.minecraft.client.renderer.RenderBlocks
import net.minecraft.client.renderer.RenderGlobal
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.init.Blocks
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.MovingObjectPosition
import net.minecraft.util.Vec3
import net.minecraftforge.common.util.ForgeDirection
import org.lwjgl.opengl.GL11
import scala.collection.convert.WrapAsJava._
class PrintPart(val original: Option[tileentity.Print] = None) extends SimpleBlockPart with TCuboidPart with TNormalOcclusion with IRedstonePart {
var facing = ForgeDirection.SOUTH
var data = new PrintData()
var boundsOff = ExtendedAABB.unitBounds
var boundsOn = ExtendedAABB.unitBounds
var state = false
original.foreach(print => {
facing = print.facing
data = print.data
boundsOff = print.boundsOff
boundsOn = print.boundsOn
state = print.state
})
// ----------------------------------------------------------------------- //
override def simpleBlock = Items.get("print").block().asInstanceOf[Print]
def getType = Settings.namespace + "print"
override def doesTick = false
override def getBounds = new Cuboid6(if (state) boundsOn else boundsOff)
override def getOcclusionBoxes = {
val shapes = if (state) data.stateOn else data.stateOff
asJavaIterable(shapes.map(shape => new Cuboid6(shape.bounds.rotateTowards(facing))))
}
override def getCollisionBoxes = getOcclusionBoxes
override def getRenderBounds = getBounds
// ----------------------------------------------------------------------- //
override def canConnectRedstone(side: Int): Boolean = true
override def strongPowerLevel(side: Int): Int = weakPowerLevel(side)
override def weakPowerLevel(side: Int): Int = if (data.emitRedstone && state) 15 else 0
// ----------------------------------------------------------------------- //
override def activate(player: EntityPlayer, hit: MovingObjectPosition, item: ItemStack): Boolean = {
if (data.stateOn.size > 0) {
if (!state || !data.isButtonMode) {
toggleState()
return true
}
}
false
}
def toggleState(): Unit = {
if (canToggle) {
state = !state
world.playSoundEffect(x + 0.5, y + 0.5, z + 0.5, "random.click", 0.3F, if (state) 0.6F else 0.5F)
world.notifyBlocksOfNeighborChange(x, y, z, tile.getBlockType)
world.markBlockForUpdate(x, y, z)
if (!world.isRemote) sendDescUpdate()
tile.partList.foreach {
case print: PrintPart if print != this => print.onNeighborChanged()
case _ =>
}
if (state && data.isButtonMode) {
scheduleTick(simpleBlock.tickRate(world))
}
}
}
def canToggle = {
val toggled = new PrintPart()
toggled.facing = facing
toggled.data = data
toggled.state = !state
toggled.boundsOff = boundsOff
toggled.boundsOn = boundsOn
tile.canReplacePart(this, toggled)
}
// ----------------------------------------------------------------------- //
override def scheduledTick(): Unit = if (state) toggleState()
override def pickItem(hit: MovingObjectPosition): ItemStack = data.createItemStack()
override def getDrops: lang.Iterable[ItemStack] = asJavaIterable(Iterable(data.createItemStack()))
override def collisionRayTrace(start: Vec3, end: Vec3): ExtendedMOP = {
val shapes = if (state) data.stateOn else data.stateOff
var closestDistance = Double.PositiveInfinity
var closest: Option[MovingObjectPosition] = None
for (shape <- shapes) {
val bounds = shape.bounds.rotateTowards(facing).offset(x, y, z)
val hit = bounds.calculateIntercept(start, end)
if (hit != null) {
val distance = hit.hitVec.distanceTo(start)
if (distance < closestDistance) {
closestDistance = distance
hit.blockX = x
hit.blockY = y
hit.blockZ = z
closest = Option(hit)
}
}
}
closest.fold(if (shapes.isEmpty) new ExtendedMOP(x, y, z, 0, Vec3.createVectorHelper(0.5, 0.5, 0.5), null) else null)(hit => new ExtendedMOP(hit, null, closestDistance))
}
@SideOnly(Side.CLIENT)
override def drawHighlight(hit: MovingObjectPosition, player: EntityPlayer, frame: Float): Boolean = {
val pos = player.getPosition(frame)
val expansion = 0.002f
// See RenderGlobal.drawSelectionBox.
GL11.glEnable(GL11.GL_BLEND)
OpenGlHelper.glBlendFunc(770, 771, 1, 0)
GL11.glColor4f(0, 0, 0, 0.4f)
GL11.glLineWidth(2)
GL11.glDisable(GL11.GL_TEXTURE_2D)
GL11.glDepthMask(false)
for (shape <- if (state) data.stateOn else data.stateOff) {
val bounds = shape.bounds.rotateTowards(facing)
RenderGlobal.drawOutlinedBoundingBox(bounds.copy().expand(expansion, expansion, expansion)
.offset(hit.blockX, hit.blockY, hit.blockZ)
.offset(-pos.xCoord, -pos.yCoord, -pos.zCoord), -1)
}
GL11.glDepthMask(true)
GL11.glEnable(GL11.GL_TEXTURE_2D)
GL11.glDisable(GL11.GL_BLEND)
true
}
override def onNeighborChanged(): Unit = {
super.onNeighborChanged()
val inner = tile.partList.foldLeft(false)((powered, part) => part match {
case print: PrintPart => powered || (print.state && print.data.emitRedstone)
case _ => powered
})
val newMaxValue = if (inner) 15 else ForgeDirection.VALID_DIRECTIONS.map(computeInput).max
if (!data.emitRedstone && data.stateOn.size > 0) {
val newState = newMaxValue > 0
if (state != newState) {
toggleState()
}
}
}
protected def computeInput(side: ForgeDirection) = {
val blockPos = BlockPosition(x, y, z).offset(side)
if (!world.blockExists(blockPos)) 0
else {
// See BlockRedstoneLogic.getInputStrength() for reference.
val vanilla = math.max(world.getIndirectPowerLevelTo(blockPos, side),
if (world.getBlock(blockPos) == Blocks.redstone_wire) world.getBlockMetadata(blockPos) else 0)
val redLogic = if (Mods.RedLogic.isAvailable) {
world.getTileEntity(blockPos) match {
case emitter: IRedstoneEmitter =>
var strength = 0
for (i <- -1 to 5) {
strength = math.max(strength, emitter.getEmittedSignalStrength(i, side.getOpposite.ordinal()))
}
strength
case _ => 0
}
}
else 0
math.max(vanilla, redLogic)
}
}
// ----------------------------------------------------------------------- //
override def load(nbt: NBTTagCompound) {
super.load(nbt)
facing = nbt.getDirection("facing").getOrElse(facing)
data.load(nbt.getCompoundTag("data"))
state = nbt.getBoolean("state")
updateBounds()
}
override def save(nbt: NBTTagCompound) {
super.save(nbt)
nbt.setDirection("facing", Option(facing))
nbt.setNewCompoundTag("data", data.save)
nbt.setBoolean("state", state)
}
override def readDesc(packet: MCDataInput) {
super.readDesc(packet)
facing = ForgeDirection.getOrientation(packet.readUByte())
data.load(packet.readNBTTagCompound())
state = packet.readBoolean()
updateBounds()
if (world != null) {
world.markBlockForUpdate(x, y, z)
}
}
override def writeDesc(packet: MCDataOutput) {
super.writeDesc(packet)
packet.writeByte(facing.ordinal().toByte)
val nbt = new NBTTagCompound()
data.save(nbt)
packet.writeNBTTagCompound(nbt)
packet.writeBoolean(state)
}
def updateBounds(): Unit = {
boundsOff = data.stateOff.drop(1).foldLeft(data.stateOff.headOption.fold(ExtendedAABB.unitBounds)(_.bounds))((a, b) => a.func_111270_a(b.bounds))
if (boundsOff.volume == 0) boundsOff = ExtendedAABB.unitBounds
else boundsOff = boundsOff.rotateTowards(facing)
boundsOn = data.stateOn.drop(1).foldLeft(data.stateOn.headOption.fold(ExtendedAABB.unitBounds)(_.bounds))((a, b) => a.func_111270_a(b.bounds))
if (boundsOn.volume == 0) boundsOn = ExtendedAABB.unitBounds
else boundsOn = boundsOn.rotateTowards(facing)
}
// ----------------------------------------------------------------------- //
@SideOnly(Side.CLIENT)
override def renderStatic(pos: Vector3, pass: Int) = {
val (x, y, z) = (pos.x.toInt, pos.y.toInt, pos.z.toInt)
val renderer = RenderBlocks.getInstance
renderer.blockAccess = world
Print.render(data, state, facing, x, y, z, simpleBlock, renderer)
true
}
}