wrapped robot tile entity in a proxy that gets placed into the world instead, which allows more natural cleanup in minecraft (meaning we don't have to use cheap tricks like manipulating the tile entity list directly); robot rendering and animations

This commit is contained in:
Florian Nücke 2013-11-20 20:50:35 +01:00
parent 920d485f18
commit ca606686d3
24 changed files with 721 additions and 195 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

View File

@ -16,7 +16,7 @@ object Blocks {
var keyboard: Keyboard = null var keyboard: Keyboard = null
var powerDistributor: PowerDistributor = null var powerDistributor: PowerDistributor = null
var powerSupply: PowerConverter = null var powerSupply: PowerConverter = null
var robot: Robot = null var robotProxy: RobotProxy = null
var robotAfterimage: RobotAfterimage = null var robotAfterimage: RobotAfterimage = null
var screen1, screen2, screen3: Screen = null var screen1, screen2, screen3: Screen = null
@ -35,7 +35,7 @@ object Blocks {
GameRegistry.registerTileEntity(classOf[tileentity.Keyboard], Config.namespace + "keyboard") GameRegistry.registerTileEntity(classOf[tileentity.Keyboard], Config.namespace + "keyboard")
GameRegistry.registerTileEntity(classOf[tileentity.PowerConverter], Config.namespace + "power_converter") GameRegistry.registerTileEntity(classOf[tileentity.PowerConverter], Config.namespace + "power_converter")
GameRegistry.registerTileEntity(classOf[tileentity.PowerDistributor], Config.namespace + "power_distributor") GameRegistry.registerTileEntity(classOf[tileentity.PowerDistributor], Config.namespace + "power_distributor")
GameRegistry.registerTileEntity(classOf[tileentity.Robot], Config.namespace + "robot") GameRegistry.registerTileEntity(classOf[tileentity.RobotProxy], Config.namespace + "robot")
GameRegistry.registerTileEntity(classOf[tileentity.Screen], Config.namespace + "screen") GameRegistry.registerTileEntity(classOf[tileentity.Screen], Config.namespace + "screen")
// IMPORTANT: the multi block must come first, since the sub blocks will // IMPORTANT: the multi block must come first, since the sub blocks will
@ -53,7 +53,7 @@ object Blocks {
screen3 = new Screen.Tier3(blockSimple) screen3 = new Screen.Tier3(blockSimple)
capacitor = new Capacitor(blockSimple) capacitor = new Capacitor(blockSimple)
robot = new Robot(blockSpecial) robotProxy = new RobotProxy(blockSpecial)
robotAfterimage = new RobotAfterimage(blockSpecial) robotAfterimage = new RobotAfterimage(blockSpecial)
} }
} }

View File

@ -12,8 +12,8 @@ object GuiHandler extends CommonGuiHandler {
new gui.Case(player.inventory, computer) new gui.Case(player.inventory, computer)
case drive: tileentity.DiskDrive if id == GuiType.DiskDrive.id => case drive: tileentity.DiskDrive if id == GuiType.DiskDrive.id =>
new gui.DiskDrive(player.inventory, drive) new gui.DiskDrive(player.inventory, drive)
case robot: tileentity.Robot if id == GuiType.Robot.id => case proxy: tileentity.RobotProxy if id == GuiType.Robot.id =>
new gui.Robot(player.inventory, robot) new gui.Robot(player.inventory, proxy.robot)
case screen: tileentity.Screen if id == GuiType.Screen.id => case screen: tileentity.Screen if id == GuiType.Screen.id =>
new gui.Screen(screen) new gui.Screen(screen)
case _ => null case _ => null

View File

@ -27,6 +27,9 @@ class PacketHandler extends CommonPacketHandler {
case PacketType.ComputerStateResponse => onComputerStateResponse(p) case PacketType.ComputerStateResponse => onComputerStateResponse(p)
case PacketType.PowerStateResponse => onPowerStateResponse(p) case PacketType.PowerStateResponse => onPowerStateResponse(p)
case PacketType.RedstoneStateResponse => onRedstoneStateResponse(p) case PacketType.RedstoneStateResponse => onRedstoneStateResponse(p)
case PacketType.RobotAnimateSwing => onRobotAnimateSwing(p)
case PacketType.RobotAnimateTurn => onRobotAnimateTurn(p)
case PacketType.RobotEquippedItemChange => onRobotEquippedItemChange(p)
case PacketType.RobotMove => onRobotMove(p) case PacketType.RobotMove => onRobotMove(p)
case PacketType.RobotSelectedSlotChange => onRobotSelectedSlotChange(p) case PacketType.RobotSelectedSlotChange => onRobotSelectedSlotChange(p)
case PacketType.RobotStateResponse => onRobotStateResponse(p) case PacketType.RobotStateResponse => onRobotStateResponse(p)
@ -80,26 +83,46 @@ class PacketHandler extends CommonPacketHandler {
case _ => // Invalid packet. case _ => // Invalid packet.
} }
def onRobotAnimateSwing(p: PacketParser) =
p.readTileEntity[RobotProxy]() match {
case Some(t) => t.robot.setAnimateSwing(p.readInt())
case _ => // Invalid packet.
}
def onRobotAnimateTurn(p: PacketParser) =
p.readTileEntity[RobotProxy]() match {
case Some(t) => t.robot.setAnimateTurn(p.readByte(), p.readInt())
case _ => // Invalid packet.
}
def onRobotEquippedItemChange(p: PacketParser) =
p.readTileEntity[RobotProxy]() match {
case Some(t) => t.robot.equippedItem = Option(p.readItemStack())
case _ => // Invalid packet.
}
def onRobotMove(p: PacketParser) = def onRobotMove(p: PacketParser) =
p.readTileEntity[Robot]() match { p.readTileEntity[RobotProxy]() match {
case Some(t) => t.move(p.readDirection()) case Some(t) => t.robot.move(p.readDirection())
case _ => // Invalid packet. case _ => // Invalid packet.
} }
def onRobotSelectedSlotChange(p: PacketParser) = def onRobotSelectedSlotChange(p: PacketParser) =
p.readTileEntity[Robot]() match { p.readTileEntity[RobotProxy]() match {
case Some(t) => t.selectedSlot = p.readInt() case Some(t) => t.robot.selectedSlot = p.readInt()
case _ => // Invalid packet. case _ => // Invalid packet.
} }
def onRobotStateResponse(p: PacketParser) = def onRobotStateResponse(p: PacketParser) =
p.readTileEntity[Robot]() match { p.readTileEntity[RobotProxy]() match {
case Some(t) => case Some(t) =>
t.selectedSlot = p.readInt() t.robot.selectedSlot = p.readInt()
t.animationTicksTotal = p.readInt() t.robot.equippedItem = Option(p.readItemStack())
t.animationTicksLeft = p.readInt() t.robot.animationTicksTotal = p.readInt()
t.moveDirection = p.readDirection() t.robot.animationTicksLeft = p.readInt()
t.turnOldFacing = p.readDirection() t.robot.moveDirection = p.readDirection()
t.robot.swingingTool = p.readBoolean()
t.robot.turnAxis = p.readByte()
case _ => // Invalid packet. case _ => // Invalid packet.
} }

View File

@ -6,7 +6,7 @@ import cpw.mods.fml.common.network.NetworkRegistry
import cpw.mods.fml.common.registry.TickRegistry import cpw.mods.fml.common.registry.TickRegistry
import cpw.mods.fml.relauncher.Side import cpw.mods.fml.relauncher.Side
import li.cil.oc.OpenComputers import li.cil.oc.OpenComputers
import li.cil.oc.client.renderer.tileentity.{CableRenderer, PowerDistributorRenderer, ScreenRenderer, CaseRenderer} import li.cil.oc.client.renderer.tileentity._
import li.cil.oc.common.tileentity import li.cil.oc.common.tileentity
import li.cil.oc.common.{Proxy => CommonProxy} import li.cil.oc.common.{Proxy => CommonProxy}
import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.MinecraftForge
@ -20,6 +20,7 @@ private[oc] class Proxy extends CommonProxy {
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Cable], CableRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Cable], CableRenderer)
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Case], CaseRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Case], CaseRenderer)
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.PowerDistributor], PowerDistributorRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.PowerDistributor], PowerDistributorRenderer)
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.RobotProxy], RobotRenderer)
ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Screen], ScreenRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Screen], ScreenRenderer)
TickRegistry.registerTickHandler(ScreenRenderer, Side.CLIENT) TickRegistry.registerTickHandler(ScreenRenderer, Side.CLIENT)

View File

@ -125,7 +125,7 @@ object CableRenderer extends TileEntitySpecialRenderer {
def renderTileEntityAt(t: TileEntity, x: Double, y: Double, z: Double, f: Float) { def renderTileEntityAt(t: TileEntity, x: Double, y: Double, z: Double, f: Float) {
val cable = t.asInstanceOf[Cable] val cable = t.asInstanceOf[Cable]
val l = t.getWorldObj.getLightBrightnessForSkyBlocks(t.xCoord, t.yCoord, t.zCoord, 0) val l = t.getWorldObj.getLightBrightnessForSkyBlocks(cable.x, cable.y, cable.z, 0)
val l1 = l % 65536 val l1 = l % 65536
val l2 = l / 65536 val l2 = l / 65536
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, l1, l2) OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, l1, l2)

View File

@ -0,0 +1,180 @@
package li.cil.oc.client.renderer.tileentity
import java.util.logging.Level
import li.cil.oc.common.tileentity
import li.cil.oc.{OpenComputers, Config}
import net.minecraft.client.renderer.entity.RenderManager
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer
import net.minecraft.client.renderer.{OpenGlHelper, Tessellator, GLAllocation}
import net.minecraft.tileentity.TileEntity
import net.minecraft.util.ResourceLocation
import net.minecraftforge.common.ForgeDirection
import org.lwjgl.opengl.GL11
import li.cil.oc.util.RenderState
object RobotRenderer extends TileEntitySpecialRenderer {
private val texture = new ResourceLocation(Config.resourceDomain, "textures/blocks/robot.png")
private val displayList = GLAllocation.generateDisplayLists(1)
private val gap = 1.0 / 28.0
private val gt = 0.5 + gap
private val gb = 0.5 - gap
def compileList() {
val t = Tessellator.instance
val size = 0.4
val l = 0.5 - size
val h = 0.5 + size
GL11.glNewList(displayList, GL11.GL_COMPILE)
t.startDrawing(GL11.GL_TRIANGLE_FAN)
t.addVertexWithUV(0.5, 1, 0.5, 0.25, 0.25)
t.addVertexWithUV(l, gt, h, 0, 0.5)
t.addVertexWithUV(h, gt, h, 0.5, 0.5)
t.addVertexWithUV(h, gt, l, 0.5, 0)
t.addVertexWithUV(l, gt, l, 0, 0)
t.addVertexWithUV(l, gt, h, 0, 0.5)
t.draw()
t.startDrawingQuads()
t.addVertexWithUV(l, gt, h, 0, 1)
t.addVertexWithUV(l, gt, l, 0, 0.5)
t.addVertexWithUV(h, gt, l, 0.5, 0.5)
t.addVertexWithUV(h, gt, h, 0.5, 1)
t.draw()
t.startDrawing(GL11.GL_TRIANGLE_FAN)
t.addVertexWithUV(0.5, 0.03, 0.5, 0.75, 0.25)
t.addVertexWithUV(l, gb, l, 0.5, 0)
t.addVertexWithUV(h, gb, l, 1, 0)
t.addVertexWithUV(h, gb, h, 1, 0.5)
t.addVertexWithUV(l, gb, h, 0.5, 0.5)
t.addVertexWithUV(l, gb, l, 0.5, 0)
t.draw()
t.startDrawingQuads()
t.addVertexWithUV(l, gb, l, 0, 0.5)
t.addVertexWithUV(l, gb, h, 0, 1)
t.addVertexWithUV(h, gb, h, 0.5, 1)
t.addVertexWithUV(h, gb, l, 0.5, 0.5)
t.draw()
GL11.glEndList()
}
compileList()
def renderTileEntityAt(entity: TileEntity, x: Double, y: Double, z: Double, f: Float) {
val proxy = entity.asInstanceOf[tileentity.RobotProxy]
val robot = proxy.robot
val worldTime = entity.getWorldObj.getTotalWorldTime + f
{
val l = robot.world.getLightBrightnessForSkyBlocks(robot.x, robot.y, robot.z, 0)
val l1 = l % 65536
val l2 = l / 65536
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, l1, l2)
}
GL11.glPushMatrix()
GL11.glTranslated(x + 0.5, y + 0.5, z + 0.5)
if (robot.isAnimatingMove) {
val remaining = (robot.animationTicksLeft - f) / robot.animationTicksTotal.toDouble
GL11.glTranslated(
-robot.moveDirection.offsetX * remaining,
-robot.moveDirection.offsetY * remaining,
-robot.moveDirection.offsetZ * remaining)
}
if (robot.isAnimatingTurn) {
val remaining = (robot.animationTicksLeft - f) / robot.animationTicksTotal.toDouble
GL11.glRotated(90 * remaining, 0, robot.turnAxis, 0)
}
robot.yaw match {
case ForgeDirection.WEST => GL11.glRotatef(-90, 0, 1, 0)
case ForgeDirection.NORTH => GL11.glRotatef(180, 0, 1, 0)
case ForgeDirection.EAST => GL11.glRotatef(90, 0, 1, 0)
case _ => // No yaw.
}
GL11.glTranslated(-0.5, -0.5, -0.5)
val timeJitter = robot.hashCode
val hover =
if (robot.isOn) Math.sin(timeJitter + worldTime / 20.0) * 0.03
else -0.03
GL11.glTranslated(0, hover, 0)
bindTexture(texture)
GL11.glCallList(displayList)
val size = 0.3
val l = 0.5 - size
val h = 0.5 + size
val vStep = 1.0 / 32.0
val strip = timeJitter + worldTime / 20.0
val offsetV = ((strip - strip.toInt) * 16).toInt * vStep
val (u0, u1, v0, v1) = {
if (robot.isOn)
(0.5, 1.0, 0.5 + offsetV, 0.5 + vStep + offsetV)
else
(0.25 - vStep, 0.25 + vStep, 0.75 - vStep, 0.75 + vStep)
}
RenderState.disableLighting()
val t = Tessellator.instance
t.startDrawingQuads()
t.addVertexWithUV(l, gt, l, u0, v0)
t.addVertexWithUV(l, gb, l, u0, v1)
t.addVertexWithUV(l, gb, h, u1, v1)
t.addVertexWithUV(l, gt, h, u1, v0)
t.addVertexWithUV(l, gt, h, u0, v0)
t.addVertexWithUV(l, gb, h, u0, v1)
t.addVertexWithUV(h, gb, h, u1, v1)
t.addVertexWithUV(h, gt, h, u1, v0)
t.addVertexWithUV(h, gt, h, u0, v0)
t.addVertexWithUV(h, gb, h, u0, v1)
t.addVertexWithUV(h, gb, l, u1, v1)
t.addVertexWithUV(h, gt, l, u1, v0)
t.addVertexWithUV(h, gt, l, u0, v0)
t.addVertexWithUV(h, gb, l, u0, v1)
t.addVertexWithUV(l, gb, l, u1, v1)
t.addVertexWithUV(l, gt, l, u1, v0)
t.draw()
RenderState.enableLighting()
robot.equippedItem match {
case Some(stack) =>
GL11.glDisable(GL11.GL_CULL_FACE)
GL11.glTranslated(0.1, 0.25, 0.75)
GL11.glScaled(0.4, 0.4, -0.4)
if (robot.isAnimatingSwing) {
val remaining = (robot.animationTicksLeft - f) / robot.animationTicksTotal.toDouble
GL11.glRotated(Math.sin(remaining * Math.PI) * 45, -1, 0, 0)
}
GL11.glRotatef(-30, 1, 0, 0)
GL11.glRotatef(40, 0, 1, 0)
try {
RenderManager.instance.itemRenderer.renderItem(robot.player(), stack, 0)
}
catch {
case e: Throwable =>
OpenComputers.log.log(Level.WARNING, "Failed rendering equipped item.", e)
robot.equippedItem = None
}
GL11.glEnable(GL11.GL_CULL_FACE)
case _ =>
}
GL11.glPopMatrix()
}
}

View File

@ -11,8 +11,8 @@ abstract class GuiHandler extends IGuiHandler {
new container.Case(player.inventory, computer) new container.Case(player.inventory, computer)
case drive: tileentity.DiskDrive if id == GuiType.DiskDrive.id => case drive: tileentity.DiskDrive if id == GuiType.DiskDrive.id =>
new container.DiskDrive(player.inventory, drive) new container.DiskDrive(player.inventory, drive)
case robot: tileentity.Robot if id == GuiType.Robot.id => case proxy: tileentity.RobotProxy if id == GuiType.Robot.id =>
new container.Robot(player.inventory, robot) new container.Robot(player.inventory, proxy.robot)
case _ => null case _ => null
} }
} }

View File

@ -5,7 +5,8 @@ import cpw.mods.fml.common.network.Player
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.DataOutputStream import java.io.DataOutputStream
import li.cil.oc.common.tileentity.TileEntity import li.cil.oc.common.tileentity.TileEntity
import net.minecraft.nbt.NBTBase import net.minecraft.item.ItemStack
import net.minecraft.nbt.{NBTTagCompound, NBTBase}
import net.minecraft.network.packet.Packet250CustomPayload import net.minecraft.network.packet.Packet250CustomPayload
import net.minecraftforge.common.ForgeDirection import net.minecraftforge.common.ForgeDirection
@ -22,10 +23,14 @@ class PacketBuilder(packetType: PacketType.Value, private val stream: ByteArrayO
def writeDirection(d: ForgeDirection) = writeInt(d.ordinal) def writeDirection(d: ForgeDirection) = writeInt(d.ordinal)
def writeItemStack(stack: ItemStack) = writeNBT(stack.writeToNBT(new NBTTagCompound()))
def writeNBT(nbt: NBTBase) = NBTBase.writeNamedTag(nbt, this) def writeNBT(nbt: NBTBase) = NBTBase.writeNamedTag(nbt, this)
def sendToAllPlayers() = PacketDispatcher.sendPacketToAllPlayers(packet) def sendToAllPlayers() = PacketDispatcher.sendPacketToAllPlayers(packet)
def sendToNearbyPlayers(t: TileEntity, range: Double = 64) = PacketDispatcher.sendPacketToAllAround(t.x, t.y, t.z, range, t.world.provider.dimensionId, packet)
def sendToPlayer(player: Player) = PacketDispatcher.sendPacketToPlayer(packet, player) def sendToPlayer(player: Player) = PacketDispatcher.sendPacketToPlayer(packet, player)
def sendToServer() = PacketDispatcher.sendPacketToServer(packet) def sendToServer() = PacketDispatcher.sendPacketToServer(packet)

View File

@ -6,7 +6,8 @@ import java.io.ByteArrayInputStream
import java.io.DataInputStream import java.io.DataInputStream
import java.util.logging.Level import java.util.logging.Level
import li.cil.oc.OpenComputers import li.cil.oc.OpenComputers
import net.minecraft.nbt.NBTBase import net.minecraft.item.ItemStack
import net.minecraft.nbt.{NBTTagCompound, NBTBase}
import net.minecraft.network.INetworkManager import net.minecraft.network.INetworkManager
import net.minecraft.network.packet.Packet250CustomPayload import net.minecraft.network.packet.Packet250CustomPayload
import net.minecraft.world.World import net.minecraft.world.World
@ -62,6 +63,8 @@ abstract class PacketHandler extends IPacketHandler {
def readDirection() = ForgeDirection.getOrientation(readInt()) def readDirection() = ForgeDirection.getOrientation(readInt())
def readItemStack() = ItemStack.loadItemStackFromNBT(readNBT().asInstanceOf[NBTTagCompound])
def readNBT() = NBTBase.readNamedTag(this) def readNBT() = NBTBase.readNamedTag(this)
} }

View File

@ -1,37 +1,40 @@
package li.cil.oc.common package li.cil.oc.common
object PacketType extends Enumeration { object PacketType extends Enumeration {
val ComputerStateRequest = Value("ComputerStateRequest") val ComputerStateRequest,
val ComputerStateResponse = Value("ComputerStateResponse") ComputerStateResponse,
val PowerStateRequest = Value("PowerStateRequest") PowerStateRequest,
val PowerStateResponse = Value("PowerStateResponse") PowerStateResponse,
val RedstoneStateRequest = Value("RedstoneStateRequest") RedstoneStateRequest,
val RedstoneStateResponse = Value("RedstoneStateResponse") RedstoneStateResponse,
val RobotMove = Value("RobotMove") RobotAnimateSwing,
val RobotSelectedSlotChange = Value("RobotSelectedSlotChange") RobotAnimateTurn,
val RobotStateRequest = Value("RobotSelectedSlotRequest") RobotEquippedItemChange,
val RobotStateResponse = Value("RobotStateResponse") RobotMove,
RobotSelectedSlotChange,
RobotStateRequest,
RobotStateResponse,
val RotatableStateRequest = Value("RotatableStateRequest") RotatableStateRequest,
val RotatableStateResponse = Value("RotatableStateResponse") RotatableStateResponse,
val ScreenBufferRequest = Value("ScreenBufferRequest") ScreenBufferRequest,
val ScreenBufferResponse = Value("ScreenBufferResponse") ScreenBufferResponse,
val ScreenColorChange = Value("ScreenColorChange") ScreenColorChange,
val ScreenCopy = Value("ScreenCopy") ScreenCopy,
val ScreenDepthChange = Value("ScreenDepthChange") ScreenDepthChange,
val ScreenFill = Value("ScreenFill") ScreenFill,
val ScreenPowerChange = Value("ScreenPowerChange") ScreenPowerChange,
val ScreenResolutionChange = Value("ScreenResolutionChange") ScreenResolutionChange,
val ScreenSet = Value("ScreenSet") ScreenSet,
val KeyDown = Value("KeyDown") KeyDown,
val KeyUp = Value("KeyUp") KeyUp,
val Clipboard = Value("Clipboard") Clipboard,
val Analyze = Value("Analyze") Analyze = Value
} }

View File

@ -17,14 +17,6 @@ abstract class Computer extends Delegate {
override def isProvidingWeakPower(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) = override def isProvidingWeakPower(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) =
world.getBlockTileEntity(x, y, z).asInstanceOf[tileentity.Computer].output(side) world.getBlockTileEntity(x, y, z).asInstanceOf[tileentity.Computer].output(side)
override def onBlockPreDestroy(world: World, x: Int, y: Int, z: Int) = {
if (!world.isRemote) world.getBlockTileEntity(x, y, z) match {
case computer: tileentity.Computer => computer.computer.stop()
case _ => // Ignore.
}
super.onBlockPreDestroy(world, x, y, z)
}
override def onBlockActivated(world: World, x: Int, y: Int, z: Int, player: EntityPlayer, override def onBlockActivated(world: World, x: Int, y: Int, z: Int, player: EntityPlayer,
side: ForgeDirection, hitX: Float, hitY: Float, hitZ: Float) = { side: ForgeDirection, hitX: Float, hitY: Float, hitZ: Float) = {
if (!player.isSneaking) { if (!player.isSneaking) {

View File

@ -53,7 +53,7 @@ class RobotAfterimage(val parent: SpecialDelegator) extends SpecialDelegate {
for (side <- ForgeDirection.VALID_DIRECTIONS) { for (side <- ForgeDirection.VALID_DIRECTIONS) {
val (rx, ry, rz) = (x + side.offsetX, y + side.offsetY, z + side.offsetZ) val (rx, ry, rz) = (x + side.offsetX, y + side.offsetY, z + side.offsetZ)
world.getBlockTileEntity(rx, ry, rz) match { world.getBlockTileEntity(rx, ry, rz) match {
case robot: tileentity.Robot if robot.isAnimatingMove && robot.moveDirection == side => return Some(robot) case proxy: tileentity.RobotProxy if proxy.robot.isAnimatingMove && proxy.robot.moveDirection == side => return Some(proxy.robot)
case _ => case _ =>
} }
} }

View File

@ -7,7 +7,7 @@ import net.minecraft.util.{AxisAlignedBB, Vec3}
import net.minecraft.world.{IBlockAccess, World} import net.minecraft.world.{IBlockAccess, World}
import net.minecraftforge.common.ForgeDirection import net.minecraftforge.common.ForgeDirection
class Robot(val parent: SpecialDelegator) extends Computer with SpecialDelegate { class RobotProxy(val parent: SpecialDelegator) extends Computer with SpecialDelegate {
val unlocalizedName = "Robot" val unlocalizedName = "Robot"
var moving = new ThreadLocal[Option[tileentity.Robot]] { var moving = new ThreadLocal[Option[tileentity.Robot]] {
@ -16,8 +16,8 @@ class Robot(val parent: SpecialDelegator) extends Computer with SpecialDelegate
override def createTileEntity(world: World) = { override def createTileEntity(world: World) = {
moving.get match { moving.get match {
case Some(robot) => Some(robot) case Some(robot) => Some(new tileentity.RobotProxy(robot))
case _ => Some(new tileentity.Robot(world.isRemote)) case _ => Some(new tileentity.RobotProxy(new tileentity.Robot(world.isRemote)))
} }
} }
@ -35,14 +35,14 @@ class Robot(val parent: SpecialDelegator) extends Computer with SpecialDelegate
override def collisionRayTrace(world: World, x: Int, y: Int, z: Int, origin: Vec3, direction: Vec3) = { override def collisionRayTrace(world: World, x: Int, y: Int, z: Int, origin: Vec3, direction: Vec3) = {
val bounds = parent.getCollisionBoundingBoxFromPool(world, x, y, z) val bounds = parent.getCollisionBoundingBoxFromPool(world, x, y, z)
bounds.offset(x, y, z)
if (bounds.isVecInside(origin)) null if (bounds.isVecInside(origin)) null
else super.collisionRayTrace(world, x, y, z, origin, direction) else super.collisionRayTrace(world, x, y, z, origin, direction)
} }
override def setBlockBoundsBasedOnState(world: IBlockAccess, x: Int, y: Int, z: Int) { override def setBlockBoundsBasedOnState(world: IBlockAccess, x: Int, y: Int, z: Int) {
world.getBlockTileEntity(x, y, z) match { world.getBlockTileEntity(x, y, z) match {
case robot: tileentity.Robot => case proxy: tileentity.RobotProxy =>
val robot = proxy.robot
val bounds = AxisAlignedBB.getBoundingBox(0.1, 0.1, 0.1, 0.9, 0.9, 0.9) val bounds = AxisAlignedBB.getBoundingBox(0.1, 0.1, 0.1, 0.9, 0.9, 0.9)
if (robot.isAnimatingMove) { if (robot.isAnimatingMove) {
val remaining = robot.animationTicksLeft.toDouble / robot.animationTicksTotal.toDouble val remaining = robot.animationTicksLeft.toDouble / robot.animationTicksTotal.toDouble

View File

@ -9,11 +9,21 @@ import li.cil.oc.util.{PackedColor, Persistable}
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
trait Buffer extends Environment with Persistable { trait Buffer extends Environment with Persistable {
val buffer = new component.Buffer(this) protected val buffer_ = new component.Buffer(this)
var bufferIsDirty = false protected var bufferIsDirty_ = false
var currentGui: Option[gui.Buffer] = None protected var currentGui_ = None: Option[gui.Buffer]
def buffer = buffer_
def bufferIsDirty = bufferIsDirty_
def bufferIsDirty_=(value: Boolean) = bufferIsDirty_ = value
def currentGui = currentGui_
def currentGui_=(value: Option[gui.Buffer]) = currentGui_ = value
def node: Node = buffer.node def node: Node = buffer.node

View File

@ -11,7 +11,9 @@ import net.minecraftforge.common.ForgeDirection
import scala.Some import scala.Some
abstract class Computer(isRemote: Boolean) extends Environment with ComponentInventory with Rotatable with Redstone with Analyzable { abstract class Computer(isRemote: Boolean) extends Environment with ComponentInventory with Rotatable with Redstone with Analyzable {
val computer = if (isRemote) null else new component.Computer(this) protected val computer_ = if (isRemote) null else new component.Computer(this)
def computer = computer_
def node = if (isClient) null else computer.node def node = if (isClient) null else computer.node
@ -52,7 +54,7 @@ abstract class Computer(isRemote: Boolean) extends Environment with ComponentInv
if (hasChanged) { if (hasChanged) {
hasChanged = false hasChanged = false
worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this) world.markTileEntityChunkModified(x, y, z, this)
} }
if (isRunning != computer.isRunning) { if (isRunning != computer.isRunning) {

View File

@ -23,7 +23,7 @@ abstract class Environment extends net.minecraft.tileentity.TileEntity with Tile
override def updateEntity() { override def updateEntity() {
super.updateEntity() super.updateEntity()
if (node != null && node.network == null) { if (node != null && node.network == null) {
Network.joinOrCreateNetwork(worldObj, xCoord, yCoord, zCoord) Network.joinOrCreateNetwork(world, x, y, z)
} }
} }

View File

@ -11,39 +11,43 @@ import li.cil.oc.server.{PacketSender => ServerPacketSender, component}
import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.{Blocks, Config, api, common} import li.cil.oc.{Blocks, Config, api, common}
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import net.minecraftforge.common.ForgeDirection import net.minecraftforge.common.ForgeDirection
import scala.Some import scala.Some
import scala.collection.convert.WrapAsScala._
class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with PowerInformation { class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with PowerInformation {
def this() = this(false) def this() = this(false)
var proxy: RobotProxy = _
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override val node = api.Network.newNode(this, Visibility.Network). override def node = if (isClient) null else computer.node
withComponent("computer", Visibility.Neighbors).
create()
override val buffer = new common.component.Buffer(this) { override val buffer_ = new common.component.Buffer(this) {
override def maxResolution = (48, 14) override def maxResolution = (48, 14)
} }
override val computer = if (isRemote) null else new component.Robot(this) override val computer_ = if (isRemote) null else new component.Robot(this)
val (battery, distributor, gpu, keyboard) = if (isServer) { val (battery, distributor, gpu, keyboard) = if (isServer) {
val battery = api.Network.newNode(this, Visibility.Network).withConnector(10000).create() val battery = api.Network.newNode(this, Visibility.Network).withConnector(10000).create()
val distributor = new component.PowerDistributor(this) val distributor = new component.PowerDistributor(this)
val gpu = new GraphicsCard.Tier1 { val gpu = new GraphicsCard.Tier1 {
override val maxResolution = (48, 14) override val maxResolution = (48, 14)
} }
val keyboard = new component.Keyboard(this) val keyboard = new component.Keyboard(this) {
override def isUseableByPlayer(p: EntityPlayer) =
world.getBlockTileEntity(x, y, z) == proxy &&
p.getDistanceSq(x + 0.5, y + 0.5, z + 0.5) <= 64
}
(battery, distributor, gpu, keyboard) (battery, distributor, gpu, keyboard)
} }
else (null, null, null, null) else (null, null, null, null)
var selectedSlot = 0 var selectedSlot = 0
private lazy val player_ = new Player(this) var equippedItem: Option[ItemStack] = None
var animationTicksLeft = 0 var animationTicksLeft = 0
@ -51,12 +55,15 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power
var moveDirection = ForgeDirection.UNKNOWN var moveDirection = ForgeDirection.UNKNOWN
var turnOldFacing = ForgeDirection.UNKNOWN var swingingTool = false
var turnAxis = 0
private lazy val player_ = new Player(this)
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
def player(facing: ForgeDirection = facing, side: ForgeDirection = facing) = { def player(facing: ForgeDirection = facing, side: ForgeDirection = facing) = {
assert(isServer)
player_.updatePositionAndRotation(facing, side) player_.updatePositionAndRotation(facing, side)
player_ player_
} }
@ -66,92 +73,107 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power
def move(direction: ForgeDirection) = { def move(direction: ForgeDirection) = {
val (ox, oy, oz) = (x, y, z) val (ox, oy, oz) = (x, y, z)
val (nx, ny, nz) = (x + direction.offsetX, y + direction.offsetY, z + direction.offsetZ) val (nx, ny, nz) = (x + direction.offsetX, y + direction.offsetY, z + direction.offsetZ)
val blockId = world.getBlockId(nx, ny, nz)
val metadata = world.getBlockMetadata(nx, ny, nz)
try {
// Setting this will make the tile entity created via the following call // Setting this will make the tile entity created via the following call
// to setBlock to re-use our "real" instance as the inner object, instead // to setBlock to re-use our "real" instance as the inner object, instead
// of creating a new one. // of creating a new one.
Blocks.robot.moving.set(Some(this)) Blocks.robotProxy.moving.set(Some(this))
// Do *not* immediately send the change to clients to allow checking if it // Do *not* immediately send the change to clients to allow checking if it
// worked before the client is notified so that we can use the same trick on // worked before the client is notified so that we can use the same trick on
// the client by sending a corresponding packet. This also saves us from // the client by sending a corresponding packet. This also saves us from
// having to send the complete state again (e.g. screen buffer) each move. // having to send the complete state again (e.g. screen buffer) each move.
val blockId = world.getBlockId(nx, ny, nz) val created = world.setBlock(nx, ny, nz, Blocks.robotProxy.parent.blockID, Blocks.robotProxy.blockId, 1)
val metadata = world.getBlockMetadata(nx, ny, nz)
val created = world.setBlock(nx, ny, nz, getBlockType.blockID, getBlockMetadata, 1)
if (created) { if (created) {
assert(world.getBlockTileEntity(nx, ny, nz) == this) assert(world.getBlockTileEntity(nx, ny, nz) == proxy)
assert(x == nx && y == ny && z == nz) assert(x == nx && y == ny && z == nz)
world.setBlock(ox, oy, oz, Blocks.robotAfterimage.parent.blockID, Blocks.robotAfterimage.blockId, 1) world.setBlock(ox, oy, oz, Blocks.robotAfterimage.parent.blockID, Blocks.robotAfterimage.blockId, 1)
assert(Blocks.blockSpecial.subBlock(world, ox, oy, oz).exists(_ == Blocks.robotAfterimage)) assert(Blocks.blockSpecial.subBlock(world, ox, oy, oz).exists(_ == Blocks.robotAfterimage))
if (isServer) { if (isServer) {
ServerPacketSender.sendRobotMove(this, ox, oy, oz, direction) ServerPacketSender.sendRobotMove(this, ox, oy, oz, direction)
for (neighbor <- node.neighbors) {
node.disconnect(neighbor)
}
api.Network.joinOrCreateNetwork(world, nx, ny, nz)
} }
else { else {
// On the client this is called from the packet handler code, leading // If we broke some replaceable block (like grass) play its break sound.
// to the entity being added directly to the list of loaded tile
// entities, without any additional checks - so we get a duplicate.
val duplicate = world.loadedTileEntityList.remove(world.loadedTileEntityList.size - 1)
assert(duplicate == this)
if (blockId > 0) { if (blockId > 0) {
world.playAuxSFX(2001, nx, ny, nz, blockId + (metadata << 12)) world.playAuxSFX(2001, nx, ny, nz, blockId + (metadata << 12))
} }
world.markBlockForUpdate(ox, oy, oz) world.markBlockForRenderUpdate(ox, oy, oz)
world.markBlockForUpdate(nx, ny, nz) world.markBlockForRenderUpdate(nx, ny, nz)
} }
assert(!isInvalid) assert(!isInvalid)
} }
Blocks.robot.moving.set(None)
if (created) { if (created) {
// Here instead of Lua callback so that it gets triggered on client.
animateMove(direction, Config.moveDelay) animateMove(direction, Config.moveDelay)
checkRedstoneInputChanged() checkRedstoneInputChanged()
} }
created created
} }
finally {
Blocks.robotProxy.moving.set(None)
}
}
def isAnimatingMove = animationTicksLeft > 0 && moveDirection != ForgeDirection.UNKNOWN def isAnimatingMove = animationTicksLeft > 0 && moveDirection != ForgeDirection.UNKNOWN
def isAnimatingTurn = animationTicksLeft > 0 && turnOldFacing != ForgeDirection.UNKNOWN def isAnimatingSwing = animationTicksLeft > 0 && swingingTool
def animateMove(direction: ForgeDirection, duration: Double) { def isAnimatingTurn = animationTicksLeft > 0 && turnAxis != 0
animationTicksTotal = (duration * 20).toInt
animationTicksLeft = animationTicksTotal def animateMove(direction: ForgeDirection, duration: Double) =
moveDirection = direction setAnimateMove(direction, (duration * 20).toInt)
turnOldFacing = ForgeDirection.UNKNOWN
def animateSwing(duration: Double) = {
setAnimateSwing((duration * 20).toInt)
ServerPacketSender.sendRobotAnimateSwing(this)
} }
def animateTurn(oldFacing: ForgeDirection, duration: Double) { def animateTurn(clockwise: Boolean, duration: Double) = {
animationTicksTotal = (duration * 20).toInt setAnimateTurn(if (clockwise) 1 else -1, (duration * 20).toInt)
ServerPacketSender.sendRobotAnimateTurn(this)
}
def setAnimateMove(direction: ForgeDirection, ticks: Int) {
animationTicksTotal = ticks
prepareForAnimation()
moveDirection = direction
}
def setAnimateSwing(ticks: Int) {
animationTicksTotal = ticks
prepareForAnimation()
swingingTool = true
}
def setAnimateTurn(axis: Int, ticks: Int) {
animationTicksTotal = ticks
prepareForAnimation()
turnAxis = axis
}
private def prepareForAnimation() {
animationTicksLeft = animationTicksTotal animationTicksLeft = animationTicksTotal
moveDirection = ForgeDirection.UNKNOWN moveDirection = ForgeDirection.UNKNOWN
turnOldFacing = oldFacing swingingTool = false
turnAxis = 0
} }
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def getRenderBoundingBox =
getBlockType.getCollisionBoundingBoxFromPool(world, x, y, z).expand(0.5, 0.5, 0.5)
override def installedMemory = 64 * 1024 override def installedMemory = 64 * 1024
def tier = 0 def tier = 0
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
@LuaCallback("start")
def start(context: Context, args: Arguments): Array[AnyRef] =
result(computer.start())
@LuaCallback("stop")
def stop(context: Context, args: Arguments): Array[AnyRef] =
result(computer.stop())
@LuaCallback(value = "isRunning", direct = true)
def isRunning(context: Context, args: Arguments): Array[AnyRef] =
result(computer.isRunning)
// ----------------------------------------------------------------------- //
override def updateEntity() { override def updateEntity() {
if (node != null && node.network == null) {
api.Network.joinNewNetwork(computer.node)
}
if (animationTicksLeft > 0) { if (animationTicksLeft > 0) {
animationTicksLeft -= 1 animationTicksLeft -= 1
if (animationTicksLeft == 0) { if (animationTicksLeft == 0) {
@ -173,7 +195,6 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power
} }
override def validate() { override def validate() {
if (Blocks.robot.moving.get.isEmpty) {
super.validate() super.validate()
if (isServer) { if (isServer) {
items(0) match { items(0) match {
@ -186,21 +207,17 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power
ClientPacketSender.sendRobotStateRequest(this) ClientPacketSender.sendRobotStateRequest(this)
} }
} }
}
override def invalidate() { override def invalidate() {
if (Blocks.robot.moving.get.isEmpty) {
super.invalidate() super.invalidate()
if (currentGui.isDefined) { if (currentGui.isDefined) {
Minecraft.getMinecraft.displayGuiScreen(null) Minecraft.getMinecraft.displayGuiScreen(null)
} }
} }
}
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def readFromNBT(nbt: NBTTagCompound) { override def readFromNBT(nbt: NBTTagCompound) {
super.readFromNBT(nbt)
if (isServer) { if (isServer) {
battery.load(nbt.getCompoundTag(Config.namespace + "battery")) battery.load(nbt.getCompoundTag(Config.namespace + "battery"))
buffer.load(nbt.getCompoundTag(Config.namespace + "buffer")) buffer.load(nbt.getCompoundTag(Config.namespace + "buffer"))
@ -211,12 +228,14 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power
selectedSlot = nbt.getInteger(Config.namespace + "selectedSlot") selectedSlot = nbt.getInteger(Config.namespace + "selectedSlot")
animationTicksTotal = nbt.getInteger(Config.namespace + "animationTicksTotal") animationTicksTotal = nbt.getInteger(Config.namespace + "animationTicksTotal")
animationTicksLeft = nbt.getInteger(Config.namespace + "animationTicksLeft") animationTicksLeft = nbt.getInteger(Config.namespace + "animationTicksLeft")
moveDirection = ForgeDirection.getOrientation(nbt.getInteger(Config.namespace + "moveDirection")) if (animationTicksLeft > 0) {
turnOldFacing = ForgeDirection.getOrientation(nbt.getInteger(Config.namespace + "turnOldFacing")) moveDirection = ForgeDirection.getOrientation(nbt.getByte(Config.namespace + "moveDirection"))
swingingTool = nbt.getBoolean(Config.namespace + "swingingTool")
turnAxis = nbt.getByte(Config.namespace + "turnAxis")
}
} }
override def writeToNBT(nbt: NBTTagCompound) { override def writeToNBT(nbt: NBTTagCompound) {
super.writeToNBT(nbt)
if (isServer) { if (isServer) {
nbt.setNewCompoundTag(Config.namespace + "battery", battery.save) nbt.setNewCompoundTag(Config.namespace + "battery", battery.save)
nbt.setNewCompoundTag(Config.namespace + "buffer", buffer.save) nbt.setNewCompoundTag(Config.namespace + "buffer", buffer.save)
@ -225,27 +244,26 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power
nbt.setNewCompoundTag(Config.namespace + "keyboard", keyboard.save) nbt.setNewCompoundTag(Config.namespace + "keyboard", keyboard.save)
} }
nbt.setInteger(Config.namespace + "selectedSlot", selectedSlot) nbt.setInteger(Config.namespace + "selectedSlot", selectedSlot)
if (isAnimatingMove || isAnimatingTurn) { if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) {
nbt.setInteger(Config.namespace + "animationTicksTotal", animationTicksTotal) nbt.setInteger(Config.namespace + "animationTicksTotal", animationTicksTotal)
nbt.setInteger(Config.namespace + "animationTicksLeft", animationTicksLeft) nbt.setInteger(Config.namespace + "animationTicksLeft", animationTicksLeft)
nbt.setInteger(Config.namespace + "moveDirection", moveDirection.ordinal) nbt.setByte(Config.namespace + "moveDirection", moveDirection.ordinal.toByte)
nbt.setInteger(Config.namespace + "turnOldFacing", turnOldFacing.ordinal) nbt.setBoolean(Config.namespace + "swingingTool", swingingTool)
nbt.setByte(Config.namespace + "turnAxis", turnAxis.toByte)
} }
} }
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def onConnect(node: Node) { override def onConnect(node: Node) {
super.onConnect(node)
if (node == this.node) { if (node == this.node) {
api.Network.joinNewNetwork(computer.node)
computer.node.connect(buffer.node) computer.node.connect(buffer.node)
computer.node.connect(distributor.node) computer.node.connect(distributor.node)
computer.node.connect(gpu.node) computer.node.connect(gpu.node)
distributor.node.connect(battery) distributor.node.connect(battery)
buffer.node.connect(keyboard.node) buffer.node.connect(keyboard.node)
} }
super.onConnect(node)
} }
override def onDisconnect(node: Node) { override def onDisconnect(node: Node) {
@ -280,6 +298,12 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power
override def getInventoryStackLimit = 64 override def getInventoryStackLimit = 64
override def isUseableByPlayer(player: EntityPlayer) =
world.getBlockTileEntity(x, y, z) match {
case t: RobotProxy if t == proxy => player.getDistanceSq(x + 0.5, y + 0.5, z + 0.5) <= 64
case _ => false
}
def isItemValidForSlot(slot: Int, item: ItemStack) = (slot, Registry.driverFor(item)) match { def isItemValidForSlot(slot: Int, item: ItemStack) = (slot, Registry.driverFor(item)) match {
case (0, _) => true // Allow anything in the tool slot. case (0, _) => true // Allow anything in the tool slot.
case (1, Some(driver)) => driver.slot(item) == Slot.Card case (1, Some(driver)) => driver.slot(item) == Slot.Card
@ -288,19 +312,38 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power
case _ => false // Invalid slot. case _ => false // Invalid slot.
} }
override def onInventoryChanged() {
super.onInventoryChanged()
if (isServer) {
computer.signal("inventory_changed")
}
}
override protected def onItemRemoved(slot: Int, item: ItemStack) { override protected def onItemRemoved(slot: Int, item: ItemStack) {
super.onItemRemoved(slot, item) super.onItemRemoved(slot, item)
if (isServer) {
if (slot == 0) { if (slot == 0) {
player_.getAttributeMap.removeAttributeModifiers(item.getAttributeModifiers) player_.getAttributeMap.removeAttributeModifiers(item.getAttributeModifiers)
ServerPacketSender.sendRobotEquippedItemChange(this)
}
else if (slot >= actualSlot(0)) {
computer.signal("inventory_changed", Int.box(slot - actualSlot(0) + 1))
}
} }
} }
override protected def onItemAdded(slot: Int, item: ItemStack) { override protected def onItemAdded(slot: Int, item: ItemStack) {
if (isServer) {
if (slot == 0) { if (slot == 0) {
player_.getAttributeMap.applyAttributeModifiers(item.getAttributeModifiers) player_.getAttributeMap.applyAttributeModifiers(item.getAttributeModifiers)
ServerPacketSender.sendRobotEquippedItemChange(this)
} }
else if (slot == 1 || slot == 2) { else if (slot == 1 || slot == 2) {
super.onItemAdded(slot, item) super.onItemAdded(slot, item)
} }
else if (slot >= actualSlot(0)) {
computer.signal("inventory_changed", Int.box(slot - actualSlot(0) + 1))
}
}
} }
} }

View File

@ -0,0 +1,229 @@
package li.cil.oc.common.tileentity
import cpw.mods.fml.common.Optional
import li.cil.oc.api
import li.cil.oc.api.network._
import li.cil.oc.client.gui
import mods.immibis.redlogic.api.wiring.IWire
import net.minecraft.entity.Entity
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraftforge.common.ForgeDirection
class RobotProxy(val robot: Robot) extends Computer(robot.isClient) with Buffer with PowerInformation {
def this() = this(new Robot(false))
// ----------------------------------------------------------------------- //
@LuaCallback("start")
def start(context: Context, args: Arguments): Array[AnyRef] =
result(computer.start())
@LuaCallback("stop")
def stop(context: Context, args: Arguments): Array[AnyRef] =
result(computer.stop())
@LuaCallback(value = "isRunning", direct = true)
def isRunning(context: Context, args: Arguments): Array[AnyRef] =
result(computer.isRunning)
// ----------------------------------------------------------------------- //
override def onAnalyze(stats: NBTTagCompound, player: EntityPlayer, side: Int, hitX: Float, hitY: Float, hitZ: Float) = robot.onAnalyze(stats, player, side, hitX, hitY, hitZ)
// ----------------------------------------------------------------------- //
override val node = api.Network.newNode(this, Visibility.Network).
withComponent("computer", Visibility.Neighbors).
create()
override def isClient = robot.isClient
override def isServer = robot.isServer
// ----------------------------------------------------------------------- //
override def isOutputEnabled = robot.isOutputEnabled
override def isOutputEnabled_=(value: Boolean) = robot.isOutputEnabled_=(value)
override def input(side: ForgeDirection) = robot.input(side)
override def output(side: ForgeDirection) = robot.output(side)
override def output(side: ForgeDirection, value: Short) = robot.output(side, value)
override def bundledInput(side: ForgeDirection, color: Int) = robot.bundledInput(side, color)
override def bundledOutput(side: ForgeDirection, color: Int) = robot.bundledOutput(side, color)
override def bundledOutput(side: ForgeDirection, color: Int, value: Short) = robot.bundledOutput(side, color, value)
override def checkRedstoneInputChanged() = robot.checkRedstoneInputChanged()
override def updateRedstoneInput() = robot.updateRedstoneInput()
@Optional.Method(modid = "RedLogic")
override def connects(wire: IWire, blockFace: Int, fromDirection: Int) = robot.connects(wire, blockFace, fromDirection)
@Optional.Method(modid = "RedLogic")
override def connectsAroundCorner(wire: IWire, blockFace: Int, fromDirection: Int) = robot.connectsAroundCorner(wire, blockFace, fromDirection)
@Optional.Method(modid = "RedLogic")
override def getBundledCableStrength(blockFace: Int, toDirection: Int) = robot.getBundledCableStrength(blockFace, toDirection)
@Optional.Method(modid = "RedLogic")
override def getEmittedSignalStrength(blockFace: Int, toDirection: Int) = robot.getEmittedSignalStrength(blockFace, toDirection)
@Optional.Method(modid = "RedLogic")
override def onBundledInputChanged() = robot.onBundledInputChanged()
@Optional.Method(modid = "RedLogic")
override def onRedstoneInputChanged() = robot.onRedstoneInputChanged()
// ----------------------------------------------------------------------- //
override def pitch = robot.pitch
override def pitch_=(value: ForgeDirection) = robot.pitch_=(value)
override def yaw = robot.yaw
override def yaw_=(value: ForgeDirection) = robot.yaw_=(value)
override def setFromEntityPitchAndYaw(entity: Entity) = robot.setFromEntityPitchAndYaw(entity)
override def setFromFacing(value: ForgeDirection) = robot.setFromFacing(value)
override def invertRotation() = robot.invertRotation()
override def facing = robot.facing
override def rotate(axis: ForgeDirection) = robot.rotate(axis)
override def toLocal(value: ForgeDirection) = robot.toLocal(value)
override def toGlobal(value: ForgeDirection) = robot.toGlobal(value)
// ----------------------------------------------------------------------- //
override def getStackInSlot(i: Int) = robot.getStackInSlot(i)
override def decrStackSize(slot: Int, amount: Int) = robot.decrStackSize(slot, amount)
override def setInventorySlotContents(slot: Int, item: ItemStack) = robot.setInventorySlotContents(slot, item)
override def getStackInSlotOnClosing(slot: Int) = robot.getStackInSlotOnClosing(slot)
override def openChest() = robot.openChest()
override def closeChest() = robot.closeChest()
override def isInvNameLocalized = robot.isInvNameLocalized
override def isUseableByPlayer(player: EntityPlayer) = robot.isUseableByPlayer(player)
override def dropSlot(slot: Int, count: Int, direction: ForgeDirection) = robot.dropSlot(slot, count, direction)
override def dropAllSlots() = robot.dropAllSlots()
override def getInventoryStackLimit = robot.getInventoryStackLimit
override def installedMemory = robot.installedMemory
def getInvName = robot.getInvName
def getSizeInventory = robot.getSizeInventory
def isItemValidForSlot(slot: Int, stack: ItemStack) = robot.isItemValidForSlot(slot, stack)
// ----------------------------------------------------------------------- //
override def canUpdate = robot.canUpdate
override def updateEntity() = robot.updateEntity()
override def validate() {
super.validate()
val firstProxy = robot.proxy == null
robot.proxy = this
robot.worldObj = worldObj
robot.xCoord = xCoord
robot.yCoord = yCoord
robot.zCoord = zCoord
if (firstProxy) {
robot.validate()
}
}
override def invalidate() {
super.invalidate()
if (robot.proxy == this) {
robot.invalidate()
}
}
override def onChunkUnload() {
super.onChunkUnload()
if (robot.proxy == this) {
robot.onChunkUnload()
}
}
override def readFromNBT(nbt: NBTTagCompound) {
super.readFromNBT(nbt)
robot.readFromNBT(nbt)
}
override def writeToNBT(nbt: NBTTagCompound) {
super.writeToNBT(nbt)
robot.writeToNBT(nbt)
}
override def save(nbt: NBTTagCompound) = robot.save(nbt)
override def load(nbt: NBTTagCompound) = robot.load(nbt)
override def getMaxRenderDistanceSquared = robot.getMaxRenderDistanceSquared
override def getRenderBoundingBox = robot.getRenderBoundingBox
override def shouldRenderInPass(pass: Int) = robot.shouldRenderInPass(pass)
override def onInventoryChanged() = robot.onInventoryChanged()
// ----------------------------------------------------------------------- //
override def onMessage(message: Message) = robot.onMessage(message)
override def onConnect(node: Node) = robot.onConnect(node)
override def onDisconnect(node: Node) = robot.onDisconnect(node)
// ----------------------------------------------------------------------- //
override def computer = robot.computer
override def isOn = robot.isOn
override def isOn_=(value: Boolean) = robot.isOn_=(value)
override def markAsChanged() = robot.markAsChanged()
override def hasRedstoneCard = robot.hasRedstoneCard
// ----------------------------------------------------------------------- //
override def buffer = robot.buffer
override def bufferIsDirty = robot.bufferIsDirty
override def bufferIsDirty_=(value: Boolean) = robot.bufferIsDirty = value
override def currentGui = robot.currentGui
override def currentGui_=(value: Option[gui.Buffer]) = robot.currentGui = value
def tier = robot.tier
}

View File

@ -154,9 +154,9 @@ class Screen(var tier: Int) extends Buffer with Rotatable with Analyzable with O
if ((width == 1 && height == 1) || !isOrigin) super.getRenderBoundingBox if ((width == 1 && height == 1) || !isOrigin) super.getRenderBoundingBox
else { else {
val (sx, sy, sz) = unproject(width, height, 1) val (sx, sy, sz) = unproject(width, height, 1)
val ox = xCoord + (if (sx < 0) 1 else 0) val ox = x + (if (sx < 0) 1 else 0)
val oy = yCoord + (if (sy < 0) 1 else 0) val oy = y + (if (sy < 0) 1 else 0)
val oz = zCoord + (if (sz < 0) 1 else 0) val oz = z + (if (sz < 0) 1 else 0)
val b = AxisAlignedBB.getAABBPool.getAABB(ox, oy, oz, ox + sx, oy + sy, oz + sz) val b = AxisAlignedBB.getAABBPool.getAABB(ox, oy, oz, ox + sx, oy + sy, oz + sz)
b.setBounds(b.minX min b.maxX, b.minY min b.maxY, b.minZ min b.maxZ, b.setBounds(b.minX min b.maxX, b.minY min b.maxY, b.minZ min b.maxZ,
b.minX max b.maxX, b.minY max b.maxY, b.minZ max b.maxZ) b.minX max b.maxX, b.minY max b.maxY, b.minZ max b.maxZ)
@ -227,7 +227,7 @@ class Screen(var tier: Int) extends Buffer with Rotatable with Analyzable with O
} }
private def project(t: Screen) = { private def project(t: Screen) = {
def dot(f: ForgeDirection, s: Screen) = f.offsetX * s.xCoord + f.offsetY * s.yCoord + f.offsetZ * s.zCoord def dot(f: ForgeDirection, s: Screen) = f.offsetX * s.x + f.offsetY * s.y + f.offsetZ * s.z
(dot(toGlobal(ForgeDirection.EAST), t), dot(toGlobal(ForgeDirection.UP), t), dot(toGlobal(ForgeDirection.SOUTH), t)) (dot(toGlobal(ForgeDirection.EAST), t), dot(toGlobal(ForgeDirection.UP), t), dot(toGlobal(ForgeDirection.SOUTH), t))
} }

View File

@ -16,7 +16,7 @@ class PacketHandler extends CommonPacketHandler {
case PacketType.ComputerStateRequest => onComputerStateRequest(p) case PacketType.ComputerStateRequest => onComputerStateRequest(p)
case PacketType.PowerStateRequest => onPowerStateRequest(p) case PacketType.PowerStateRequest => onPowerStateRequest(p)
case PacketType.RedstoneStateRequest => onRedstoneStateRequest(p) case PacketType.RedstoneStateRequest => onRedstoneStateRequest(p)
case PacketType.RobotStateRequest => onRobotSelectedSlotRequest(p) case PacketType.RobotStateRequest => onRobotStateRequest(p)
case PacketType.RotatableStateRequest => onRotatableStateRequest(p) case PacketType.RotatableStateRequest => onRotatableStateRequest(p)
case PacketType.ScreenBufferRequest => onScreenBufferRequest(p) case PacketType.ScreenBufferRequest => onScreenBufferRequest(p)
case PacketType.KeyDown => onKeyDown(p) case PacketType.KeyDown => onKeyDown(p)
@ -43,9 +43,9 @@ class PacketHandler extends CommonPacketHandler {
case _ => // Invalid packet. case _ => // Invalid packet.
} }
def onRobotSelectedSlotRequest(p: PacketParser) = def onRobotStateRequest(p: PacketParser) =
p.readTileEntity[Robot]() match { p.readTileEntity[RobotProxy]() match {
case Some(t) => PacketSender.sendRobotSelectedSlotChange(t, Option(p.player)) case Some(t) => PacketSender.sendRobotState(t.robot, Option(p.player))
case _ => // Invalid packet. case _ => // Invalid packet.
} }

View File

@ -63,7 +63,7 @@ object PacketSender {
val pb = new PacketBuilder(PacketType.RobotMove) val pb = new PacketBuilder(PacketType.RobotMove)
// Custom pb.writeTileEntity() with fake coordinates (valid for the client). // Custom pb.writeTileEntity() with fake coordinates (valid for the client).
pb.writeInt(t.world.provider.dimensionId) pb.writeInt(t.proxy.world.provider.dimensionId)
pb.writeInt(ox) pb.writeInt(ox)
pb.writeInt(oy) pb.writeInt(oy)
pb.writeInt(oz) pb.writeInt(oz)
@ -72,27 +72,54 @@ object PacketSender {
pb.sendToAllPlayers() pb.sendToAllPlayers()
} }
def sendRobotSelectedSlotChange(t: Robot, player: Option[Player] = None) { def sendRobotAnimateSwing(t: Robot) {
val pb = new PacketBuilder(PacketType.RobotAnimateSwing)
pb.writeTileEntity(t.proxy)
pb.writeInt(t.animationTicksTotal)
pb.sendToNearbyPlayers(t.proxy)
}
def sendRobotAnimateTurn(t: Robot) {
val pb = new PacketBuilder(PacketType.RobotAnimateTurn)
pb.writeTileEntity(t.proxy)
pb.writeByte(t.turnAxis)
pb.writeInt(t.animationTicksTotal)
pb.sendToNearbyPlayers(t.proxy)
}
def sendRobotEquippedItemChange(t: Robot) {
val pb = new PacketBuilder(PacketType.RobotEquippedItemChange)
pb.writeTileEntity(t.proxy)
pb.writeItemStack(t.getStackInSlot(0))
pb.sendToAllPlayers()
}
def sendRobotSelectedSlotChange(t: Robot) {
val pb = new PacketBuilder(PacketType.RobotSelectedSlotChange) val pb = new PacketBuilder(PacketType.RobotSelectedSlotChange)
pb.writeTileEntity(t) pb.writeTileEntity(t.proxy)
pb.writeInt(t.selectedSlot) pb.writeInt(t.selectedSlot)
player match { pb.sendToAllPlayers()
case Some(p) => pb.sendToPlayer(p)
case _ => pb.sendToAllPlayers()
}
} }
def sendRobotState(t: Robot, player: Option[Player] = None) { def sendRobotState(t: Robot, player: Option[Player] = None) {
val pb = new PacketBuilder(PacketType.RobotStateResponse) val pb = new PacketBuilder(PacketType.RobotStateResponse)
pb.writeTileEntity(t) pb.writeTileEntity(t.proxy)
pb.writeInt(t.selectedSlot) pb.writeInt(t.selectedSlot)
pb.writeItemStack(t.getStackInSlot(0))
pb.writeInt(t.animationTicksTotal) pb.writeInt(t.animationTicksTotal)
pb.writeInt(t.animationTicksLeft) pb.writeInt(t.animationTicksLeft)
pb.writeDirection(t.moveDirection) pb.writeDirection(t.moveDirection)
pb.writeDirection(t.turnOldFacing) pb.writeBoolean(t.swingingTool)
pb.writeByte(t.turnAxis)
player match { player match {
case Some(p) => pb.sendToPlayer(p) case Some(p) => pb.sendToPlayer(p)

View File

@ -69,7 +69,7 @@ class Keyboard(owner: Environment) extends ManagedComponent {
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
private def isUseableByPlayer(p: EntityPlayer) = def isUseableByPlayer(p: EntityPlayer) =
owner.world.getBlockTileEntity(owner.x, owner.y, owner.z) == owner && owner.world.getBlockTileEntity(owner.x, owner.y, owner.z) == owner &&
p.getDistanceSq(owner.x + 0.5, owner.y + 0.5, owner.z + 0.5) <= 64 p.getDistanceSq(owner.x + 0.5, owner.y + 0.5, owner.z + 0.5) <= 64
} }

View File

@ -165,6 +165,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
} }
if (what) { if (what) {
context.pause(Config.placeDelay) context.pause(Config.placeDelay)
robot.animateSwing(Config.placeDelay)
} }
result(what) result(what)
} }
@ -207,17 +208,21 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
throw new IllegalArgumentException("invalid side") throw new IllegalArgumentException("invalid side")
} }
val player = robot.player(facing, side) val player = robot.player(facing, side)
def triggerDelay() = {
context.pause(Config.swingDelay)
robot.animateSwing(Config.swingDelay)
}
Option(pick(facing, side, Config.swingRange)) match { Option(pick(facing, side, Config.swingRange)) match {
case Some(hit) => case Some(hit) =>
val what = hit.typeOfHit match { val what = hit.typeOfHit match {
case EnumMovingObjectType.ENTITY => case EnumMovingObjectType.ENTITY =>
player.attackTargetEntityWithCurrentItem(hit.entityHit) player.attackTargetEntityWithCurrentItem(hit.entityHit)
context.pause(Config.swingDelay) triggerDelay()
result(true, "entity") result(true, "entity")
case EnumMovingObjectType.TILE => case EnumMovingObjectType.TILE =>
val broke = player.clickBlock(hit.blockX, hit.blockY, hit.blockZ, hit.sideHit) val broke = player.clickBlock(hit.blockX, hit.blockY, hit.blockZ, hit.sideHit)
if (broke) { if (broke) {
context.pause(Config.swingDelay) triggerDelay()
} }
result(broke, "block") result(broke, "block")
} }
@ -226,7 +231,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
player.closestLivingEntity(facing) match { player.closestLivingEntity(facing) match {
case Some(entity) => case Some(entity) =>
player.attackTargetEntityWithCurrentItem(entity) player.attackTargetEntityWithCurrentItem(entity)
context.pause(Config.swingDelay) triggerDelay()
result(true, "entity") result(true, "entity")
case _ => case _ =>
result(false) result(false)
@ -243,16 +248,20 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
} }
val sneaky = args.isBoolean(2) && args.checkBoolean(2) val sneaky = args.isBoolean(2) && args.checkBoolean(2)
val player = robot.player(facing, side) val player = robot.player(facing, side)
def triggerDelay() {
context.pause(Config.useDelay)
robot.animateSwing(Config.useDelay)
}
def activationResult(activationType: ActivationType.Value): Array[AnyRef] = def activationResult(activationType: ActivationType.Value): Array[AnyRef] =
activationType match { activationType match {
case ActivationType.BlockActivated => case ActivationType.BlockActivated =>
context.pause(Config.useDelay) triggerDelay()
result(true, "block_activated") result(true, "block_activated")
case ActivationType.ItemPlaced => case ActivationType.ItemPlaced =>
context.pause(Config.useDelay) triggerDelay()
result(true, "item_placed") result(true, "item_placed")
case ActivationType.ItemUsed => case ActivationType.ItemUsed =>
context.pause(Config.useDelay) triggerDelay()
result(true, "item_used") result(true, "item_used")
case _ => result(false) case _ => result(false)
} }
@ -275,7 +284,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
} else ActivationType.None) match { } else ActivationType.None) match {
case ActivationType.None => case ActivationType.None =>
if (player.useEquippedItem()) { if (player.useEquippedItem()) {
context.pause(Config.useDelay) triggerDelay()
result(true, "item_used") result(true, "item_used")
} }
else result(false) else result(false)
@ -321,10 +330,9 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
@LuaCallback("turn") @LuaCallback("turn")
def turn(context: Context, args: Arguments): Array[AnyRef] = { def turn(context: Context, args: Arguments): Array[AnyRef] = {
val clockwise = args.checkBoolean(0) val clockwise = args.checkBoolean(0)
val oldFacing = robot.facing
if (clockwise) robot.rotate(ForgeDirection.UP) if (clockwise) robot.rotate(ForgeDirection.UP)
else robot.rotate(ForgeDirection.DOWN) else robot.rotate(ForgeDirection.DOWN)
robot.animateTurn(oldFacing, 0.4) robot.animateTurn(clockwise, Config.turnDelay)
context.pause(Config.turnDelay) context.pause(Config.turnDelay)
result(true) result(true)
} }