Allow specifying level of redstone signal emitted by prints.

Allow prints to emit light, if so configured. Max light level capped to 8 by default.
This commit is contained in:
Florian Nücke 2015-04-06 13:11:52 +02:00
parent bb14c64ce3
commit ffd0908567
7 changed files with 93 additions and 13 deletions

View File

@ -1045,6 +1045,11 @@ opencomputers {
# Whether Chamelium is edible or not. When eaten, it gives a (short) # Whether Chamelium is edible or not. When eaten, it gives a (short)
# invisibility buff, and (slightly longer) blindness debuff. # invisibility buff, and (slightly longer) blindness debuff.
chameliumEdible: true chameliumEdible: true
# The maximum light level a printed block can emit. This defaults to
# a value similar to that of a redstone torch, because by default the
# material prints are made of contains redstone, but no glowstone.
maxPrintLightLevel: 8
} }
# Settings for mod integration (the mod previously known as OpenComponents). # Settings for mod integration (the mod previously known as OpenComponents).

View File

@ -296,6 +296,7 @@ class Settings(val config: Config) {
val maxPrintComplexity = config.getInt("misc.maxPrinterShapes") val maxPrintComplexity = config.getInt("misc.maxPrinterShapes")
val printRecycleRate = config.getDouble("misc.printRecycleRate") val printRecycleRate = config.getDouble("misc.printRecycleRate")
val chameliumEdible = config.getBoolean("misc.chameliumEdible") val chameliumEdible = config.getBoolean("misc.chameliumEdible")
val maxPrintLightLevel = config.getInt("misc.maxPrintLightLevel") max 0 min 15
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
// integration // integration

View File

@ -56,6 +56,18 @@ class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends
data.tooltip.foreach(s => tooltip.addAll(s.lines.toIterable)) data.tooltip.foreach(s => tooltip.addAll(s.lines.toIterable))
} }
override def getLightValue(world: IBlockAccess, x: Int, y: Int, z: Int): Int =
world.getTileEntity(x, y, z) match {
case print: tileentity.Print => print.data.lightLevel
case _ => super.getLightValue(world, x, y, z)
}
override def getLightOpacity(world: IBlockAccess, x: Int, y: Int, z: Int): Int =
world.getTileEntity(x, y, z) match {
case print: tileentity.Print => (print.data.opacity * 4).toInt
case _ => super.getLightOpacity(world, x, y, z)
}
override def shouldSideBeRendered(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) = true override def shouldSideBeRendered(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) = true
override def isBlockSolid(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) = isSideSolid(world, x, y, z, side) override def isBlockSolid(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) = isSideSolid(world, x, y, z, side)

View File

@ -1,5 +1,6 @@
package li.cil.oc.common.item.data package li.cil.oc.common.item.data
import li.cil.oc.Settings
import li.cil.oc.api import li.cil.oc.api
import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ExtendedNBT._
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
@ -18,34 +19,54 @@ class PrintData extends ItemData {
var label: Option[String] = None var label: Option[String] = None
var tooltip: Option[String] = None var tooltip: Option[String] = None
var isButtonMode = false var isButtonMode = false
var emitRedstone = false var redstoneLevel = 0
var pressurePlate = false var pressurePlate = false
val stateOff = mutable.Set.empty[PrintData.Shape] val stateOff = mutable.Set.empty[PrintData.Shape]
val stateOn = mutable.Set.empty[PrintData.Shape] val stateOn = mutable.Set.empty[PrintData.Shape]
var isBeaconBase = false var isBeaconBase = false
var lightLevel = 0
def emitRedstone = redstoneLevel > 0
def opacity = {
if (opacityDirty) {
opacityDirty = false
opacity_ = PrintData.computeApproximateOpacity(stateOn) min PrintData.computeApproximateOpacity(stateOff)
}
opacity_
}
// lazily computed and stored, because potentially slow
private var opacity_ = 0f
private var opacityDirty = true
override def load(nbt: NBTTagCompound): Unit = { override def load(nbt: NBTTagCompound): Unit = {
if (nbt.hasKey("label")) label = Option(nbt.getString("label")) else label = None if (nbt.hasKey("label")) label = Option(nbt.getString("label")) else label = None
if (nbt.hasKey("tooltip")) tooltip = Option(nbt.getString("tooltip")) else tooltip = None if (nbt.hasKey("tooltip")) tooltip = Option(nbt.getString("tooltip")) else tooltip = None
isButtonMode = nbt.getBoolean("isButtonMode") isButtonMode = nbt.getBoolean("isButtonMode")
emitRedstone = nbt.getBoolean("emitRedstone") redstoneLevel = nbt.getInteger("redstoneLevel") max 0 min 15
if (nbt.getBoolean("emitRedstone")) redstoneLevel = 15
pressurePlate = nbt.getBoolean("pressurePlate") pressurePlate = nbt.getBoolean("pressurePlate")
stateOff.clear() stateOff.clear()
stateOff ++= nbt.getTagList("stateOff", NBT.TAG_COMPOUND).map(PrintData.nbtToShape) stateOff ++= nbt.getTagList("stateOff", NBT.TAG_COMPOUND).map(PrintData.nbtToShape)
stateOn.clear() stateOn.clear()
stateOn ++= nbt.getTagList("stateOn", NBT.TAG_COMPOUND).map(PrintData.nbtToShape) stateOn ++= nbt.getTagList("stateOn", NBT.TAG_COMPOUND).map(PrintData.nbtToShape)
isBeaconBase = nbt.getBoolean("isBeaconBase") isBeaconBase = nbt.getBoolean("isBeaconBase")
lightLevel = (nbt.getByte("lightLevel") & 0xFF) max 0 min Settings.get.maxPrintLightLevel
opacityDirty = true
} }
override def save(nbt: NBTTagCompound): Unit = { override def save(nbt: NBTTagCompound): Unit = {
label.foreach(nbt.setString("label", _)) label.foreach(nbt.setString("label", _))
tooltip.foreach(nbt.setString("tooltip", _)) tooltip.foreach(nbt.setString("tooltip", _))
nbt.setBoolean("isButtonMode", isButtonMode) nbt.setBoolean("isButtonMode", isButtonMode)
nbt.setBoolean("emitRedstone", emitRedstone) nbt.setInteger("redstoneLevel", redstoneLevel)
nbt.setBoolean("pressurePlate", pressurePlate) nbt.setBoolean("pressurePlate", pressurePlate)
nbt.setNewTagList("stateOff", stateOff.map(PrintData.shapeToNBT)) nbt.setNewTagList("stateOff", stateOff.map(PrintData.shapeToNBT))
nbt.setNewTagList("stateOn", stateOn.map(PrintData.shapeToNBT)) nbt.setNewTagList("stateOn", stateOn.map(PrintData.shapeToNBT))
nbt.setBoolean("isBeaconBase", isBeaconBase) nbt.setBoolean("isBeaconBase", isBeaconBase)
nbt.setByte("lightLevel", lightLevel.toByte)
} }
def createItemStack() = { def createItemStack() = {
@ -56,6 +77,32 @@ class PrintData extends ItemData {
} }
object PrintData { object PrintData {
// The following logic is used to approximate the opacity of a print, for
// which we use the volume as a heuristic. Because computing the actual
// volume is a) expensive b) not necessarily a good heuristic (e.g. a
// "dotted grid") we take a shortcut and divide the space into a few
// sub-sections, for each of which we check if there's anything in it.
// If so, we consider that area "opaque". To compensate, prints can never
// be fully light-opaque. This gives a little bit of shading as a nice
// effect, but avoid it looking derpy when there are only a few sparse
// shapes in the model.
private val stepping = 4
private val step = stepping / 16f
private val invMaxVolume = 1f / (stepping * stepping * stepping)
def computeApproximateOpacity(shapes: Iterable[PrintData.Shape]) = {
var volume = 1f
if (shapes.size > 0) for (x <- 0 until 16 / stepping; y <- 0 until 16 / stepping; z <- 0 until 16 / stepping) {
val bounds = AxisAlignedBB.getBoundingBox(
x * step, y * step, z * step,
(x + 1) * step, (y + 1) * step, (z + 1) * step)
if (!shapes.exists(_.bounds.intersectsWith(bounds))) {
volume -= invMaxVolume
}
}
volume
}
def nbtToShape(nbt: NBTTagCompound): Shape = { def nbtToShape(nbt: NBTTagCompound): Shape = {
val aabb = val aabb =
if (nbt.hasKey("minX")) { if (nbt.hasKey("minX")) {

View File

@ -33,7 +33,7 @@ class Print extends traits.TileEntity with traits.RedstoneAware with traits.Rota
world.playSoundEffect(x + 0.5, y + 0.5, z + 0.5, "random.click", 0.3F, if (state) 0.6F else 0.5F) world.playSoundEffect(x + 0.5, y + 0.5, z + 0.5, "random.click", 0.3F, if (state) 0.6F else 0.5F)
world.markBlockForUpdate(x, y, z) world.markBlockForUpdate(x, y, z)
if (data.emitRedstone) { if (data.emitRedstone) {
ForgeDirection.VALID_DIRECTIONS.foreach(output(_, if (state) 15 else 0)) ForgeDirection.VALID_DIRECTIONS.foreach(output(_, if (state) data.redstoneLevel else 0))
} }
if (state && data.isButtonMode) { if (state && data.isButtonMode) {
world.scheduleBlockUpdate(x, y, z, block, block.tickRate(world)) world.scheduleBlockUpdate(x, y, z, block, block.tickRate(world))

View File

@ -104,16 +104,29 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat
result(data.tooltip.orNull) result(data.tooltip.orNull)
} }
@Callback(doc = """function(value:boolean) -- Set whether the printed block should emit redstone when in its active state.""") @Callback(doc = """function(value:boolean or number) -- Set whether the printed block should emit redstone when in its active state.""")
def setRedstoneEmitter(context: Context, args: Arguments): Array[Object] = { def setRedstoneEmitter(context: Context, args: Arguments): Array[Object] = {
data.emitRedstone = args.checkBoolean(0) if (args.isBoolean(0)) data.redstoneLevel = if (args.checkBoolean(0)) 15 else 0
else data.redstoneLevel = args.checkInteger(0) max 0 min 15
isActive = false // Needs committing. isActive = false // Needs committing.
null null
} }
@Callback(doc = """function():boolean -- Get whether the printed block should emit redstone when in its active state.""") @Callback(doc = """function():boolean, number -- Get whether the printed block should emit redstone when in its active state.""")
def isRedstoneEmitter(context: Context, args: Arguments): Array[Object] = { def isRedstoneEmitter(context: Context, args: Arguments): Array[Object] = {
result(data.emitRedstone) result(data.emitRedstone, data.redstoneLevel)
}
@Callback(doc = """function(value:number) -- Set what light level the printed block should have.""")
def setLightLevel(context: Context, args: Arguments): Array[Object] = {
data.lightLevel = args.checkInteger(0) max 0 min Settings.get.maxPrintLightLevel
isActive = false // Needs committing.
null
}
@Callback(doc = """function():number -- Get which light level the printed block should have.""")
def getLightLevel(context: Context, args: Arguments): Array[Object] = {
result(data.lightLevel)
} }
@Callback(doc = """function(value:boolean) -- Set whether the printed block should automatically return to its off state.""") @Callback(doc = """function(value:boolean) -- Set whether the printed block should automatically return to its off state.""")

View File

@ -110,6 +110,8 @@ class PrintPart(val original: Option[tileentity.Print] = None) extends SimpleBlo
override def doesTick = false override def doesTick = false
override def getLightValue: Int = data.lightLevel
override def getBounds = new Cuboid6(if (state) boundsOn else boundsOff) override def getBounds = new Cuboid6(if (state) boundsOn else boundsOff)
override def getOcclusionBoxes = { override def getOcclusionBoxes = {
@ -143,7 +145,7 @@ class PrintPart(val original: Option[tileentity.Print] = None) extends SimpleBlo
override def strongPowerLevel(side: Int): Int = weakPowerLevel(side) override def strongPowerLevel(side: Int): Int = weakPowerLevel(side)
override def weakPowerLevel(side: Int): Int = if (data.emitRedstone && state) 15 else 0 override def weakPowerLevel(side: Int): Int = if (data.emitRedstone && state) data.redstoneLevel else 0
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
@ -266,11 +268,11 @@ class PrintPart(val original: Option[tileentity.Print] = None) extends SimpleBlo
} }
protected def computeInput(): Int = { protected def computeInput(): Int = {
val inner = tile.partList.foldLeft(false)((powered, part) => part match { val inner = tile.partList.foldLeft(0)((power, part) => part match {
case print: PrintPart => powered || (print.state && print.data.emitRedstone) case print: PrintPart if print.state && print.data.emitRedstone => math.max(power, print.data.redstoneLevel)
case _ => powered case _ => power
}) })
if (inner) 15 else ForgeDirection.VALID_DIRECTIONS.map(computeInput).max math.max(inner, ForgeDirection.VALID_DIRECTIONS.map(computeInput).max)
} }
protected def computeInput(side: ForgeDirection): Int = { protected def computeInput(side: ForgeDirection): Int = {