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 powerDistributor: PowerDistributor = null
var powerSupply: PowerConverter = null
var robot: Robot = null
var robotProxy: RobotProxy = null
var robotAfterimage: RobotAfterimage = 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.PowerConverter], Config.namespace + "power_converter")
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")
// IMPORTANT: the multi block must come first, since the sub blocks will
@ -53,7 +53,7 @@ object Blocks {
screen3 = new Screen.Tier3(blockSimple)
capacitor = new Capacitor(blockSimple)
robot = new Robot(blockSpecial)
robotProxy = new RobotProxy(blockSpecial)
robotAfterimage = new RobotAfterimage(blockSpecial)
}
}

View File

@ -12,8 +12,8 @@ object GuiHandler extends CommonGuiHandler {
new gui.Case(player.inventory, computer)
case drive: tileentity.DiskDrive if id == GuiType.DiskDrive.id =>
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

View File

@ -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.
}

View File

@ -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)

View File

@ -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)

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)
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
}
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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
}

View File

@ -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) {

View File

@ -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 _ =>
}
}

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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)
}
}

View File

@ -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))
}
}
}
}

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
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))
}

View File

@ -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.
}

View File

@ -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)

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 &&
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) {
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)
}