Added ability to copy lines from screens using the analyzer (shift-rightclicking the line with the analyzer), then paste it on a screen by simply rightclicking it with the analyzer. Closes #644.

This commit is contained in:
Florian Nücke 2015-02-02 23:05:21 +01:00
parent 16170e31ee
commit f69250b565
7 changed files with 114 additions and 15 deletions

View File

@ -110,6 +110,15 @@ object PacketSender {
pb.sendToServer()
}
def sendCopyToAnalyzer(address: String, line: Int): Unit = {
val pb = new SimplePacketBuilder(PacketType.CopyToAnalyzer)
pb.writeUTF(address)
pb.writeInt(line)
pb.sendToServer()
}
def sendMultiPlace() {
val pb = new SimplePacketBuilder(PacketType.MultiPartPlace)
pb.sendToServer()

View File

@ -52,6 +52,7 @@ object PacketType extends Enumeration {
// Client -> Server
ComputerPower,
CopyToAnalyzer,
DronePower,
KeyDown,
KeyUp,

View File

@ -6,10 +6,12 @@ import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import li.cil.oc.OpenComputers
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.common.GuiType
import li.cil.oc.common.tileentity
import li.cil.oc.integration.util.Wrench
import li.cil.oc.util._
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.texture.IIconRegister
import net.minecraft.entity.Entity
import net.minecraft.entity.EntityLivingBase
@ -332,6 +334,7 @@ class Screen(val tier: Int) extends RedstoneAware {
def rightClick(world: World, x: Int, y: Int, z: Int, player: EntityPlayer,
side: ForgeDirection, hitX: Float, hitY: Float, hitZ: Float, force: Boolean) = {
if (Wrench.holdsApplicableWrench(player, BlockPosition(x, y, z)) && getValidRotations(world, x, y, z).contains(side) && !force) false
else if (api.Items.get(player.getHeldItem) == api.Items.get("analyzer")) false
else world.getTileEntity(x, y, z) match {
case screen: tileentity.Screen if screen.hasKeyboard && (force || player.isSneaking == screen.invertTouchMode) =>
// Yep, this GUI is actually purely client side. We could skip this
@ -342,7 +345,10 @@ class Screen(val tier: Int) extends RedstoneAware {
}
true
case screen: tileentity.Screen if screen.tier > 0 && side == screen.facing =>
screen.click(player, hitX, hitY, hitZ)
if (world.isRemote && player == Minecraft.getMinecraft.thePlayer) {
screen.click(hitX, hitY, hitZ)
}
else true
case _ => false
}
}

View File

@ -429,6 +429,10 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi
override def mouseScroll(x: Int, y: Int, delta: Int, player: EntityPlayer) =
mouseScroll(x, y, delta, player)
def copyToAnalyzer(line: Int, player: EntityPlayer): Unit = {
proxy.copyToAnalyzer(line, player)
}
// ----------------------------------------------------------------------- //
override def onConnect(node: Node) {
@ -602,6 +606,8 @@ object TextBuffer {
def mouseUp(x: Double, y: Double, button: Int, player: EntityPlayer): Unit
def mouseScroll(x: Double, y: Double, delta: Int, player: EntityPlayer): Unit
def copyToAnalyzer(line: Int, player: EntityPlayer): Unit
}
class ClientProxy(val owner: TextBuffer) extends Proxy {
@ -686,6 +692,10 @@ object TextBuffer {
ClientPacketSender.sendMouseScroll(nodeAddress, x, y, delta)
}
override def copyToAnalyzer(line: Int, player: EntityPlayer): Unit = {
ClientPacketSender.sendCopyToAnalyzer(nodeAddress, line)
}
private lazy val Debugger = api.Items.get("debugger")
private def debug(message: String) {
@ -789,6 +799,27 @@ object TextBuffer {
sendMouseEvent(player, "scroll", x, y, delta)
}
override def copyToAnalyzer(line: Int, player: EntityPlayer): Unit = {
val stack = player.getHeldItem
if (stack != null) {
if (!stack.hasTagCompound) {
stack.setTagCompound(new NBTTagCompound())
}
stack.getTagCompound.removeTag(Settings.namespace + "clipboard")
if (line >= 0 && line < owner.data.height) {
val text = new String(owner.data.buffer(line)).trim
if (!Strings.isNullOrEmpty(text)) {
stack.getTagCompound.setString(Settings.namespace + "clipboard", text)
}
}
if (stack.getTagCompound.hasNoTags) {
stack.setTagCompound(null)
}
}
}
private def sendMouseEvent(player: EntityPlayer, name: String, x: Double, y: Double, data: Int) = {
val args = mutable.ArrayBuffer.empty[AnyRef]

View File

@ -2,15 +2,18 @@ package li.cil.oc.common.item
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import li.cil.oc.Localization
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.network.Analyzable
import li.cil.oc.api.network._
import li.cil.oc.common.tileentity
import li.cil.oc.server.PacketSender
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedWorld._
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.item.ItemStack
import net.minecraft.world.World
import net.minecraftforge.common.util.ForgeDirection
import net.minecraftforge.event.entity.player.EntityInteractEvent
@ -79,7 +82,31 @@ object Analyzer {
}
class Analyzer(val parent: Delegator) extends Delegate {
override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ItemStack = {
if (player.isSneaking && stack.hasTagCompound) {
stack.getTagCompound.removeTag(Settings.namespace + "clipboard")
if (stack.getTagCompound.hasNoTags) {
stack.setTagCompound(null)
}
}
super.onItemRightClick(stack, world, player)
}
override def onItemUse(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: Int, hitX: Float, hitY: Float, hitZ: Float) = {
Analyzer.analyze(position.world.get.getTileEntity(position), player, side, hitX, hitY, hitZ)
val world = player.getEntityWorld
world.getTileEntity(position) match {
case screen: tileentity.Screen if ForgeDirection.getOrientation(side) == screen.facing =>
if (player.isSneaking) {
screen.copyToAnalyzer(hitX, hitY, hitZ)
}
else if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "clipboard")) {
if (!world.isRemote) {
screen.origin.buffer.clipboard(stack.getTagCompound.getString(Settings.namespace + "clipboard"), player)
}
true
}
else false
case _ => Analyzer.analyze(position.world.get.getTileEntity(position), player, side, hitX, hitY, hitZ)
}
}
}

View File

@ -5,6 +5,7 @@ import cpw.mods.fml.relauncher.SideOnly
import li.cil.oc.Settings
import li.cil.oc.api.network.Analyzable
import li.cil.oc.api.network._
import li.cil.oc.common.component.TextBuffer
import li.cil.oc.util.Color
import net.minecraft.client.Minecraft
import net.minecraft.entity.Entity
@ -93,7 +94,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
invertTouchMode = false
}
def click(player: EntityPlayer, hitX: Double, hitY: Double, hitZ: Double): Boolean = {
def toScreenCoordinates(hitX: Double, hitY: Double, hitZ: Double): (Boolean, Option[(Double, Double)]) = {
// Compute absolute position of the click on the face, measured in blocks.
def dot(f: ForgeDirection) = f.offsetX * hitX + f.offsetY * hitY + f.offsetZ * hitZ
val (hx, hy) = (dot(toGlobal(ForgeDirection.EAST)), dot(toGlobal(ForgeDirection.UP)))
@ -105,9 +106,9 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
// Get the relative position in the *display area* of the face.
val border = 2.25 / 16.0
if (ax <= border || ay <= border || ax >= width - border || ay >= height - border) {
return false
return (false, None)
}
if (!world.isRemote) return true
if (!world.isRemote) return (true, None)
val (iw, ih) = (width - border * 2, height - border * 2)
val (rx, ry) = ((ax - border) / iw, (ay - border) / ih)
@ -119,27 +120,43 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
val (brx, bry) = if (bpw > bph) {
val rh = bph.toDouble / bpw.toDouble
val bry = (ry - (1 - rh) * 0.5) / rh
if (bry <= 0 || bry >= 1) {
return true
}
(rx, bry)
}
else if (bph > bpw) {
val rw = bpw.toDouble / bph.toDouble
val brx = (rx - (1 - rw) * 0.5) / rw
if (brx <= 0 || brx >= 1) {
return true
}
(brx, ry)
}
else {
(rx, ry)
}
// Convert to absolute coordinates and send the packet to the server.
origin.buffer.mouseDown(brx * bw, bry * bh, 0, null)
val inBounds = bry >= 0 && bry <= 1 && brx >= 0 || brx <= 1
(inBounds, Some((brx * bw, bry * bh)))
}
true
def copyToAnalyzer(hitX: Double, hitY: Double, hitZ: Double): Boolean = {
val (inBounds, coordinates) = toScreenCoordinates(hitX, hitY, hitZ)
coordinates match {
case Some((x, y)) => origin.buffer match {
case buffer: TextBuffer =>
buffer.copyToAnalyzer(y.toInt, null)
true
case _ => false
}
case _ => inBounds
}
}
def click(hitX: Double, hitY: Double, hitZ: Double): Boolean = {
val (inBounds, coordinates) = toScreenCoordinates(hitX, hitY, hitZ)
coordinates match {
case Some((x, y)) =>
// Send the packet to the server (manually, for accuracy).
origin.buffer.mouseDown(x, y, 0, null)
true
case _ => inBounds
}
}
def walk(entity: Entity) {
@ -236,7 +253,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
hitXInner && !hitYInner && hitZInner ||
!hitXInner && hitYInner && hitZInner) {
arrow.shootingEntity match {
case player: EntityPlayer if player == Minecraft.getMinecraft.thePlayer => click(player, hitX, hitY, hitZ)
case player: EntityPlayer if player == Minecraft.getMinecraft.thePlayer => click(hitX, hitY, hitZ)
case _ =>
}
}

View File

@ -32,6 +32,7 @@ object PacketHandler extends CommonPacketHandler {
override def dispatch(p: PacketParser) {
p.packetType match {
case PacketType.ComputerPower => onComputerPower(p)
case PacketType.CopyToAnalyzer => onCopyToAnalyzer(p)
case PacketType.DronePower => onDronePower(p)
case PacketType.KeyDown => onKeyDown(p)
case PacketType.KeyUp => onKeyUp(p)
@ -67,6 +68,13 @@ object PacketHandler extends CommonPacketHandler {
case _ => // Invalid packet.
}
def onCopyToAnalyzer(p: PacketParser) {
ComponentTracker.get(p.player.worldObj, p.readUTF()) match {
case Some(buffer: TextBuffer) => buffer.copyToAnalyzer(p.readInt(), p.player.asInstanceOf[EntityPlayer])
case _ => // Invalid Packet
}
}
def onDronePower(p: PacketParser) =
p.readEntity[Drone]() match {
case Some(drone) => p.player match {