Reworked robot.durability() implementation, closes #666.

It now returns a single number, the relative durability (0 = broken, 1 = no damage), using durability provider methods that can be registered via IMC `registerToolDurabilityProvider` (takes a single string, the name of the class+method, e.g. `com.example.OCInterop.getDurability`), returns a double, NaN if unsupported item stack. Added custom providers for IC2, RF, GT and TCon.
This commit is contained in:
Florian Nücke 2014-11-14 14:56:59 +01:00
parent ccdc482588
commit d9f3487ff1
14 changed files with 194 additions and 29 deletions

View File

@ -4,11 +4,11 @@ import cpw.mods.fml.common.Mod.EventHandler
import cpw.mods.fml.common.event.FMLInterModComms.IMCEvent import cpw.mods.fml.common.event.FMLInterModComms.IMCEvent
import cpw.mods.fml.common.event._ import cpw.mods.fml.common.event._
import cpw.mods.fml.common.network.FMLEventChannel import cpw.mods.fml.common.network.FMLEventChannel
import cpw.mods.fml.common.Mod import cpw.mods.fml.common.{Mod, SidedProxy}
import cpw.mods.fml.common.SidedProxy
import li.cil.oc.common.Proxy
import li.cil.oc.common.template.AssemblerTemplates import li.cil.oc.common.template.AssemblerTemplates
import li.cil.oc.common.{Proxy, ToolDurabilityProviders}
import li.cil.oc.server.CommandHandler import li.cil.oc.server.CommandHandler
import net.minecraftforge.common.util.Constants.NBT
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import scala.collection.convert.WrapAsScala._ import scala.collection.convert.WrapAsScala._
@ -52,9 +52,16 @@ object OpenComputers {
def imc(e: IMCEvent) = { def imc(e: IMCEvent) = {
for (message <- e.getMessages) { for (message <- e.getMessages) {
if (message.key == "registerAssemblerTemplate" && message.isNBTMessage) { if (message.key == "registerAssemblerTemplate" && message.isNBTMessage) {
log.info(s"Registering new assembler template from mod ${message.getSender}.") if (message.getNBTValue.hasKey("name", NBT.TAG_STRING))
log.info(s"Registering new assembler template '${message.getNBTValue.getString("name")}' from mod ${message.getSender}.")
else
log.info(s"Registering new, unnamed assembler template from mod ${message.getSender}.")
AssemblerTemplates.add(message.getNBTValue) AssemblerTemplates.add(message.getNBTValue)
} }
else if (message.key == "registerToolDurabilityProvider" && message.isStringMessage) {
log.info(s"Registering new tool durability provider '${message.getStringValue}' from mod ${message.getSender}.")
ToolDurabilityProviders.add(message.getStringValue)
}
} }
} }
} }

View File

@ -0,0 +1,45 @@
package li.cil.oc.common
import java.lang.reflect.{Method, Modifier}
import li.cil.oc.OpenComputers
import net.minecraft.item.ItemStack
import scala.collection.mutable
object ToolDurabilityProviders {
private val providers = mutable.ArrayBuffer.empty[Method]
def add(name: String): Unit = try {
providers += getStaticMethod(name, classOf[ItemStack])
}
catch {
case t: Throwable => OpenComputers.log.warn("Failed registering tool durability provider.", t)
}
def getDurability(stack: ItemStack): Option[Double] = {
for (provider <- providers) {
val durability = tryInvokeStatic(provider, stack)(Double.NaN)
if (!durability.isNaN) return Option(durability)
}
// Fall back to vanilla damage values.
if (stack.isItemStackDamageable) Option(1.0 - stack.getItemDamage.toDouble / stack.getMaxDamage.toDouble)
else None
}
private def getStaticMethod(name: String, signature: Class[_]*) = {
val nameSplit = name.lastIndexOf('.')
val className = name.substring(0, nameSplit)
val methodName = name.substring(nameSplit + 1)
val clazz = Class.forName(className)
val method = clazz.getDeclaredMethod(methodName, signature: _*)
if (!Modifier.isStatic(method.getModifiers)) throw new IllegalArgumentException(s"Method $name is not static.")
method
}
private def tryInvokeStatic[T](method: Method, args: AnyRef*)(default: T): T = try method.invoke(null, args: _*).asInstanceOf[T] catch {
case t: Throwable =>
OpenComputers.log.warn(s"Error invoking callback ${method.getDeclaringClass.getCanonicalName + "." + method.getName}.", t)
default
}
}

View File

@ -21,7 +21,7 @@ import scala.collection.mutable
object AssemblerTemplates { object AssemblerTemplates {
val NoSlot = new Slot(Slot.None, Tier.None, None, None) val NoSlot = new Slot(Slot.None, Tier.None, None, None)
val templates = mutable.ArrayBuffer.empty[Template] private val templates = mutable.ArrayBuffer.empty[Template]
def add(template: NBTTagCompound): Unit = try { def add(template: NBTTagCompound): Unit = try {
val selector = getStaticMethod(template.getString("select"), classOf[ItemStack]) val selector = getStaticMethod(template.getString("select"), classOf[ItemStack])

View File

@ -46,6 +46,7 @@ object RobotTemplate extends Template {
// Tier 1 // Tier 1
{ {
val nbt = new NBTTagCompound() val nbt = new NBTTagCompound()
nbt.setString("name", "Robot (Tier 1)")
nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectTier1") nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectTier1")
nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate") nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate")
nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble") nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble")
@ -80,6 +81,7 @@ object RobotTemplate extends Template {
// Tier 2 // Tier 2
{ {
val nbt = new NBTTagCompound() val nbt = new NBTTagCompound()
nbt.setString("name", "Robot (Tier 2)")
nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectTier2") nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectTier2")
nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate") nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate")
nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble") nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble")
@ -117,6 +119,7 @@ object RobotTemplate extends Template {
// Tier 3 // Tier 3
{ {
val nbt = new NBTTagCompound() val nbt = new NBTTagCompound()
nbt.setString("name", "Robot (Tier 3)")
nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectTier3") nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectTier3")
nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate") nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate")
nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble") nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble")
@ -158,6 +161,7 @@ object RobotTemplate extends Template {
// Creative // Creative
{ {
val nbt = new NBTTagCompound() val nbt = new NBTTagCompound()
nbt.setString("name", "Robot (Creative)")
nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectCreative") nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectCreative")
nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate") nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate")
nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble") nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble")

View File

@ -43,6 +43,7 @@ object TabletTemplate extends Template {
def register() { def register() {
val nbt = new NBTTagCompound() val nbt = new NBTTagCompound()
nbt.setString("name", "Tablet")
nbt.setString("select", "li.cil.oc.common.template.TabletTemplate.select") nbt.setString("select", "li.cil.oc.common.template.TabletTemplate.select")
nbt.setString("validate", "li.cil.oc.common.template.TabletTemplate.validate") nbt.setString("validate", "li.cil.oc.common.template.TabletTemplate.validate")
nbt.setString("assemble", "li.cil.oc.common.template.TabletTemplate.assemble") nbt.setString("assemble", "li.cil.oc.common.template.TabletTemplate.assemble")

View File

@ -3,6 +3,7 @@ package li.cil.oc.integration.cofh.energy
import cofh.api.energy.IEnergyContainerItem import cofh.api.energy.IEnergyContainerItem
import cpw.mods.fml.common.eventhandler.SubscribeEvent import cpw.mods.fml.common.eventhandler.SubscribeEvent
import li.cil.oc.api.event.RobotUsedToolEvent import li.cil.oc.api.event.RobotUsedToolEvent
import net.minecraft.item.ItemStack
object EventHandlerRedstoneFlux { object EventHandlerRedstoneFlux {
@SubscribeEvent @SubscribeEvent
@ -22,4 +23,11 @@ object EventHandlerRedstoneFlux {
case _ => case _ =>
} }
} }
def getDurability(stack: ItemStack): Double = {
stack.getItem match {
case item: IEnergyContainerItem => item.getEnergyStored(stack).toDouble / item.getMaxEnergyStored(stack).toDouble
case _ => Double.NaN
}
}
} }

View File

@ -1,5 +1,6 @@
package li.cil.oc.integration.cofh.energy package li.cil.oc.integration.cofh.energy
import cpw.mods.fml.common.event.FMLInterModComms
import li.cil.oc.api.Driver import li.cil.oc.api.Driver
import li.cil.oc.integration.ModProxy import li.cil.oc.integration.ModProxy
import li.cil.oc.integration.Mods import li.cil.oc.integration.Mods
@ -9,10 +10,12 @@ object ModCoFHEnergy extends ModProxy {
override def getMod = Mods.CoFHEnergy override def getMod = Mods.CoFHEnergy
override def initialize() { override def initialize() {
Driver.add(new DriverEnergyHandler) FMLInterModComms.sendMessage("OpenComputers", "registerToolDurabilityProvider", "li.cil.oc.integration.cofh.energy.EventHandlerRedstoneFlux.getDurability")
MinecraftForge.EVENT_BUS.register(EventHandlerRedstoneFlux) MinecraftForge.EVENT_BUS.register(EventHandlerRedstoneFlux)
Driver.add(new DriverEnergyHandler)
Driver.add(new ConverterEnergyContainerItem) Driver.add(new ConverterEnergyContainerItem)
} }
} }

View File

@ -0,0 +1,34 @@
package li.cil.oc.integration.gregtech
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import gregtech.api.interfaces.IDamagableItem
import gregtech.api.items.GT_MetaGenerated_Tool
import li.cil.oc.api.event.RobotUsedToolEvent
import net.minecraft.item.ItemStack
object EventHandlerGregTech {
@SubscribeEvent
def onRobotApplyDamageRate(e: RobotUsedToolEvent.ApplyDamageRate) {
(e.toolBeforeUse.getItem, e.toolAfterUse.getItem) match {
case (itemBefore: IDamagableItem, itemAfter: IDamagableItem) =>
val damage = GT_MetaGenerated_Tool.getToolDamage(e.toolAfterUse) - GT_MetaGenerated_Tool.getToolDamage(e.toolBeforeUse)
if (damage > 0) {
val actualDamage = damage * e.getDamageRate
val repairedDamage =
if (e.robot.player.getRNG.nextDouble() > 0.5)
damage - math.floor(actualDamage).toInt
else
damage - math.ceil(actualDamage).toInt
GT_MetaGenerated_Tool.setToolDamage(e.toolAfterUse, GT_MetaGenerated_Tool.getToolDamage(e.toolAfterUse) - repairedDamage)
}
case _ =>
}
}
def getDurability(stack: ItemStack): Double = {
stack.getItem match {
case item: IDamagableItem => 1.0 - GT_MetaGenerated_Tool.getToolDamage(stack).toDouble / GT_MetaGenerated_Tool.getToolMaxDamage(stack).toDouble
case _ => Double.NaN
}
}
}

View File

@ -1,13 +1,18 @@
package li.cil.oc.integration.gregtech package li.cil.oc.integration.gregtech
import cpw.mods.fml.common.event.FMLInterModComms
import li.cil.oc.api.Driver import li.cil.oc.api.Driver
import li.cil.oc.integration.ModProxy import li.cil.oc.integration.{ModProxy, Mods}
import li.cil.oc.integration.Mods import net.minecraftforge.common.MinecraftForge
object ModGregtech extends ModProxy { object ModGregtech extends ModProxy {
override def getMod = Mods.GregTech override def getMod = Mods.GregTech
override def initialize() { override def initialize() {
FMLInterModComms.sendMessage("OpenComputers", "registerToolDurabilityProvider", "li.cil.oc.integration.gregtech.EventHandlerGregTech.getDurability")
MinecraftForge.EVENT_BUS.register(EventHandlerGregTech)
Driver.add(new DriverEnergyContainer) Driver.add(new DriverEnergyContainer)
} }
} }

View File

@ -0,0 +1,44 @@
package li.cil.oc.integration.ic2
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import ic2.api.item.{ElectricItem, IElectricItem, ISpecialElectricItem}
import li.cil.oc.api.event.RobotUsedToolEvent
import net.minecraft.item.ItemStack
object EventHandlerIndustrialCraft2 {
@SubscribeEvent
def onRobotApplyDamageRate(e: RobotUsedToolEvent.ApplyDamageRate) {
val optManagerBefore = e.toolBeforeUse.getItem match {
case item: ISpecialElectricItem => Option(item.getManager(e.toolBeforeUse))
case item: IElectricItem => Option(ElectricItem.manager)
case _ => None
}
val optManagerAfter = e.toolAfterUse.getItem match {
case item: ISpecialElectricItem => Option(item.getManager(e.toolAfterUse))
case item: IElectricItem => Option(ElectricItem.manager)
case _ => None
}
(optManagerBefore, optManagerAfter) match {
case (Some(managerBefore), Some(managerAfter)) =>
val damage = managerBefore.getCharge(e.toolBeforeUse) - managerAfter.getCharge(e.toolAfterUse)
if (damage > 0) {
val actualDamage = damage * e.getDamageRate
val repairedDamage =
if (e.robot.player.getRNG.nextDouble() > 0.5)
damage - math.floor(actualDamage).toInt
else
damage - math.ceil(actualDamage).toInt
managerAfter.charge(e.toolAfterUse, repairedDamage, Int.MaxValue, true, false)
}
case _ =>
}
}
def getDurability(stack: ItemStack): Double = {
stack.getItem match {
case item: ISpecialElectricItem => item.getManager(stack).getCharge(stack) / item.getMaxCharge(stack)
case item: IElectricItem => ElectricItem.manager.getCharge(stack) / item.getMaxCharge(stack)
case _ => Double.NaN
}
}
}

View File

@ -1,13 +1,18 @@
package li.cil.oc.integration.ic2 package li.cil.oc.integration.ic2
import cpw.mods.fml.common.event.FMLInterModComms
import li.cil.oc.api.Driver import li.cil.oc.api.Driver
import li.cil.oc.integration.ModProxy import li.cil.oc.integration.{ModProxy, Mods}
import li.cil.oc.integration.Mods import net.minecraftforge.common.MinecraftForge
object ModIndustrialCraft2 extends ModProxy { object ModIndustrialCraft2 extends ModProxy {
override def getMod = Mods.IndustrialCraft2 override def getMod = Mods.IndustrialCraft2
override def initialize() { override def initialize() {
FMLInterModComms.sendMessage("OpenComputers", "registerToolDurabilityProvider", "li.cil.oc.integration.ic2.EventHandlerIndustrialCraft2.getDurability")
MinecraftForge.EVENT_BUS.register(EventHandlerIndustrialCraft2)
Driver.add(new DriverEnergyConductor) Driver.add(new DriverEnergyConductor)
Driver.add(new DriverEnergySink) Driver.add(new DriverEnergySink)
Driver.add(new DriverEnergySource) Driver.add(new DriverEnergySource)
@ -15,6 +20,7 @@ object ModIndustrialCraft2 extends ModProxy {
Driver.add(new DriverMassFab) Driver.add(new DriverMassFab)
Driver.add(new DriverReactor) Driver.add(new DriverReactor)
Driver.add(new DriverReactorChamber) Driver.add(new DriverReactorChamber)
Driver.add(new ConverterElectricItem) Driver.add(new ConverterElectricItem)
} }
} }

View File

@ -24,4 +24,17 @@ object EventHandlerTinkersConstruct {
} }
} }
} }
def getDurability(stack: ItemStack): Double = {
if (isTinkerTool(stack)) {
val nbt = stack.getTagCompound.getCompoundTag("InfiTool")
if (nbt.getBoolean("Broken")) 0.0
else {
val damage = nbt.getInteger("Damage")
val maxDamage = nbt.getInteger("TotalDurability")
1.0 - damage.toDouble / maxDamage.toDouble
}
}
else Double.NaN
}
} }

View File

@ -1,5 +1,6 @@
package li.cil.oc.integration.tcon package li.cil.oc.integration.tcon
import cpw.mods.fml.common.event.FMLInterModComms
import li.cil.oc.integration.ModProxy import li.cil.oc.integration.ModProxy
import li.cil.oc.integration.Mods import li.cil.oc.integration.Mods
import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.MinecraftForge
@ -8,6 +9,8 @@ object ModTinkersConstruct extends ModProxy {
override def getMod = Mods.TinkersConstruct override def getMod = Mods.TinkersConstruct
override def initialize() { override def initialize() {
FMLInterModComms.sendMessage("OpenComputers", "registerToolDurabilityProvider", "li.cil.oc.integration.tcon.EventHandlerTinkersConstruct.getDurability")
MinecraftForge.EVENT_BUS.register(EventHandlerTinkersConstruct) MinecraftForge.EVENT_BUS.register(EventHandlerTinkersConstruct)
} }
} }

View File

@ -1,30 +1,22 @@
package li.cil.oc.server.component.robot package li.cil.oc.server.component.robot
import li.cil.oc.OpenComputers import li.cil.oc.{OpenComputers, Settings, api}
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.event.RobotPlaceInAirEvent import li.cil.oc.api.event.RobotPlaceInAirEvent
import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.{Arguments, Callback, Context}
import li.cil.oc.api.machine.Callback
import li.cil.oc.api.machine.Context
import li.cil.oc.api.network._ import li.cil.oc.api.network._
import li.cil.oc.api.prefab import li.cil.oc.api.prefab
import li.cil.oc.common.tileentity import li.cil.oc.common.{ToolDurabilityProviders, tileentity}
import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.InventoryUtils import li.cil.oc.util.InventoryUtils
import li.cil.oc.util.ResultWrapper.result import li.cil.oc.util.ResultWrapper.result
import net.minecraft.entity.Entity import net.minecraft.entity.{Entity, EntityLivingBase}
import net.minecraft.entity.EntityLivingBase import net.minecraft.entity.item.{EntityItem, EntityMinecart}
import net.minecraft.entity.item.EntityItem import net.minecraft.item.{ItemBlock, ItemStack}
import net.minecraft.entity.item.EntityMinecart
import net.minecraft.item.ItemBlock
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.MovingObjectPosition
import net.minecraft.util.MovingObjectPosition.MovingObjectType import net.minecraft.util.MovingObjectPosition.MovingObjectType
import net.minecraft.util.Vec3 import net.minecraft.util.{MovingObjectPosition, Vec3}
import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.common.util.ForgeDirection import net.minecraftforge.common.util.ForgeDirection
import net.minecraftforge.event.world.BlockEvent import net.minecraftforge.event.world.BlockEvent
@ -449,10 +441,10 @@ class Robot(val robot: tileentity.Robot) extends prefab.ManagedEnvironment {
def durability(context: Context, args: Arguments): Array[AnyRef] = { def durability(context: Context, args: Arguments): Array[AnyRef] = {
Option(robot.getStackInSlot(0)) match { Option(robot.getStackInSlot(0)) match {
case Some(item) => case Some(item) =>
if (item.isItemStackDamageable) { ToolDurabilityProviders.getDurability(item) match {
result(item.getMaxDamage - item.getItemDamage, item.getMaxDamage - item.getItemDamageForDisplay, item.getMaxDamage) case Some(durability) => result(durability)
case _ => result(Unit, "tool cannot be damaged")
} }
else result(Unit, "tool cannot be damaged")
case _ => result(Unit, "no tool equipped") case _ => result(Unit, "no tool equipped")
} }
} }