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)

This commit is contained in:
Florian Nücke 2014-01-11 13:41:50 +01:00
parent cce2d4fa6e
commit cf70a33ea4

View File

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