From ca606686d33e142074097df640047d755178f41d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Wed, 20 Nov 2013 20:50:35 +0100 Subject: [PATCH] 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 --- .../opencomputers/textures/blocks/robot.png | Bin 0 -> 648 bytes li/cil/oc/Blocks.scala | 6 +- li/cil/oc/client/GuiHandler.scala | 4 +- li/cil/oc/client/PacketHandler.scala | 43 ++- li/cil/oc/client/Proxy.scala | 3 +- .../renderer/tileentity/CableRenderer.scala | 2 +- .../renderer/tileentity/RobotRenderer.scala | 180 +++++++++++++ li/cil/oc/common/GuiHandler.scala | 4 +- li/cil/oc/common/PacketBuilder.scala | 7 +- li/cil/oc/common/PacketHandler.scala | 5 +- li/cil/oc/common/PacketType.scala | 53 ++-- li/cil/oc/common/block/Computer.scala | 8 - li/cil/oc/common/block/RobotAfterimage.scala | 2 +- .../block/{Robot.scala => RobotProxy.scala} | 10 +- li/cil/oc/common/tileentity/Buffer.scala | 16 +- li/cil/oc/common/tileentity/Computer.scala | 6 +- li/cil/oc/common/tileentity/Environment.scala | 2 +- li/cil/oc/common/tileentity/Robot.scala | 247 ++++++++++-------- li/cil/oc/common/tileentity/RobotProxy.scala | 229 ++++++++++++++++ li/cil/oc/common/tileentity/Screen.scala | 8 +- li/cil/oc/server/PacketHandler.scala | 8 +- li/cil/oc/server/PacketSender.scala | 45 +++- li/cil/oc/server/component/Keyboard.scala | 2 +- li/cil/oc/server/component/Robot.scala | 26 +- 24 files changed, 721 insertions(+), 195 deletions(-) create mode 100644 assets/opencomputers/textures/blocks/robot.png create mode 100644 li/cil/oc/client/renderer/tileentity/RobotRenderer.scala rename li/cil/oc/common/block/{Robot.scala => RobotProxy.scala} (89%) create mode 100644 li/cil/oc/common/tileentity/RobotProxy.scala diff --git a/assets/opencomputers/textures/blocks/robot.png b/assets/opencomputers/textures/blocks/robot.png new file mode 100644 index 0000000000000000000000000000000000000000..b6276d51ddc4ddf841696415f3cb557148587172 GIT binary patch literal 648 zcmV;30(bq1P)6(Ke3=9$yuDvg$+V1uK&$tuY9}8Bkd<=7BNCm$di+r>2g@x?$!PCi+!$TZn zXQ`#*gZHW~1nRpYQ%7{ z(h3%TG7r)sY*uN^MWVZ^gN587R~YmX8@DdPBHo#^V8;QWV-Aeny&#KL7HlCPt(FB= z0En!bhlG$CNp`nToLe}0`1#sG~g z0C~2D31KENuA2l>d`t+N!~g#C%Y>k9?EiC_5R}LM^D-eQkNu}*LQop}Ps@a$yycXv i@svL8J+*!JC%^!O1t1r(QI7!t0000 new gui.DiskDrive(player.inventory, drive) - case robot: tileentity.Robot if id == GuiType.Robot.id => - new gui.Robot(player.inventory, robot) + case proxy: tileentity.RobotProxy if id == GuiType.Robot.id => + new gui.Robot(player.inventory, proxy.robot) case screen: tileentity.Screen if id == GuiType.Screen.id => new gui.Screen(screen) case _ => null diff --git a/li/cil/oc/client/PacketHandler.scala b/li/cil/oc/client/PacketHandler.scala index 3c561f7c7..dd07ccf3a 100644 --- a/li/cil/oc/client/PacketHandler.scala +++ b/li/cil/oc/client/PacketHandler.scala @@ -27,6 +27,9 @@ class PacketHandler extends CommonPacketHandler { case PacketType.ComputerStateResponse => onComputerStateResponse(p) case PacketType.PowerStateResponse => onPowerStateResponse(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.RobotSelectedSlotChange => onRobotSelectedSlotChange(p) case PacketType.RobotStateResponse => onRobotStateResponse(p) @@ -80,26 +83,46 @@ class PacketHandler extends CommonPacketHandler { 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) = - p.readTileEntity[Robot]() match { - case Some(t) => t.move(p.readDirection()) + p.readTileEntity[RobotProxy]() match { + case Some(t) => t.robot.move(p.readDirection()) case _ => // Invalid packet. } def onRobotSelectedSlotChange(p: PacketParser) = - p.readTileEntity[Robot]() match { - case Some(t) => t.selectedSlot = p.readInt() + p.readTileEntity[RobotProxy]() match { + case Some(t) => t.robot.selectedSlot = p.readInt() case _ => // Invalid packet. } def onRobotStateResponse(p: PacketParser) = - p.readTileEntity[Robot]() match { + p.readTileEntity[RobotProxy]() match { case Some(t) => - t.selectedSlot = p.readInt() - t.animationTicksTotal = p.readInt() - t.animationTicksLeft = p.readInt() - t.moveDirection = p.readDirection() - t.turnOldFacing = p.readDirection() + t.robot.selectedSlot = p.readInt() + t.robot.equippedItem = Option(p.readItemStack()) + t.robot.animationTicksTotal = p.readInt() + t.robot.animationTicksLeft = p.readInt() + t.robot.moveDirection = p.readDirection() + t.robot.swingingTool = p.readBoolean() + t.robot.turnAxis = p.readByte() case _ => // Invalid packet. } diff --git a/li/cil/oc/client/Proxy.scala b/li/cil/oc/client/Proxy.scala index 761267c1d..8a7d2ae82 100644 --- a/li/cil/oc/client/Proxy.scala +++ b/li/cil/oc/client/Proxy.scala @@ -6,7 +6,7 @@ import cpw.mods.fml.common.network.NetworkRegistry import cpw.mods.fml.common.registry.TickRegistry import cpw.mods.fml.relauncher.Side 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.{Proxy => CommonProxy} 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.Case], CaseRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.PowerDistributor], PowerDistributorRenderer) + ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.RobotProxy], RobotRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Screen], ScreenRenderer) TickRegistry.registerTickHandler(ScreenRenderer, Side.CLIENT) diff --git a/li/cil/oc/client/renderer/tileentity/CableRenderer.scala b/li/cil/oc/client/renderer/tileentity/CableRenderer.scala index ccd5d4eda..1d38aa20d 100644 --- a/li/cil/oc/client/renderer/tileentity/CableRenderer.scala +++ b/li/cil/oc/client/renderer/tileentity/CableRenderer.scala @@ -125,7 +125,7 @@ object CableRenderer extends TileEntitySpecialRenderer { def renderTileEntityAt(t: TileEntity, x: Double, y: Double, z: Double, f: Float) { 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 l2 = l / 65536 OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, l1, l2) diff --git a/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala b/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala new file mode 100644 index 000000000..1fbcad6fc --- /dev/null +++ b/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala @@ -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() + } +} diff --git a/li/cil/oc/common/GuiHandler.scala b/li/cil/oc/common/GuiHandler.scala index 2726274ee..defbabd22 100644 --- a/li/cil/oc/common/GuiHandler.scala +++ b/li/cil/oc/common/GuiHandler.scala @@ -11,8 +11,8 @@ abstract class GuiHandler extends IGuiHandler { new container.Case(player.inventory, computer) case drive: tileentity.DiskDrive if id == GuiType.DiskDrive.id => new container.DiskDrive(player.inventory, drive) - case robot: tileentity.Robot if id == GuiType.Robot.id => - new container.Robot(player.inventory, robot) + case proxy: tileentity.RobotProxy if id == GuiType.Robot.id => + new container.Robot(player.inventory, proxy.robot) case _ => null } } \ No newline at end of file diff --git a/li/cil/oc/common/PacketBuilder.scala b/li/cil/oc/common/PacketBuilder.scala index 8e892a42c..f4d20f015 100644 --- a/li/cil/oc/common/PacketBuilder.scala +++ b/li/cil/oc/common/PacketBuilder.scala @@ -5,7 +5,8 @@ import cpw.mods.fml.common.network.Player import java.io.ByteArrayOutputStream import java.io.DataOutputStream 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.minecraftforge.common.ForgeDirection @@ -22,10 +23,14 @@ class PacketBuilder(packetType: PacketType.Value, private val stream: ByteArrayO 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 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 sendToServer() = PacketDispatcher.sendPacketToServer(packet) diff --git a/li/cil/oc/common/PacketHandler.scala b/li/cil/oc/common/PacketHandler.scala index 96016e1e3..d7fd0d02d 100644 --- a/li/cil/oc/common/PacketHandler.scala +++ b/li/cil/oc/common/PacketHandler.scala @@ -6,7 +6,8 @@ import java.io.ByteArrayInputStream import java.io.DataInputStream import java.util.logging.Level 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.packet.Packet250CustomPayload import net.minecraft.world.World @@ -62,6 +63,8 @@ abstract class PacketHandler extends IPacketHandler { def readDirection() = ForgeDirection.getOrientation(readInt()) + def readItemStack() = ItemStack.loadItemStackFromNBT(readNBT().asInstanceOf[NBTTagCompound]) + def readNBT() = NBTBase.readNamedTag(this) } diff --git a/li/cil/oc/common/PacketType.scala b/li/cil/oc/common/PacketType.scala index 25ba1943c..a9886e604 100644 --- a/li/cil/oc/common/PacketType.scala +++ b/li/cil/oc/common/PacketType.scala @@ -1,37 +1,40 @@ package li.cil.oc.common object PacketType extends Enumeration { - val ComputerStateRequest = Value("ComputerStateRequest") - val ComputerStateResponse = Value("ComputerStateResponse") + val ComputerStateRequest, + ComputerStateResponse, - val PowerStateRequest = Value("PowerStateRequest") - val PowerStateResponse = Value("PowerStateResponse") + PowerStateRequest, + PowerStateResponse, - val RedstoneStateRequest = Value("RedstoneStateRequest") - val RedstoneStateResponse = Value("RedstoneStateResponse") + RedstoneStateRequest, + RedstoneStateResponse, - val RobotMove = Value("RobotMove") - val RobotSelectedSlotChange = Value("RobotSelectedSlotChange") - val RobotStateRequest = Value("RobotSelectedSlotRequest") - val RobotStateResponse = Value("RobotStateResponse") + RobotAnimateSwing, + RobotAnimateTurn, + RobotEquippedItemChange, + RobotMove, + RobotSelectedSlotChange, + RobotStateRequest, + RobotStateResponse, - val RotatableStateRequest = Value("RotatableStateRequest") - val RotatableStateResponse = Value("RotatableStateResponse") + RotatableStateRequest, + RotatableStateResponse, - val ScreenBufferRequest = Value("ScreenBufferRequest") - val ScreenBufferResponse = Value("ScreenBufferResponse") + ScreenBufferRequest, + ScreenBufferResponse, - val ScreenColorChange = Value("ScreenColorChange") - val ScreenCopy = Value("ScreenCopy") - val ScreenDepthChange = Value("ScreenDepthChange") - val ScreenFill = Value("ScreenFill") - val ScreenPowerChange = Value("ScreenPowerChange") - val ScreenResolutionChange = Value("ScreenResolutionChange") - val ScreenSet = Value("ScreenSet") + ScreenColorChange, + ScreenCopy, + ScreenDepthChange, + ScreenFill, + ScreenPowerChange, + ScreenResolutionChange, + ScreenSet, - val KeyDown = Value("KeyDown") - val KeyUp = Value("KeyUp") - val Clipboard = Value("Clipboard") + KeyDown, + KeyUp, + Clipboard, - val Analyze = Value("Analyze") + Analyze = Value } \ No newline at end of file diff --git a/li/cil/oc/common/block/Computer.scala b/li/cil/oc/common/block/Computer.scala index 87ff3cf51..505b87a05 100644 --- a/li/cil/oc/common/block/Computer.scala +++ b/li/cil/oc/common/block/Computer.scala @@ -17,14 +17,6 @@ abstract class Computer extends Delegate { override def isProvidingWeakPower(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) = 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, side: ForgeDirection, hitX: Float, hitY: Float, hitZ: Float) = { if (!player.isSneaking) { diff --git a/li/cil/oc/common/block/RobotAfterimage.scala b/li/cil/oc/common/block/RobotAfterimage.scala index ed36587b7..ebafab37e 100644 --- a/li/cil/oc/common/block/RobotAfterimage.scala +++ b/li/cil/oc/common/block/RobotAfterimage.scala @@ -53,7 +53,7 @@ class RobotAfterimage(val parent: SpecialDelegator) extends SpecialDelegate { for (side <- ForgeDirection.VALID_DIRECTIONS) { val (rx, ry, rz) = (x + side.offsetX, y + side.offsetY, z + side.offsetZ) 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 _ => } } diff --git a/li/cil/oc/common/block/Robot.scala b/li/cil/oc/common/block/RobotProxy.scala similarity index 89% rename from li/cil/oc/common/block/Robot.scala rename to li/cil/oc/common/block/RobotProxy.scala index 835abb7cf..cfb845803 100644 --- a/li/cil/oc/common/block/Robot.scala +++ b/li/cil/oc/common/block/RobotProxy.scala @@ -7,7 +7,7 @@ import net.minecraft.util.{AxisAlignedBB, Vec3} import net.minecraft.world.{IBlockAccess, World} 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" 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) = { moving.get match { - case Some(robot) => Some(robot) - case _ => Some(new tileentity.Robot(world.isRemote)) + case Some(robot) => Some(new tileentity.RobotProxy(robot)) + 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) = { val bounds = parent.getCollisionBoundingBoxFromPool(world, x, y, z) - bounds.offset(x, y, z) if (bounds.isVecInside(origin)) null else super.collisionRayTrace(world, x, y, z, origin, direction) } override def setBlockBoundsBasedOnState(world: IBlockAccess, x: Int, y: Int, z: Int) { 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) if (robot.isAnimatingMove) { val remaining = robot.animationTicksLeft.toDouble / robot.animationTicksTotal.toDouble diff --git a/li/cil/oc/common/tileentity/Buffer.scala b/li/cil/oc/common/tileentity/Buffer.scala index 8f9b92038..8cf83785e 100644 --- a/li/cil/oc/common/tileentity/Buffer.scala +++ b/li/cil/oc/common/tileentity/Buffer.scala @@ -9,11 +9,21 @@ import li.cil.oc.util.{PackedColor, Persistable} import net.minecraft.nbt.NBTTagCompound 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 diff --git a/li/cil/oc/common/tileentity/Computer.scala b/li/cil/oc/common/tileentity/Computer.scala index f24db3eba..b56084377 100644 --- a/li/cil/oc/common/tileentity/Computer.scala +++ b/li/cil/oc/common/tileentity/Computer.scala @@ -11,7 +11,9 @@ import net.minecraftforge.common.ForgeDirection import scala.Some 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 @@ -52,7 +54,7 @@ abstract class Computer(isRemote: Boolean) extends Environment with ComponentInv if (hasChanged) { hasChanged = false - worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this) + world.markTileEntityChunkModified(x, y, z, this) } if (isRunning != computer.isRunning) { diff --git a/li/cil/oc/common/tileentity/Environment.scala b/li/cil/oc/common/tileentity/Environment.scala index 801949813..ebda2782a 100644 --- a/li/cil/oc/common/tileentity/Environment.scala +++ b/li/cil/oc/common/tileentity/Environment.scala @@ -23,7 +23,7 @@ abstract class Environment extends net.minecraft.tileentity.TileEntity with Tile override def updateEntity() { super.updateEntity() if (node != null && node.network == null) { - Network.joinOrCreateNetwork(worldObj, xCoord, yCoord, zCoord) + Network.joinOrCreateNetwork(world, x, y, z) } } diff --git a/li/cil/oc/common/tileentity/Robot.scala b/li/cil/oc/common/tileentity/Robot.scala index 8e1defc9b..4aee600cb 100644 --- a/li/cil/oc/common/tileentity/Robot.scala +++ b/li/cil/oc/common/tileentity/Robot.scala @@ -11,39 +11,43 @@ import li.cil.oc.server.{PacketSender => ServerPacketSender, component} import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.{Blocks, Config, api, common} import net.minecraft.client.Minecraft +import net.minecraft.entity.player.EntityPlayer import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraftforge.common.ForgeDirection import scala.Some -import scala.collection.convert.WrapAsScala._ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with PowerInformation { def this() = this(false) + var proxy: RobotProxy = _ + // ----------------------------------------------------------------------- // - override val node = api.Network.newNode(this, Visibility.Network). - withComponent("computer", Visibility.Neighbors). - create() + override def node = if (isClient) null else computer.node - override val buffer = new common.component.Buffer(this) { + override val buffer_ = new common.component.Buffer(this) { 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 = api.Network.newNode(this, Visibility.Network).withConnector(10000).create() val distributor = new component.PowerDistributor(this) val gpu = new GraphicsCard.Tier1 { 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) } else (null, null, null, null) var selectedSlot = 0 - private lazy val player_ = new Player(this) + var equippedItem: Option[ItemStack] = None var animationTicksLeft = 0 @@ -51,12 +55,15 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power 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) = { - assert(isServer) player_.updatePositionAndRotation(facing, side) player_ } @@ -66,92 +73,107 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power def move(direction: ForgeDirection) = { val (ox, oy, oz) = (x, y, z) val (nx, ny, nz) = (x + direction.offsetX, y + direction.offsetY, z + direction.offsetZ) - // 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 - // of creating a new one. - Blocks.robot.moving.set(Some(this)) - // 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 - // 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. val blockId = world.getBlockId(nx, ny, nz) val metadata = world.getBlockMetadata(nx, ny, nz) - val created = world.setBlock(nx, ny, nz, getBlockType.blockID, getBlockMetadata, 1) - if (created) { - assert(world.getBlockTileEntity(nx, ny, nz) == this) - assert(x == nx && y == ny && z == nz) - world.setBlock(ox, oy, oz, Blocks.robotAfterimage.parent.blockID, Blocks.robotAfterimage.blockId, 1) - assert(Blocks.blockSpecial.subBlock(world, ox, oy, oz).exists(_ == Blocks.robotAfterimage)) - if (isServer) { - ServerPacketSender.sendRobotMove(this, ox, oy, oz, direction) - for (neighbor <- node.neighbors) { - node.disconnect(neighbor) + try { + // 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 + // of creating a new one. + Blocks.robotProxy.moving.set(Some(this)) + // 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 + // 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. + val created = world.setBlock(nx, ny, nz, Blocks.robotProxy.parent.blockID, Blocks.robotProxy.blockId, 1) + if (created) { + assert(world.getBlockTileEntity(nx, ny, nz) == proxy) + assert(x == nx && y == ny && z == nz) + world.setBlock(ox, oy, oz, Blocks.robotAfterimage.parent.blockID, Blocks.robotAfterimage.blockId, 1) + assert(Blocks.blockSpecial.subBlock(world, ox, oy, oz).exists(_ == Blocks.robotAfterimage)) + if (isServer) { + ServerPacketSender.sendRobotMove(this, ox, oy, oz, direction) } - api.Network.joinOrCreateNetwork(world, nx, ny, nz) - } - else { - // On the client this is called from the packet handler code, leading - // 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) { - world.playAuxSFX(2001, nx, ny, nz, blockId + (metadata << 12)) + else { + // If we broke some replaceable block (like grass) play its break sound. + if (blockId > 0) { + world.playAuxSFX(2001, nx, ny, nz, blockId + (metadata << 12)) + } + world.markBlockForRenderUpdate(ox, oy, oz) + world.markBlockForRenderUpdate(nx, ny, nz) } - world.markBlockForUpdate(ox, oy, oz) - world.markBlockForUpdate(nx, ny, nz) + assert(!isInvalid) } - assert(!isInvalid) + if (created) { + // Here instead of Lua callback so that it gets triggered on client. + animateMove(direction, Config.moveDelay) + checkRedstoneInputChanged() + } + created } - Blocks.robot.moving.set(None) - if (created) { - animateMove(direction, Config.moveDelay) - checkRedstoneInputChanged() + finally { + Blocks.robotProxy.moving.set(None) } - created } 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) { - animationTicksTotal = (duration * 20).toInt - animationTicksLeft = animationTicksTotal - moveDirection = direction - turnOldFacing = ForgeDirection.UNKNOWN + def isAnimatingTurn = animationTicksLeft > 0 && turnAxis != 0 + + def animateMove(direction: ForgeDirection, duration: Double) = + setAnimateMove(direction, (duration * 20).toInt) + + def animateSwing(duration: Double) = { + setAnimateSwing((duration * 20).toInt) + ServerPacketSender.sendRobotAnimateSwing(this) } - def animateTurn(oldFacing: ForgeDirection, duration: Double) { - animationTicksTotal = (duration * 20).toInt + def animateTurn(clockwise: Boolean, duration: Double) = { + 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 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 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() { + if (node != null && node.network == null) { + api.Network.joinNewNetwork(computer.node) + } if (animationTicksLeft > 0) { animationTicksLeft -= 1 if (animationTicksLeft == 0) { @@ -173,34 +195,29 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power } override def validate() { - if (Blocks.robot.moving.get.isEmpty) { - super.validate() - if (isServer) { - items(0) match { - case Some(item) => player_.getAttributeMap.applyAttributeModifiers(item.getAttributeModifiers) - case _ => - } - } - else { - ClientPacketSender.sendScreenBufferRequest(this) - ClientPacketSender.sendRobotStateRequest(this) + super.validate() + if (isServer) { + items(0) match { + case Some(item) => player_.getAttributeMap.applyAttributeModifiers(item.getAttributeModifiers) + case _ => } } + else { + ClientPacketSender.sendScreenBufferRequest(this) + ClientPacketSender.sendRobotStateRequest(this) + } } override def invalidate() { - if (Blocks.robot.moving.get.isEmpty) { - super.invalidate() - if (currentGui.isDefined) { - Minecraft.getMinecraft.displayGuiScreen(null) - } + super.invalidate() + if (currentGui.isDefined) { + Minecraft.getMinecraft.displayGuiScreen(null) } } // ----------------------------------------------------------------------- // override def readFromNBT(nbt: NBTTagCompound) { - super.readFromNBT(nbt) if (isServer) { battery.load(nbt.getCompoundTag(Config.namespace + "battery")) 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") animationTicksTotal = nbt.getInteger(Config.namespace + "animationTicksTotal") animationTicksLeft = nbt.getInteger(Config.namespace + "animationTicksLeft") - moveDirection = ForgeDirection.getOrientation(nbt.getInteger(Config.namespace + "moveDirection")) - turnOldFacing = ForgeDirection.getOrientation(nbt.getInteger(Config.namespace + "turnOldFacing")) + if (animationTicksLeft > 0) { + 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) { - super.writeToNBT(nbt) if (isServer) { nbt.setNewCompoundTag(Config.namespace + "battery", battery.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.setInteger(Config.namespace + "selectedSlot", selectedSlot) - if (isAnimatingMove || isAnimatingTurn) { + if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) { nbt.setInteger(Config.namespace + "animationTicksTotal", animationTicksTotal) nbt.setInteger(Config.namespace + "animationTicksLeft", animationTicksLeft) - nbt.setInteger(Config.namespace + "moveDirection", moveDirection.ordinal) - nbt.setInteger(Config.namespace + "turnOldFacing", turnOldFacing.ordinal) + nbt.setByte(Config.namespace + "moveDirection", moveDirection.ordinal.toByte) + nbt.setBoolean(Config.namespace + "swingingTool", swingingTool) + nbt.setByte(Config.namespace + "turnAxis", turnAxis.toByte) } } // ----------------------------------------------------------------------- // override def onConnect(node: Node) { + super.onConnect(node) if (node == this.node) { - api.Network.joinNewNetwork(computer.node) - computer.node.connect(buffer.node) computer.node.connect(distributor.node) computer.node.connect(gpu.node) distributor.node.connect(battery) buffer.node.connect(keyboard.node) } - super.onConnect(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 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 { case (0, _) => true // Allow anything in the tool slot. 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. } + override def onInventoryChanged() { + super.onInventoryChanged() + if (isServer) { + computer.signal("inventory_changed") + } + } + override protected def onItemRemoved(slot: Int, item: ItemStack) { super.onItemRemoved(slot, item) - if (slot == 0) { - player_.getAttributeMap.removeAttributeModifiers(item.getAttributeModifiers) + if (isServer) { + if (slot == 0) { + 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) { - if (slot == 0) { - player_.getAttributeMap.applyAttributeModifiers(item.getAttributeModifiers) - } - else if (slot == 1 || slot == 2) { - super.onItemAdded(slot, item) + if (isServer) { + if (slot == 0) { + player_.getAttributeMap.applyAttributeModifiers(item.getAttributeModifiers) + ServerPacketSender.sendRobotEquippedItemChange(this) + } + else if (slot == 1 || slot == 2) { + super.onItemAdded(slot, item) + } + else if (slot >= actualSlot(0)) { + computer.signal("inventory_changed", Int.box(slot - actualSlot(0) + 1)) + } } } } diff --git a/li/cil/oc/common/tileentity/RobotProxy.scala b/li/cil/oc/common/tileentity/RobotProxy.scala new file mode 100644 index 000000000..9797c502f --- /dev/null +++ b/li/cil/oc/common/tileentity/RobotProxy.scala @@ -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 +} diff --git a/li/cil/oc/common/tileentity/Screen.scala b/li/cil/oc/common/tileentity/Screen.scala index 435eb2801..57bddd71f 100644 --- a/li/cil/oc/common/tileentity/Screen.scala +++ b/li/cil/oc/common/tileentity/Screen.scala @@ -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 else { val (sx, sy, sz) = unproject(width, height, 1) - val ox = xCoord + (if (sx < 0) 1 else 0) - val oy = yCoord + (if (sy < 0) 1 else 0) - val oz = zCoord + (if (sz < 0) 1 else 0) + val ox = x + (if (sx < 0) 1 else 0) + val oy = y + (if (sy < 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) 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) @@ -227,7 +227,7 @@ class Screen(var tier: Int) extends Buffer with Rotatable with Analyzable with O } 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)) } diff --git a/li/cil/oc/server/PacketHandler.scala b/li/cil/oc/server/PacketHandler.scala index cfe4dacd3..642ca3d7e 100644 --- a/li/cil/oc/server/PacketHandler.scala +++ b/li/cil/oc/server/PacketHandler.scala @@ -16,7 +16,7 @@ class PacketHandler extends CommonPacketHandler { case PacketType.ComputerStateRequest => onComputerStateRequest(p) case PacketType.PowerStateRequest => onPowerStateRequest(p) case PacketType.RedstoneStateRequest => onRedstoneStateRequest(p) - case PacketType.RobotStateRequest => onRobotSelectedSlotRequest(p) + case PacketType.RobotStateRequest => onRobotStateRequest(p) case PacketType.RotatableStateRequest => onRotatableStateRequest(p) case PacketType.ScreenBufferRequest => onScreenBufferRequest(p) case PacketType.KeyDown => onKeyDown(p) @@ -43,9 +43,9 @@ class PacketHandler extends CommonPacketHandler { case _ => // Invalid packet. } - def onRobotSelectedSlotRequest(p: PacketParser) = - p.readTileEntity[Robot]() match { - case Some(t) => PacketSender.sendRobotSelectedSlotChange(t, Option(p.player)) + def onRobotStateRequest(p: PacketParser) = + p.readTileEntity[RobotProxy]() match { + case Some(t) => PacketSender.sendRobotState(t.robot, Option(p.player)) case _ => // Invalid packet. } diff --git a/li/cil/oc/server/PacketSender.scala b/li/cil/oc/server/PacketSender.scala index c62fc36ed..1b13aee07 100644 --- a/li/cil/oc/server/PacketSender.scala +++ b/li/cil/oc/server/PacketSender.scala @@ -63,7 +63,7 @@ object PacketSender { val pb = new PacketBuilder(PacketType.RobotMove) // 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(oy) pb.writeInt(oz) @@ -72,27 +72,54 @@ object PacketSender { 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) - pb.writeTileEntity(t) + pb.writeTileEntity(t.proxy) pb.writeInt(t.selectedSlot) - player match { - case Some(p) => pb.sendToPlayer(p) - case _ => pb.sendToAllPlayers() - } + pb.sendToAllPlayers() } def sendRobotState(t: Robot, player: Option[Player] = None) { val pb = new PacketBuilder(PacketType.RobotStateResponse) - pb.writeTileEntity(t) + pb.writeTileEntity(t.proxy) pb.writeInt(t.selectedSlot) + pb.writeItemStack(t.getStackInSlot(0)) pb.writeInt(t.animationTicksTotal) pb.writeInt(t.animationTicksLeft) pb.writeDirection(t.moveDirection) - pb.writeDirection(t.turnOldFacing) + pb.writeBoolean(t.swingingTool) + pb.writeByte(t.turnAxis) player match { case Some(p) => pb.sendToPlayer(p) diff --git a/li/cil/oc/server/component/Keyboard.scala b/li/cil/oc/server/component/Keyboard.scala index e45620c8e..376631de9 100644 --- a/li/cil/oc/server/component/Keyboard.scala +++ b/li/cil/oc/server/component/Keyboard.scala @@ -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 && p.getDistanceSq(owner.x + 0.5, owner.y + 0.5, owner.z + 0.5) <= 64 } diff --git a/li/cil/oc/server/component/Robot.scala b/li/cil/oc/server/component/Robot.scala index 3aa9fcadd..8d90aa0fe 100644 --- a/li/cil/oc/server/component/Robot.scala +++ b/li/cil/oc/server/component/Robot.scala @@ -165,6 +165,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) { } if (what) { context.pause(Config.placeDelay) + robot.animateSwing(Config.placeDelay) } result(what) } @@ -207,17 +208,21 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) { throw new IllegalArgumentException("invalid side") } val player = robot.player(facing, side) + def triggerDelay() = { + context.pause(Config.swingDelay) + robot.animateSwing(Config.swingDelay) + } Option(pick(facing, side, Config.swingRange)) match { case Some(hit) => val what = hit.typeOfHit match { case EnumMovingObjectType.ENTITY => player.attackTargetEntityWithCurrentItem(hit.entityHit) - context.pause(Config.swingDelay) + triggerDelay() result(true, "entity") case EnumMovingObjectType.TILE => val broke = player.clickBlock(hit.blockX, hit.blockY, hit.blockZ, hit.sideHit) if (broke) { - context.pause(Config.swingDelay) + triggerDelay() } result(broke, "block") } @@ -226,7 +231,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) { player.closestLivingEntity(facing) match { case Some(entity) => player.attackTargetEntityWithCurrentItem(entity) - context.pause(Config.swingDelay) + triggerDelay() result(true, "entity") case _ => result(false) @@ -243,16 +248,20 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) { } val sneaky = args.isBoolean(2) && args.checkBoolean(2) val player = robot.player(facing, side) + def triggerDelay() { + context.pause(Config.useDelay) + robot.animateSwing(Config.useDelay) + } def activationResult(activationType: ActivationType.Value): Array[AnyRef] = activationType match { case ActivationType.BlockActivated => - context.pause(Config.useDelay) + triggerDelay() result(true, "block_activated") case ActivationType.ItemPlaced => - context.pause(Config.useDelay) + triggerDelay() result(true, "item_placed") case ActivationType.ItemUsed => - context.pause(Config.useDelay) + triggerDelay() result(true, "item_used") case _ => result(false) } @@ -275,7 +284,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) { } else ActivationType.None) match { case ActivationType.None => if (player.useEquippedItem()) { - context.pause(Config.useDelay) + triggerDelay() result(true, "item_used") } else result(false) @@ -321,10 +330,9 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) { @LuaCallback("turn") def turn(context: Context, args: Arguments): Array[AnyRef] = { val clockwise = args.checkBoolean(0) - val oldFacing = robot.facing if (clockwise) robot.rotate(ForgeDirection.UP) else robot.rotate(ForgeDirection.DOWN) - robot.animateTurn(oldFacing, 0.4) + robot.animateTurn(clockwise, Config.turnDelay) context.pause(Config.turnDelay) result(true) }