diff --git a/li/cil/oc/common/tileentity/Screen.scala b/li/cil/oc/common/tileentity/Screen.scala index 0256d3a01..de74690cd 100644 --- a/li/cil/oc/common/tileentity/Screen.scala +++ b/li/cil/oc/common/tileentity/Screen.scala @@ -2,9 +2,10 @@ package li.cil.oc.common.tileentity import cpw.mods.fml.relauncher.{Side, SideOnly} import li.cil.oc.Settings -import li.cil.oc.api.network.{SidedEnvironment, Analyzable, Visibility} +import li.cil.oc.api.network._ import li.cil.oc.client.renderer.MonospaceFontRenderer import li.cil.oc.client.{PacketSender => ClientPacketSender} +import li.cil.oc.common.component import li.cil.oc.server.{PacketSender => ServerPacketSender} import net.minecraft.client.Minecraft import net.minecraft.entity.Entity @@ -13,6 +14,7 @@ import net.minecraft.entity.projectile.EntityArrow import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.AxisAlignedBB import net.minecraftforge.common.ForgeDirection +import scala.Some import scala.collection.mutable class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable with Analyzable with Ordered[Screen] { @@ -20,9 +22,41 @@ class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable // ----------------------------------------------------------------------- // - val pixelCost = { + override protected val _buffer = new component.Buffer(this) { + @LuaCallback("isOn") + def isOn(computer: Context, args: Arguments): Array[AnyRef] = result(origin.isOn) + + @LuaCallback("turnOn") + def turnOn(computer: Context, args: Arguments): Array[AnyRef] = { + if (!origin.isOn) { + origin.isOn = true + val neededPower = width * height * Settings.get.screenCost * Settings.get.tickFrequency + origin.hasPower = node.changeBuffer(-neededPower) == 0 + ServerPacketSender.sendScreenPowerChange(origin, origin.isOn && origin.hasPower) + result(true, origin.isOn) + } + else result(false, origin.isOn) + } + + @LuaCallback("turnOff") + def turnOff(computer: Context, args: Arguments): Array[AnyRef] = { + if (origin.isOn) { + origin.isOn = false + ServerPacketSender.sendScreenPowerChange(origin, origin.isOn && origin.hasPower) + result(true, origin.isOn) + } + else result(false, origin.isOn) + } + } + + // This is the energy cost (per tick) to keep the screen running if every + // single "pixel" is lit. This cost increases with higher tiers as their + // maximum resolution (pixel density) increases. For a basic screen this is + // simply the configured cost. + val fullyLitCost = { val (w, h) = Settings.screenResolutionsByTier(0) - Settings.get.screenCost / (w * h) + val (mw, mh) = buffer.maxResolution + Settings.get.screenCost * (mw * mh) / (w * h) } /** @@ -37,10 +71,12 @@ class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable val screens = mutable.Set(this) - var litPixels = -1 + var relativeLitArea = -1.0 var hasPower = true + var isOn = true + var cachedBounds: Option[AxisAlignedBB] = None private val arrows = mutable.Set.empty[EntityArrow] @@ -153,18 +189,20 @@ class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable override def updateEntity() { super.updateEntity() - if (isServer && world.getWorldTime % Settings.get.tickFrequency == 0) { - if (litPixels < 0) { - litPixels = 0 - for (line <- buffer.lines) for (c <- line) { - if (c != ' ') litPixels += 1 - } + if (isServer && isOn && isOrigin && world.getWorldTime % Settings.get.tickFrequency == 0) { + if (relativeLitArea < 0) { + // The relative lit area is the number of pixels that are not blank + // versus the number of pixels in the *current* resolution. This is + // scaled to multi-block screens, since we only compute this for the + // origin. We add 1 to make sure we at least consume `screenCost`. + val (w, h) = buffer.resolution + relativeLitArea = 1 + width * height * buffer.lines.foldLeft(0) { (acc, line) => acc + line.count(' ' !=) } / (w * h).toDouble } val hadPower = hasPower - val neededPower = (Settings.get.screenCost + pixelCost * litPixels) * Settings.get.tickFrequency + val neededPower = relativeLitArea * fullyLitCost * Settings.get.tickFrequency hasPower = buffer.node.tryChangeBuffer(-neededPower) if (hasPower != hadPower) { - ServerPacketSender.sendScreenPowerChange(this, hasPower) + ServerPacketSender.sendScreenPowerChange(this, isOn && hasPower) } } if (shouldCheckForMultiBlock) { @@ -260,11 +298,32 @@ class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable override def readFromNBT(nbt: NBTTagCompound) { tier = nbt.getByte(Settings.namespace + "tier") max 0 min 2 super.readFromNBT(nbt) + // This check is just to avoid powering off any screens that have been + // placed before this was introduced. + if (nbt.hasKey(Settings.namespace + "isOn")) { + isOn = nbt.getBoolean(Settings.namespace + "isOn") + } + if (nbt.hasKey(Settings.namespace + "isOn")) { + hasPower = nbt.getBoolean(Settings.namespace + "hasPower") + } } override def writeToNBT(nbt: NBTTagCompound) { nbt.setByte(Settings.namespace + "tier", tier.toByte) super.writeToNBT(nbt) + nbt.setBoolean(Settings.namespace + "isOn", isOn) + nbt.setBoolean(Settings.namespace + "hasPower", hasPower) + } + + @SideOnly(Side.CLIENT) + override def readFromNBTForClient(nbt: NBTTagCompound) { + super.readFromNBTForClient(nbt) + hasPower = nbt.getBoolean("hasPower") + } + + override def writeToNBTForClient(nbt: NBTTagCompound) { + super.writeToNBTForClient(nbt) + nbt.setBoolean("hasPower", isOn && hasPower) } // ----------------------------------------------------------------------- // @@ -300,22 +359,22 @@ class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) { super.onScreenCopy(col, row, w, h, tx, ty) - litPixels = -1 + relativeLitArea = -1 } override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) { super.onScreenFill(col, row, w, h, c) - litPixels = -1 + relativeLitArea = -1 } override def onScreenResolutionChange(w: Int, h: Int) { super.onScreenResolutionChange(w, h) - litPixels = -1 + relativeLitArea = -1 } override def onScreenSet(col: Int, row: Int, s: String) { super.onScreenSet(col, row, s) - litPixels = -1 + relativeLitArea = -1 } @SideOnly(Side.CLIENT)