From cf70a33ea420b7d71777b0a3ddb64a70407d0949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sat, 11 Jan 2014 13:41:50 +0100 Subject: [PATCH] changed the way screen power consumption is computed. it's no only computed by the origin and no longer the actual number of pixels, but the relative number of pixels (based on the current resolution), with the cost being scaled based on the maximum resolution versus the resolution of the basic screen; screens can now be turned off to conserve power. turning them back on consumes as much energy as one cycle with a fully lit screen would, to avoid people trying to avoid the cost by turning screens off and on again at the right time (when the screen cost is actually computed, which doesn't happen every tick unless so configured - see power.tickFrequency) --- li/cil/oc/common/tileentity/Screen.scala | 91 +++++++++++++++++++----- 1 file changed, 75 insertions(+), 16 deletions(-) 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)