diff --git a/assets/items.psd b/assets/items.psd index 7b2ca3a50..70c88bab6 100644 Binary files a/assets/items.psd and b/assets/items.psd differ diff --git a/src/main/resources/assets/opencomputers/textures/items/tablet.png b/src/main/resources/assets/opencomputers/textures/items/tablet.png new file mode 100644 index 000000000..69878fb60 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/items/tablet.png differ diff --git a/src/main/scala/li/cil/oc/Items.scala b/src/main/scala/li/cil/oc/Items.scala index e497031be..c108e286f 100644 --- a/src/main/scala/li/cil/oc/Items.scala +++ b/src/main/scala/li/cil/oc/Items.scala @@ -228,5 +228,8 @@ object Items extends ItemAPI { Recipes.addItem(new item.UpgradeBattery(multi, Tier.One), "batteryUpgrade1", "oc:batteryUpgrade1") Recipes.addItem(new item.UpgradeBattery(multi, Tier.Two), "batteryUpgrade2", "oc:batteryUpgrade2") Recipes.addItem(new item.UpgradeBattery(multi, Tier.Three), "batteryUpgrade3", "oc:batteryUpgrade3") + + // Experimental + registerItem(new item.Tablet(multi), "tablet") } } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/client/GuiHandler.scala b/src/main/scala/li/cil/oc/client/GuiHandler.scala index 4863e4d3f..bba1c2ec2 100644 --- a/src/main/scala/li/cil/oc/client/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/client/GuiHandler.scala @@ -6,6 +6,8 @@ import li.cil.oc.{Items, Localization, Settings} import net.minecraft.client.Minecraft import net.minecraft.entity.player.EntityPlayer import net.minecraft.world.World +import li.cil.oc.api.component.TextBuffer +import li.cil.oc.common.item.Tablet object GuiHandler extends CommonGuiHandler { override def getClientGuiElement(id: Int, player: EntityPlayer, world: World, x: Int, y: Int, z: Int): AnyRef = @@ -33,6 +35,17 @@ object GuiHandler extends CommonGuiHandler { override def isUseableByPlayer(player: EntityPlayer) = player == player }) + case Some(tablet: item.Tablet) if id == GuiType.Tablet.id => + val stack = player.getCurrentEquippedItem + if (stack.hasTagCompound) { + Tablet.get(stack, player).components.collect { + case Some(buffer: TextBuffer) => buffer + }.headOption match { + case Some(buffer: TextBuffer) => return new gui.Screen(buffer, true, () => true, () => true) + case _ => + } + } + null case Some(terminal: item.Terminal) if id == GuiType.Terminal.id => val stack = player.getCurrentEquippedItem if (stack.hasTagCompound) { diff --git a/src/main/scala/li/cil/oc/common/GuiType.scala b/src/main/scala/li/cil/oc/common/GuiType.scala index ece602920..d87c60581 100644 --- a/src/main/scala/li/cil/oc/common/GuiType.scala +++ b/src/main/scala/li/cil/oc/common/GuiType.scala @@ -11,6 +11,7 @@ object GuiType extends Enumeration { Robot, Screen, Server, + Tablet, Terminal = Value diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index 3ceea9e72..315f11a96 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -7,6 +7,7 @@ import cpw.mods.fml.relauncher.Side import li.cil.oc._ import li.cil.oc.common.asm.SimpleComponentTickHandler import li.cil.oc.common.event._ +import li.cil.oc.common.item.Tablet import li.cil.oc.common.multipart.MultiPart import li.cil.oc.common.recipe.Recipes import li.cil.oc.server._ @@ -148,10 +149,13 @@ class Proxy { TickRegistry.registerTickHandler(EventHandler, Side.SERVER) TickRegistry.registerTickHandler(SimpleComponentTickHandler.Instance, Side.SERVER) + TickRegistry.registerTickHandler(Tablet, Side.CLIENT) + TickRegistry.registerTickHandler(Tablet, Side.SERVER) GameRegistry.registerPlayerTracker(Keyboard) NetworkRegistry.instance.registerConnectionHandler(EventHandler) MinecraftForge.EVENT_BUS.register(WirelessNetwork) MinecraftForge.EVENT_BUS.register(SaveHandler) + MinecraftForge.EVENT_BUS.register(Tablet) } private def registerExclusive(name: String, items: ItemStack*) { diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala new file mode 100644 index 000000000..01f0447a7 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala @@ -0,0 +1,286 @@ +package li.cil.oc.common.item + +import java.util +import java.util.UUID +import java.util.concurrent.{Callable, TimeUnit} + +import com.google.common.cache.{CacheBuilder, RemovalListener, RemovalNotification} +import cpw.mods.fml.common.{ITickHandler, TickType} +import li.cil.oc.api.Machine +import li.cil.oc.api.driver.Container +import li.cil.oc.api.machine.Owner +import li.cil.oc.api.network.{Connector, Message, Node} +import li.cil.oc.common.GuiType +import li.cil.oc.common.inventory.ComponentInventory +import li.cil.oc.{OpenComputers, Settings, api} +import net.minecraft.client.renderer.texture.IconRegister +import net.minecraft.entity.Entity +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.world.World +import net.minecraftforge.event.ForgeSubscribe +import net.minecraftforge.event.world.WorldEvent + +class Tablet(val parent: Delegator) extends Delegate { + val unlocalizedName = "Tablet" + + override def maxStackSize = 1 + + override def registerIcons(iconRegister: IconRegister) = { + super.registerIcons(iconRegister) + + icon_=(iconRegister.registerIcon(Settings.resourceDomain + ":tablet")) + } + + override def update(stack: ItemStack, world: World, player: Entity, slot: Int, selected: Boolean) = + Tablet.get(stack, player).update(world, player, slot, selected) + + override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer) = { + if (!player.isSneaking) { + if (world.isRemote) { + player.openGui(OpenComputers, GuiType.Tablet.id, world, 0, 0, 0) + } + else { + Tablet.get(stack, player).start() + } + player.swingItem() + } + super.onItemRightClick(stack, world, player) + } +} + +class TabletWrapper(val stack: ItemStack, var holder: Entity) extends ComponentInventory with Container with Owner { + lazy val computer = if (holder.worldObj.isRemote) null else Machine.create(this) + + val items = Array( + Option(api.Items.get("cpu1").createItemStack(1)), + Option(api.Items.get("ram4").createItemStack(1)), + Option(api.Items.get("screen1").createItemStack(1)), + Option(api.Items.get("keyboard").createItemStack(1)), + Option(api.Items.get("graphicsCard2").createItemStack(1)), + Option(api.Items.get("batteryUpgrade2").createItemStack(1)), + Option(api.Items.get("navigationUpgrade").createItemStack(1)), + Option(api.Items.get("openOS").createItemStack(1)), + Option(api.Items.get("wlanCard").createItemStack(1)) + ) + + def readFromNBT() { + if (stack.hasTagCompound) { + val data = stack.getTagCompound.getCompoundTag(Settings.namespace + "data") + if (!world.isRemote) { + computer.node.load(data) + } + load(data) + } + } + + def writeToNBT() { + if (!stack.hasTagCompound) { + stack.setTagCompound(new NBTTagCompound("tag")) + } + val nbt = stack.getTagCompound + if (!nbt.hasKey(Settings.namespace + "data")) { + nbt.setTag(Settings.namespace + "data", new NBTTagCompound()) + } + val data = stack.getTagCompound.getCompoundTag(Settings.namespace + "data") + if (!world.isRemote) { + computer.node.save(data) + } + save(data) + } + + readFromNBT() + if (world.isRemote) { + connectComponents() + components collect { + case Some(buffer: api.component.TextBuffer) => + buffer.setMaximumColorDepth(api.component.TextBuffer.ColorDepth.FourBit) + buffer.setMaximumResolution(80, 25) + } + } + else { + api.Network.joinNewNetwork(computer.node) + writeToNBT() + } + + // ----------------------------------------------------------------------- // + + override def onConnect(node: Node) { + if (node == this.node) { + connectComponents() + } + else node.host match { + case buffer: api.component.TextBuffer => + buffer.setMaximumColorDepth(api.component.TextBuffer.ColorDepth.FourBit) + buffer.setMaximumResolution(80, 25) + case _ => + } + } + + override def onDisconnect(node: Node) { + if (node == this.node) { + disconnectComponents() + } + } + + override def onMessage(message: Message) {} + + override def componentContainer = this + + override def getSizeInventory = items.length + + override def getInvName = Settings.namespace + "container.Tablet" + + override def isItemValidForSlot(slot: Int, stack: ItemStack) = true + + override def isUseableByPlayer(player: EntityPlayer) = canInteract(player.getCommandSenderName) + + override def onInventoryChanged() {} + + // ----------------------------------------------------------------------- // + + override def xPosition = holder.posX + + override def yPosition = holder.posY + 1 + + override def zPosition = holder.posZ + + override def markChanged() {} + + // ----------------------------------------------------------------------- // + + override def x = holder.posX.toInt + + override def y = holder.posY.toInt + 1 + + override def z = holder.posZ.toInt + + override def world = holder.worldObj + + override def installedMemory = items.foldLeft(0)((acc, itemOption) => acc + (itemOption match { + case Some(item) => Option(api.Driver.driverFor(item)) match { + case Some(driver: api.driver.Memory) => driver.amount(item) + case _ => 0 + } + case _ => 0 + })) + + override def maxComponents = items.foldLeft(0)((acc, itemOption) => acc + (itemOption match { + case Some(item) => Option(api.Driver.driverFor(item)) match { + case Some(driver: api.driver.Processor) => driver.supportedComponents(item) + case _ => 0 + } + case _ => 0 + })) + + override def markAsChanged() {} + + override def onMachineConnect(node: Node) = onConnect(node) + + override def onMachineDisconnect(node: Node) = onDisconnect(node) + + // ----------------------------------------------------------------------- // + + override def node = computer.node + + override def canInteract(player: String) = computer.canInteract(player) + + override def isRunning = computer.isRunning + + override def isPaused = computer.isPaused + + override def start() = computer.start() + + override def pause(seconds: Double) = computer.pause(seconds) + + override def stop() = computer.stop() + + override def signal(name: String, args: AnyRef*) = computer.signal(name, args) + + // ----------------------------------------------------------------------- // + + def update(world: World, player: Entity, slot: Int, selected: Boolean) { + holder = player + if (!world.isRemote) { + computer.node.asInstanceOf[Connector].changeBuffer(500) + computer.update() + updateComponents() + } + } +} + +object Tablet extends Callable[TabletWrapper] with RemovalListener[String, TabletWrapper] with ITickHandler { + val clientCache = com.google.common.cache.CacheBuilder.newBuilder(). + expireAfterAccess(10, TimeUnit.SECONDS). + removalListener(this). + asInstanceOf[CacheBuilder[String, TabletWrapper]]. + build[String, TabletWrapper]() + + val serverCache = com.google.common.cache.CacheBuilder.newBuilder(). + expireAfterAccess(10, TimeUnit.SECONDS). + removalListener(this). + asInstanceOf[CacheBuilder[String, TabletWrapper]]. + build[String, TabletWrapper]() + + // To allow access in cache entry init. + private var currentStack: ItemStack = _ + + private var currentHolder: Entity = _ + + def get(stack: ItemStack, holder: Entity) = clientCache.synchronized { + currentStack = stack + currentHolder = holder + if (!stack.hasTagCompound) { + stack.setTagCompound(new NBTTagCompound("tag")) + } + if (!stack.getTagCompound.hasKey(Settings.namespace + "tablet")) { + stack.getTagCompound.setString(Settings.namespace + "tablet", UUID.randomUUID().toString) + } + val id = stack.getTagCompound.getString(Settings.namespace + "tablet") + if (holder.worldObj.isRemote) + clientCache.get(id, this) + else + serverCache.get(id, this) + } + + def get(stack: ItemStack) = clientCache.synchronized { + if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "tablet")) { + val id = stack.getTagCompound.getString(Settings.namespace + "tablet") + Option(clientCache.getIfPresent(id)) + } + else None + } + + def call = { + new TabletWrapper(currentStack, currentHolder) + } + + def onRemoval(e: RemovalNotification[String, TabletWrapper]) { + val tablet = e.getValue + tablet.stop() + if (tablet.node != null) { + tablet.node.remove() + } + tablet.writeToNBT() + } + + @ForgeSubscribe + def onWorldUnload(e: WorldEvent.Unload) { + clientCache.invalidateAll() + clientCache.cleanUp() + serverCache.invalidateAll() + serverCache.cleanUp() + } + + override def getLabel = "OpenComputers Tablet Cleanup Ticker" + + override def ticks = util.EnumSet.of(TickType.CLIENT, TickType.SERVER) + + override def tickStart(tickType: util.EnumSet[TickType], tickData: AnyRef*) { + clientCache.cleanUp() + serverCache.cleanUp() + } + + override def tickEnd(tickType: util.EnumSet[TickType], tickData: AnyRef*) {} +} \ No newline at end of file