From d9f3487ff1e5f6daf84c90800c159ef5a7480806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Fri, 14 Nov 2014 14:56:59 +0100 Subject: [PATCH] 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. --- src/main/scala/li/cil/oc/OpenComputers.scala | 15 +++++-- .../oc/common/ToolDurabilityProviders.scala | 45 +++++++++++++++++++ .../common/template/AssemblerTemplates.scala | 2 +- .../oc/common/template/RobotTemplate.scala | 4 ++ .../oc/common/template/TabletTemplate.scala | 1 + .../energy/EventHandlerRedstoneFlux.scala | 8 ++++ .../cofh/energy/ModCoFHEnergy.scala | 5 ++- .../gregtech/EventHandlerGregTech.scala | 34 ++++++++++++++ .../oc/integration/gregtech/ModGregtech.scala | 9 +++- .../ic2/EventHandlerIndustrialCraft2.scala | 44 ++++++++++++++++++ .../integration/ic2/ModIndustrialCraft2.scala | 12 +++-- .../tcon/EventHandlerTinkersConstruct.scala | 13 ++++++ .../tcon/ModTinkersConstruct.scala | 3 ++ .../cil/oc/server/component/robot/Robot.scala | 28 +++++------- 14 files changed, 194 insertions(+), 29 deletions(-) create mode 100644 src/main/scala/li/cil/oc/common/ToolDurabilityProviders.scala create mode 100644 src/main/scala/li/cil/oc/integration/gregtech/EventHandlerGregTech.scala create mode 100644 src/main/scala/li/cil/oc/integration/ic2/EventHandlerIndustrialCraft2.scala diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index 56e5d4199..b620a9b1b 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -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._ import cpw.mods.fml.common.network.FMLEventChannel -import cpw.mods.fml.common.Mod -import cpw.mods.fml.common.SidedProxy -import li.cil.oc.common.Proxy +import cpw.mods.fml.common.{Mod, SidedProxy} import li.cil.oc.common.template.AssemblerTemplates +import li.cil.oc.common.{Proxy, ToolDurabilityProviders} import li.cil.oc.server.CommandHandler +import net.minecraftforge.common.util.Constants.NBT import org.apache.logging.log4j.LogManager import scala.collection.convert.WrapAsScala._ @@ -52,9 +52,16 @@ object OpenComputers { def imc(e: IMCEvent) = { for (message <- e.getMessages) { 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) } + 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) + } } } } diff --git a/src/main/scala/li/cil/oc/common/ToolDurabilityProviders.scala b/src/main/scala/li/cil/oc/common/ToolDurabilityProviders.scala new file mode 100644 index 000000000..61340d24f --- /dev/null +++ b/src/main/scala/li/cil/oc/common/ToolDurabilityProviders.scala @@ -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 + } +} diff --git a/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala b/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala index 671959670..c43766a50 100644 --- a/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala +++ b/src/main/scala/li/cil/oc/common/template/AssemblerTemplates.scala @@ -21,7 +21,7 @@ import scala.collection.mutable object AssemblerTemplates { 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 { val selector = getStaticMethod(template.getString("select"), classOf[ItemStack]) diff --git a/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala b/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala index 1ae914100..982a229d5 100644 --- a/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala @@ -46,6 +46,7 @@ object RobotTemplate extends Template { // Tier 1 { val nbt = new NBTTagCompound() + nbt.setString("name", "Robot (Tier 1)") nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectTier1") nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate") nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble") @@ -80,6 +81,7 @@ object RobotTemplate extends Template { // Tier 2 { val nbt = new NBTTagCompound() + nbt.setString("name", "Robot (Tier 2)") nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectTier2") nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate") nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble") @@ -117,6 +119,7 @@ object RobotTemplate extends Template { // Tier 3 { val nbt = new NBTTagCompound() + nbt.setString("name", "Robot (Tier 3)") nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectTier3") nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate") nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble") @@ -158,6 +161,7 @@ object RobotTemplate extends Template { // Creative { val nbt = new NBTTagCompound() + nbt.setString("name", "Robot (Creative)") nbt.setString("select", "li.cil.oc.common.template.RobotTemplate.selectCreative") nbt.setString("validate", "li.cil.oc.common.template.RobotTemplate.validate") nbt.setString("assemble", "li.cil.oc.common.template.RobotTemplate.assemble") diff --git a/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala b/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala index 02b233521..5144336c9 100644 --- a/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala +++ b/src/main/scala/li/cil/oc/common/template/TabletTemplate.scala @@ -43,6 +43,7 @@ object TabletTemplate extends Template { def register() { val nbt = new NBTTagCompound() + nbt.setString("name", "Tablet") nbt.setString("select", "li.cil.oc.common.template.TabletTemplate.select") nbt.setString("validate", "li.cil.oc.common.template.TabletTemplate.validate") nbt.setString("assemble", "li.cil.oc.common.template.TabletTemplate.assemble") diff --git a/src/main/scala/li/cil/oc/integration/cofh/energy/EventHandlerRedstoneFlux.scala b/src/main/scala/li/cil/oc/integration/cofh/energy/EventHandlerRedstoneFlux.scala index 542ed8061..19265667d 100644 --- a/src/main/scala/li/cil/oc/integration/cofh/energy/EventHandlerRedstoneFlux.scala +++ b/src/main/scala/li/cil/oc/integration/cofh/energy/EventHandlerRedstoneFlux.scala @@ -3,6 +3,7 @@ package li.cil.oc.integration.cofh.energy import cofh.api.energy.IEnergyContainerItem import cpw.mods.fml.common.eventhandler.SubscribeEvent import li.cil.oc.api.event.RobotUsedToolEvent +import net.minecraft.item.ItemStack object EventHandlerRedstoneFlux { @SubscribeEvent @@ -22,4 +23,11 @@ object EventHandlerRedstoneFlux { case _ => } } + + def getDurability(stack: ItemStack): Double = { + stack.getItem match { + case item: IEnergyContainerItem => item.getEnergyStored(stack).toDouble / item.getMaxEnergyStored(stack).toDouble + case _ => Double.NaN + } + } } diff --git a/src/main/scala/li/cil/oc/integration/cofh/energy/ModCoFHEnergy.scala b/src/main/scala/li/cil/oc/integration/cofh/energy/ModCoFHEnergy.scala index 190be5329..49c425d1c 100644 --- a/src/main/scala/li/cil/oc/integration/cofh/energy/ModCoFHEnergy.scala +++ b/src/main/scala/li/cil/oc/integration/cofh/energy/ModCoFHEnergy.scala @@ -1,5 +1,6 @@ package li.cil.oc.integration.cofh.energy +import cpw.mods.fml.common.event.FMLInterModComms import li.cil.oc.api.Driver import li.cil.oc.integration.ModProxy import li.cil.oc.integration.Mods @@ -9,10 +10,12 @@ object ModCoFHEnergy extends ModProxy { override def getMod = Mods.CoFHEnergy override def initialize() { - Driver.add(new DriverEnergyHandler) + FMLInterModComms.sendMessage("OpenComputers", "registerToolDurabilityProvider", "li.cil.oc.integration.cofh.energy.EventHandlerRedstoneFlux.getDurability") MinecraftForge.EVENT_BUS.register(EventHandlerRedstoneFlux) + Driver.add(new DriverEnergyHandler) + Driver.add(new ConverterEnergyContainerItem) } } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/gregtech/EventHandlerGregTech.scala b/src/main/scala/li/cil/oc/integration/gregtech/EventHandlerGregTech.scala new file mode 100644 index 000000000..382b2d50e --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/gregtech/EventHandlerGregTech.scala @@ -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 + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/gregtech/ModGregtech.scala b/src/main/scala/li/cil/oc/integration/gregtech/ModGregtech.scala index 5862190da..438e1d701 100644 --- a/src/main/scala/li/cil/oc/integration/gregtech/ModGregtech.scala +++ b/src/main/scala/li/cil/oc/integration/gregtech/ModGregtech.scala @@ -1,13 +1,18 @@ package li.cil.oc.integration.gregtech +import cpw.mods.fml.common.event.FMLInterModComms import li.cil.oc.api.Driver -import li.cil.oc.integration.ModProxy -import li.cil.oc.integration.Mods +import li.cil.oc.integration.{ModProxy, Mods} +import net.minecraftforge.common.MinecraftForge object ModGregtech extends ModProxy { override def getMod = Mods.GregTech override def initialize() { + FMLInterModComms.sendMessage("OpenComputers", "registerToolDurabilityProvider", "li.cil.oc.integration.gregtech.EventHandlerGregTech.getDurability") + + MinecraftForge.EVENT_BUS.register(EventHandlerGregTech) + Driver.add(new DriverEnergyContainer) } } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/ic2/EventHandlerIndustrialCraft2.scala b/src/main/scala/li/cil/oc/integration/ic2/EventHandlerIndustrialCraft2.scala new file mode 100644 index 000000000..db23a115f --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/ic2/EventHandlerIndustrialCraft2.scala @@ -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 + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/ic2/ModIndustrialCraft2.scala b/src/main/scala/li/cil/oc/integration/ic2/ModIndustrialCraft2.scala index 7f74f3800..0a169667f 100644 --- a/src/main/scala/li/cil/oc/integration/ic2/ModIndustrialCraft2.scala +++ b/src/main/scala/li/cil/oc/integration/ic2/ModIndustrialCraft2.scala @@ -1,13 +1,18 @@ package li.cil.oc.integration.ic2 +import cpw.mods.fml.common.event.FMLInterModComms import li.cil.oc.api.Driver -import li.cil.oc.integration.ModProxy -import li.cil.oc.integration.Mods +import li.cil.oc.integration.{ModProxy, Mods} +import net.minecraftforge.common.MinecraftForge object ModIndustrialCraft2 extends ModProxy { override def getMod = Mods.IndustrialCraft2 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 DriverEnergySink) Driver.add(new DriverEnergySource) @@ -15,6 +20,7 @@ object ModIndustrialCraft2 extends ModProxy { Driver.add(new DriverMassFab) Driver.add(new DriverReactor) Driver.add(new DriverReactorChamber) + Driver.add(new ConverterElectricItem) } -} \ No newline at end of file +} diff --git a/src/main/scala/li/cil/oc/integration/tcon/EventHandlerTinkersConstruct.scala b/src/main/scala/li/cil/oc/integration/tcon/EventHandlerTinkersConstruct.scala index 41787e5ec..466084e5e 100644 --- a/src/main/scala/li/cil/oc/integration/tcon/EventHandlerTinkersConstruct.scala +++ b/src/main/scala/li/cil/oc/integration/tcon/EventHandlerTinkersConstruct.scala @@ -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 + } } diff --git a/src/main/scala/li/cil/oc/integration/tcon/ModTinkersConstruct.scala b/src/main/scala/li/cil/oc/integration/tcon/ModTinkersConstruct.scala index 5fd56dc0e..a13c2f8af 100644 --- a/src/main/scala/li/cil/oc/integration/tcon/ModTinkersConstruct.scala +++ b/src/main/scala/li/cil/oc/integration/tcon/ModTinkersConstruct.scala @@ -1,5 +1,6 @@ package li.cil.oc.integration.tcon +import cpw.mods.fml.common.event.FMLInterModComms import li.cil.oc.integration.ModProxy import li.cil.oc.integration.Mods import net.minecraftforge.common.MinecraftForge @@ -8,6 +9,8 @@ object ModTinkersConstruct extends ModProxy { override def getMod = Mods.TinkersConstruct override def initialize() { + FMLInterModComms.sendMessage("OpenComputers", "registerToolDurabilityProvider", "li.cil.oc.integration.tcon.EventHandlerTinkersConstruct.getDurability") + MinecraftForge.EVENT_BUS.register(EventHandlerTinkersConstruct) } } diff --git a/src/main/scala/li/cil/oc/server/component/robot/Robot.scala b/src/main/scala/li/cil/oc/server/component/robot/Robot.scala index b8a421844..61a54601b 100644 --- a/src/main/scala/li/cil/oc/server/component/robot/Robot.scala +++ b/src/main/scala/li/cil/oc/server/component/robot/Robot.scala @@ -1,30 +1,22 @@ package li.cil.oc.server.component.robot -import li.cil.oc.OpenComputers -import li.cil.oc.Settings -import li.cil.oc.api +import li.cil.oc.{OpenComputers, Settings, api} import li.cil.oc.api.event.RobotPlaceInAirEvent -import li.cil.oc.api.machine.Arguments -import li.cil.oc.api.machine.Callback -import li.cil.oc.api.machine.Context +import li.cil.oc.api.machine.{Arguments, Callback, Context} import li.cil.oc.api.network._ 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.util.ExtendedArguments._ import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.InventoryUtils import li.cil.oc.util.ResultWrapper.result -import net.minecraft.entity.Entity -import net.minecraft.entity.EntityLivingBase -import net.minecraft.entity.item.EntityItem -import net.minecraft.entity.item.EntityMinecart -import net.minecraft.item.ItemBlock -import net.minecraft.item.ItemStack +import net.minecraft.entity.{Entity, EntityLivingBase} +import net.minecraft.entity.item.{EntityItem, EntityMinecart} +import net.minecraft.item.{ItemBlock, ItemStack} import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.MovingObjectPosition 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.util.ForgeDirection 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] = { Option(robot.getStackInSlot(0)) match { case Some(item) => - if (item.isItemStackDamageable) { - result(item.getMaxDamage - item.getItemDamage, item.getMaxDamage - item.getItemDamageForDisplay, item.getMaxDamage) + ToolDurabilityProviders.getDurability(item) match { + 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") } }