diff --git a/src/main/java/li/cil/oc/api/API.java b/src/main/java/li/cil/oc/api/API.java index d25a42825..733b13bb7 100644 --- a/src/main/java/li/cil/oc/api/API.java +++ b/src/main/java/li/cil/oc/api/API.java @@ -12,7 +12,7 @@ import li.cil.oc.api.detail.*; */ public class API { public static final String ID_OWNER = "OpenComputers|Core"; - public static final String VERSION = "5.5.5"; + public static final String VERSION = "5.6.0"; public static DriverAPI driver = null; public static FileSystemAPI fileSystem = null; diff --git a/src/main/java/li/cil/oc/api/nanomachines/Behavior.java b/src/main/java/li/cil/oc/api/nanomachines/Behavior.java index 38299b538..b532e253f 100644 --- a/src/main/java/li/cil/oc/api/nanomachines/Behavior.java +++ b/src/main/java/li/cil/oc/api/nanomachines/Behavior.java @@ -40,8 +40,10 @@ public interface Behavior { * Called when this behavior becomes inactive. *
* Use this to remove permanent effects. + * + * @param reason the reason the behavior is being disabled. */ - void onDisable(); + void onDisable(DisableReason reason); /** * Called each tick while this behavior is active. diff --git a/src/main/java/li/cil/oc/api/nanomachines/DisableReason.java b/src/main/java/li/cil/oc/api/nanomachines/DisableReason.java new file mode 100644 index 000000000..13d806e36 --- /dev/null +++ b/src/main/java/li/cil/oc/api/nanomachines/DisableReason.java @@ -0,0 +1,23 @@ +package li.cil.oc.api.nanomachines; + +/** + * Enum with reasons why a nanomachine behavior was disabled. + * + * This allows some more context specific behavior in a more stable fashion. + */ +public enum DisableReason { + /** + * This covers things like players logging off or the controller being reset. + */ + Default, + + /** + * Input state changed, leading to a behavior being disabled. + */ + InputChanged, + + /** + * System has run out of energy and is powering down. + */ + OutOfEnergy +} diff --git a/src/main/java/li/cil/oc/api/prefab/AbstractBehavior.java b/src/main/java/li/cil/oc/api/prefab/AbstractBehavior.java new file mode 100644 index 000000000..ea1ad4bb9 --- /dev/null +++ b/src/main/java/li/cil/oc/api/prefab/AbstractBehavior.java @@ -0,0 +1,52 @@ +package li.cil.oc.api.prefab; + +import li.cil.oc.api.nanomachines.Behavior; +import li.cil.oc.api.nanomachines.DisableReason; +import net.minecraft.entity.player.EntityPlayer; + +/** + * Base class for behaviors, mostly useful to have less cluttered classes when + * you only need one or two of the methods in the interface. + * + * This implementation will also store the player the behavior was created for. + */ +public abstract class AbstractBehavior implements Behavior { + /** + * The player this behavior was created for. + */ + public final EntityPlayer player; + + /** + * Pass along the player the behavior was created for here to have it stored + * for later use. + * + * @param player the player the behavior was created for. + */ + protected AbstractBehavior(EntityPlayer player) { + this.player = player; + } + + /** + * Use this if you do not need the player reference in your implementation. + */ + protected AbstractBehavior() { + this(null); + } + + @Override + public String getNameHint() { + return null; + } + + @Override + public void onEnable() { + } + + @Override + public void onDisable(DisableReason reason) { + } + + @Override + public void update() { + } +} diff --git a/src/main/java/li/cil/oc/api/prefab/AbstractProvider.java b/src/main/java/li/cil/oc/api/prefab/AbstractProvider.java new file mode 100644 index 000000000..9f0393835 --- /dev/null +++ b/src/main/java/li/cil/oc/api/prefab/AbstractProvider.java @@ -0,0 +1,74 @@ +package li.cil.oc.api.prefab; + +import li.cil.oc.api.nanomachines.Behavior; +import li.cil.oc.api.nanomachines.BehaviorProvider; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; + +/** + * Example base implementation of nanomachine behavior provider. + * + * This class takes care of handling the unique identifier used to tell + * if a behavior is its own when loading from NBT. + */ +public abstract class AbstractProvider implements BehaviorProvider { + /** + * Unique identifier used to tell if a behavior is ours when asked to load it. + */ + protected final String id; + + /** + * For the ID passed in here, it is suggested to use a one-time generated + * UUID that is hard-coded into your provider implementation. Take care to + * use a different one for each provider you create! + * + * @param id the unique identifier for this provider. + */ + protected AbstractProvider(String id) { + if (id == null) throw new NullPointerException("id must not be null"); + this.id = id; + } + + /** + * Called when saving a behavior created using this behavior to NBT. + * + * The ID will already have been written, don't overwrite it. Store + * any additional data you need to restore the behavior here, if any. + * + * @param behavior the behavior to persist. + * @param nbt the NBT tag to persist it to. + */ + protected void writeBehaviorToNBT(Behavior behavior, NBTTagCompound nbt) { + } + + /** + * Called when loading a behavior from NBT. + * + * Use the data written in {@link #writeBehaviorToNBT} to restore the behavior + * to its previous state, then return it. + * + * @param player the player to restore the behavior for. + * @param nbt the NBT tag to load restore the behavior from. + * @return the restored behavior. + */ + protected abstract Behavior readBehaviorFromNBT(EntityPlayer player, NBTTagCompound nbt); + + // ----------------------------------------------------------------------- // + + @Override + public NBTTagCompound writeToNBT(Behavior behavior) { + NBTTagCompound nbt = new NBTTagCompound(); + nbt.setString("provider", id); + writeBehaviorToNBT(behavior, nbt); + return nbt; + } + + @Override + public Behavior readFromNBT(EntityPlayer player, NBTTagCompound nbt) { + if (id.equals(nbt.getString("provider"))) { + return readBehaviorFromNBT(player, nbt); + } else { + return null; + } + } +} diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 35d0cdef2..0c025b968 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -1023,16 +1023,40 @@ opencomputers { # Radius in blocks of the disintegration behavior for each active input. disintegrationRange: 1 - # Blacklisted potions, i.e. potions that won't be used for the potion + # Whitelisted potions, i.e. potions that will be used for the potion # behaviors nanomachines may trigger. This can contain strings or numbers. # In the case of strings, it has to be the internal name of the potion, - # in case of a number it has to be the potion ID. - potionBlacklist: [ - "potion.heal", - "potion.regeneration", - "potion.invisibility", - "potion.saturation" + # in case of a number it has to be the potion ID. Add any potion effects + # to make use of here, since they will all be disabled by default. + potionWhitelist: [ + "potion.moveSpeed", + "potion.digSpeed", + "potion.damageBoost", + "potion.jump", + "potion.resistance", + "potion.fireResistance", + "potion.waterBreathing", + "potion.nightVision", + "potion.absorption", + + "potion.blindness", + "potion.confusion", + "potion.digSlowDown", + "potion.harm", + "potion.hunger", + "potion.moveSlowdown", + "potion.poison", + "potion.weakness", + "potion.wither" ] + + # How much damage the hungry behavior should deal to the player when the + # nanomachine controller runs out of energy. + hungryDamage: 5 + + # How much energy the hungry behavior should restore when damaging the + # player. + hungryEnergyRestored: 50 } # 3D printer related stuff. diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index 5da4b99c1..687f85cfa 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -451,6 +451,15 @@ achievement.oc.transistor.desc=Create a Transistor to get started. Then listen t achievement.oc.wirelessNetworkCard=Signals achievement.oc.wirelessNetworkCard.desc=Time to go where no packet has gone before. +# Death messages. Note that the number of entries here must match the number +# set in the actual damage source in code. +death.attack.oc.nanomachinesOverload.1=%s got too greedy. +death.attack.oc.nanomachinesOverload.2=%s had a nervous breakdown. +death.attack.oc.nanomachinesOverload.3=%s's nanomachines went out of control. +death.attack.oc.nanomachinesHungry.1=%s was eaten by nanomachines. +death.attack.oc.nanomachinesHungry.2=%s didn't keep their nanomachines fed. +death.attack.oc.nanomachinesHungry.3=%s has been digested. + # NEI Integration nei.options.inventory.oredict=Show OreDictionary names nei.options.inventory.oredict.true=True diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index 0a9757a7e..c4fb20df0 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -361,6 +361,9 @@ class Settings(val config: Config) { val nanomachineMagnetRange = config.getDouble("nanomachines.magnetRange") max 0 val nanomachineDisintegrationRange = config.getInt("nanomachines.disintegrationRange") max 0 val nanomachinePotionBlacklist = config.getAnyRefList("nanomachines.potionBlacklist") + val nanomachinePotionWhitelist = config.getAnyRefList("nanomachines.potionWhitelist") + val nanomachinesHungryDamage = config.getDouble("nanomachines.hungryDamage").toFloat max 0 + val nanomachinesHungryEnergyRestored = config.getDouble("nanomachines.hungryEnergyRestored") max 0 // ----------------------------------------------------------------------- // // printer diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index d967a4b9f..a0abc1331 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -13,7 +13,7 @@ import li.cil.oc.client.renderer.entity.DroneRenderer import li.cil.oc.client.renderer.tileentity._ import li.cil.oc.common.component.TextBuffer import li.cil.oc.common.entity.Drone -import li.cil.oc.common.event.NanomachinesEventHandler +import li.cil.oc.common.event.NanomachinesHandler import li.cil.oc.common.init.Items import li.cil.oc.common.item.traits.Delegate import li.cil.oc.common.tileentity @@ -81,7 +81,7 @@ private[oc] class Proxy extends CommonProxy { ClientRegistry.registerKeyBinding(KeyBindings.clipboardPaste) MinecraftForge.EVENT_BUS.register(HighlightRenderer) - MinecraftForge.EVENT_BUS.register(NanomachinesEventHandler.Client) + MinecraftForge.EVENT_BUS.register(NanomachinesHandler.Client) MinecraftForge.EVENT_BUS.register(PetRenderer) MinecraftForge.EVENT_BUS.register(ServerRack) MinecraftForge.EVENT_BUS.register(Sound) diff --git a/src/main/scala/li/cil/oc/common/event/NanomachinesEventHandler.scala b/src/main/scala/li/cil/oc/common/event/NanomachinesHandler.scala similarity index 90% rename from src/main/scala/li/cil/oc/common/event/NanomachinesEventHandler.scala rename to src/main/scala/li/cil/oc/common/event/NanomachinesHandler.scala index d546af4e7..070ceaaed 100644 --- a/src/main/scala/li/cil/oc/common/event/NanomachinesEventHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/NanomachinesHandler.scala @@ -8,6 +8,7 @@ import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.nanomachines.Controller import li.cil.oc.client.Textures +import li.cil.oc.common.EventHandler import li.cil.oc.common.nanomachines.ControllerImpl import net.minecraft.client.Minecraft import net.minecraft.client.gui.ScaledResolution @@ -19,8 +20,9 @@ import net.minecraftforge.client.event.RenderGameOverlayEvent import net.minecraftforge.event.entity.living.LivingEvent import net.minecraftforge.event.entity.player.PlayerEvent import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedOutEvent -object NanomachinesEventHandler { +object NanomachinesHandler { object Client { @SubscribeEvent @@ -141,6 +143,16 @@ object NanomachinesEventHandler { } } } + + @SubscribeEvent + def onPlayerDisconnect(e: PlayerLoggedOutEvent): Unit = { + api.Nanomachines.getController(e.player) match { + case controller: ControllerImpl => + // Wait a tick because saving is done after this event. + EventHandler.schedule(() => api.Nanomachines.uninstallController(e.player)) + case _ => // Not a player with nanomachines. + } + } } } diff --git a/src/main/scala/li/cil/oc/common/item/Nanomachines.scala b/src/main/scala/li/cil/oc/common/item/Nanomachines.scala index 4318f239b..24c8e3417 100644 --- a/src/main/scala/li/cil/oc/common/item/Nanomachines.scala +++ b/src/main/scala/li/cil/oc/common/item/Nanomachines.scala @@ -3,10 +3,13 @@ package li.cil.oc.common.item import li.cil.oc.api import net.minecraft.entity.player.EntityPlayer import net.minecraft.item.EnumAction +import net.minecraft.item.EnumRarity import net.minecraft.item.ItemStack import net.minecraft.world.World class Nanomachines(val parent: Delegator) extends traits.Delegate { + override def rarity(stack: ItemStack): EnumRarity = EnumRarity.UNCOMMON + override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ItemStack = { if (!api.Nanomachines.hasController(player)) { player.setItemInUse(stack, getMaxItemUseDuration(stack)) diff --git a/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala b/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala index 884082de9..bcab3770e 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/ControllerImpl.scala @@ -9,8 +9,10 @@ import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.nanomachines.Behavior import li.cil.oc.api.nanomachines.Controller +import li.cil.oc.api.nanomachines.DisableReason import li.cil.oc.api.network.Packet import li.cil.oc.api.network.WirelessEndpoint +import li.cil.oc.integration.util.DamageSourceWithRandomCause import li.cil.oc.server.PacketSender import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedNBT._ @@ -33,6 +35,10 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE final val MaxSenderDistance = 2f final val FullSyncInterval = 20 * 60 + final val OverloadDamage = new DamageSourceWithRandomCause("oc.nanomachinesOverload", 3). + setDamageBypassesArmor(). + setDamageIsAbsolute() + var uuid = UUID.randomUUID.toString var responsePort = 0 var storedEnergy = Settings.get.bufferNanomachines * 0.25 @@ -52,7 +58,7 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE override def z: Int = BlockPosition(player).z override def receivePacket(packet: Packet, sender: WirelessEndpoint): Unit = { - if (getLocalBuffer > 0) { + if (getLocalBuffer > 0 && !player.isDead) { val (dx, dy, dz) = ((sender.x + 0.5) - player.posX, (sender.y + 0.5) - player.posY, (sender.z + 0.5) - player.posZ) val dSquared = dx * dx + dy * dy + dz * dz if (dSquared < MaxSenderDistance * MaxSenderDistance) packet.data.headOption match { @@ -155,7 +161,7 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE } override def getActiveBehaviors: lang.Iterable[Behavior] = configuration.synchronized { - cleanActiveBehaviors() + cleanActiveBehaviors(DisableReason.InputChanged) activeBehaviors } @@ -180,6 +186,10 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE // ----------------------------------------------------------------------- // def update(): Unit = { + if (player.isDead) { + return + } + if (isServer) { api.Network.updateWirelessNetwork(this) } @@ -189,12 +199,15 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE return } - val hasPower = getLocalBuffer > 0 || Settings.get.ignorePower + var hasPower = getLocalBuffer > 0 || Settings.get.ignorePower lazy val active = getActiveBehaviors.toIterable // Wrap once. lazy val activeInputs = configuration.triggers.count(_.isActive) if (hasPower != hadPower) { - if (!hasPower) active.foreach(_.onDisable()) + if (!hasPower) { + active.foreach(_.onDisable(DisableReason.OutOfEnergy)) // This may change our energy buffer. + hasPower = getLocalBuffer > 0 || Settings.get.ignorePower + } else active.foreach(_.onEnable()) } @@ -209,8 +222,7 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE val overload = activeInputs - getSafeInputCount if (!player.capabilities.isCreativeMode && overload > 0 && player.getEntityWorld.getTotalWorldTime % 20 == 0) { - player.setHealth(player.getHealth - overload) - player.performHurtAnimation() + player.attackEntityFrom(OverloadDamage, overload) } } @@ -246,6 +258,7 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE configuration.triggers(index).isActive = false activeBehaviorsDirty = true } + cleanActiveBehaviors(DisableReason.Default) } } @@ -253,7 +266,6 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE reset() if (isServer) { api.Network.leaveWirelessNetwork(this) - PacketSender.sendNanomachineConfiguration(player) } } @@ -280,7 +292,7 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE private def isServer = !isClient - private def cleanActiveBehaviors(): Unit = { + private def cleanActiveBehaviors(reason: DisableReason): Unit = { if (activeBehaviorsDirty) { configuration.synchronized(if (activeBehaviorsDirty) { val newBehaviors = configuration.behaviors.filter(_.isActive).map(_.behavior) @@ -290,7 +302,7 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE activeBehaviors ++= newBehaviors activeBehaviorsDirty = false addedBehaviors.foreach(_.onEnable()) - removedBehaviors.foreach(_.onDisable()) + removedBehaviors.foreach(_.onDisable(reason)) if (isServer) { PacketSender.sendNanomachineInputs(player) diff --git a/src/main/scala/li/cil/oc/common/nanomachines/Nanomachines.scala b/src/main/scala/li/cil/oc/common/nanomachines/Nanomachines.scala index afaf3433a..bfd68536c 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/Nanomachines.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/Nanomachines.scala @@ -4,6 +4,7 @@ import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.nanomachines.BehaviorProvider import li.cil.oc.api.nanomachines.Controller +import li.cil.oc.server.PacketSender import li.cil.oc.util.PlayerUtils import net.minecraft.entity.player.EntityPlayer @@ -41,9 +42,12 @@ object Nanomachines extends api.detail.NanomachinesAPI { override def uninstallController(player: EntityPlayer): Unit = { getController(player) match { case controller: ControllerImpl => - PlayerUtils.persistedData(player).removeTag(Settings.namespace + "hasNanomachines") - controllers(player) -= player controller.dispose() + controllers(player) -= player + PlayerUtils.persistedData(player).removeTag(Settings.namespace + "hasNanomachines") + if (!player.getEntityWorld.isRemote) { + PacketSender.sendNanomachineConfiguration(player) + } case _ => // Doesn't have one anyway. } } diff --git a/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala b/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala index 6d1503e1c..3731e418f 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/NeuralNetwork.scala @@ -55,8 +55,8 @@ class NeuralNetwork(controller: ControllerImpl) extends Persistable { val rng = new Random(controller.player.getEntityWorld.rand.nextInt()) def connect[Sink <: ConnectorNeuron, Source <: Neuron](sinks: Iterable[Sink], sources: mutable.ArrayBuffer[Source]): Unit = { - val sinkPool = sinks.toBuffer - rng.shuffle(sinkPool) + // Shuffle sink list to give each entry the same chance. + val sinkPool = rng.shuffle(sinks.toBuffer) for (sink <- sinkPool if sources.nonEmpty) { for (n <- 0 to rng.nextInt(Settings.get.nanomachineMaxInputs) if sources.nonEmpty) { val sourceIndex = rng.nextInt(sources.length) @@ -65,10 +65,6 @@ class NeuralNetwork(controller: ControllerImpl) extends Persistable { } } - // Shuffle behavior and connector list to give each entry the same chance. - rng.shuffle(connectors) - rng.shuffle(behaviors) - // Connect connectors to triggers, then behaviors to connectors and/or remaining triggers. val sourcePool = mutable.ArrayBuffer.fill(Settings.get.nanomachineMaxOutputs)(triggers.map(_.asInstanceOf[Neuron])).flatten connect(connectors, sourcePool) diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/DisintegrationProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/DisintegrationProvider.scala index 63623f069..d02111685 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/DisintegrationProvider.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/DisintegrationProvider.scala @@ -1,7 +1,10 @@ package li.cil.oc.common.nanomachines.provider +import li.cil.oc.Settings import li.cil.oc.Settings import li.cil.oc.api +import li.cil.oc.api.nanomachines.DisableReason +import li.cil.oc.api.prefab.AbstractBehavior import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedWorld._ import net.minecraft.block.Block @@ -17,20 +20,18 @@ import net.minecraftforge.fml.common.eventhandler.Event import scala.collection.mutable -object DisintegrationProvider extends SimpleProvider { - final val Id = "c4e7e3c2-8069-4fbb-b08e-74b1bddcdfe7" +object DisintegrationProvider extends ScalaProvider("c4e7e3c2-8069-4fbb-b08e-74b1bddcdfe7") { + override def createScalaBehaviors(player: EntityPlayer) = Iterable(new DisintegrationBehavior(player)) - override def doCreateBehaviors(player: EntityPlayer) = Iterable(new DisintegrationBehavior(player)) + override def readBehaviorFromNBT(player: EntityPlayer, nbt: NBTTagCompound) = new DisintegrationBehavior(player) - override def doReadFromNBT(player: EntityPlayer, nbt: NBTTagCompound) = new DisintegrationBehavior(player) - - class DisintegrationBehavior(player: EntityPlayer) extends SimpleBehavior(player) { + class DisintegrationBehavior(player: EntityPlayer) extends AbstractBehavior(player) { var breakingMap = mutable.Map.empty[BlockPosition, SlowBreakInfo] var breakingMapNew = mutable.Map.empty[BlockPosition, SlowBreakInfo] // Note: intentionally not overriding getNameHint. Gotta find this one manually! - override def onDisable(): Unit = { + override def onDisable(reason: DisableReason): Unit = { val world = player.getEntityWorld for (pos <- breakingMap.keys) { world.destroyBlockInWorldPartially(pos.hashCode(), pos, -1) diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/HungryProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/HungryProvider.scala new file mode 100644 index 000000000..bf7d52b01 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/HungryProvider.scala @@ -0,0 +1,32 @@ +package li.cil.oc.common.nanomachines.provider + +import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.api.nanomachines.Behavior +import li.cil.oc.api.nanomachines.DisableReason +import li.cil.oc.api.prefab.AbstractBehavior +import li.cil.oc.integration.util.DamageSourceWithRandomCause +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.nbt.NBTTagCompound + +object HungryProvider extends ScalaProvider("") { + final val FillCount = 10 // Create a bunch of these to have a higher chance of one being picked / available. + + final val HungryDamage = new DamageSourceWithRandomCause("oc.nanomachinesHungry", 3). + setDamageBypassesArmor(). + setDamageIsAbsolute() + + override def createScalaBehaviors(player: EntityPlayer): Iterable[Behavior] = Iterable.fill(FillCount)(new HungryBehavior(player)) + + override protected def readBehaviorFromNBT(player: EntityPlayer, nbt: NBTTagCompound): Behavior = new HungryBehavior(player) + + class HungryBehavior(player: EntityPlayer) extends AbstractBehavior(player) { + override def onDisable(reason: DisableReason): Unit = { + if (reason == DisableReason.OutOfEnergy) { + player.attackEntityFrom(HungryDamage, Settings.get.nanomachinesHungryDamage) + api.Nanomachines.getController(player).changeBuffer(Settings.get.nanomachinesHungryEnergyRestored) + } + } + } + +} diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/MagnetProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/MagnetProvider.scala index 3cf7fd4b2..1257e48bf 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/MagnetProvider.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/MagnetProvider.scala @@ -2,6 +2,7 @@ package li.cil.oc.common.nanomachines.provider import li.cil.oc.Settings import li.cil.oc.api +import li.cil.oc.api.prefab.AbstractBehavior import net.minecraft.entity.item.EntityItem import net.minecraft.entity.player.EntityPlayer import net.minecraft.nbt.NBTTagCompound @@ -9,15 +10,12 @@ import net.minecraft.util.Vec3 import scala.collection.convert.WrapAsScala._ -object MagnetProvider extends SimpleProvider { - // One-time generated UUID to identify our behaviors. - final val Id = "9324d5ec-71f1-41c2-b51c-406e527668fc" +object MagnetProvider extends ScalaProvider("9324d5ec-71f1-41c2-b51c-406e527668fc") { + override def createScalaBehaviors(player: EntityPlayer) = Iterable(new MagnetBehavior(player)) - override def doCreateBehaviors(player: EntityPlayer) = Iterable(new MagnetBehavior(player)) + override def readBehaviorFromNBT(player: EntityPlayer, nbt: NBTTagCompound) = new MagnetBehavior(player) - override def doReadFromNBT(player: EntityPlayer, nbt: NBTTagCompound) = new MagnetBehavior(player) - - class MagnetBehavior(player: EntityPlayer) extends SimpleBehavior(player) { + class MagnetBehavior(player: EntityPlayer) extends AbstractBehavior(player) { override def getNameHint = "magnet" override def update(): Unit = { diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/ParticleProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/ParticleProvider.scala index dddf8bb10..ca24571ec 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/ParticleProvider.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/ParticleProvider.scala @@ -2,14 +2,13 @@ package li.cil.oc.common.nanomachines.provider import li.cil.oc.api import li.cil.oc.api.nanomachines.Behavior +import li.cil.oc.api.prefab.AbstractBehavior import li.cil.oc.util.PlayerUtils import net.minecraft.entity.player.EntityPlayer import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.EnumParticleTypes -object ParticleProvider extends SimpleProvider { - final val Id = "b48c4bbd-51bb-4915-9367-16cff3220e4b" - +object ParticleProvider extends ScalaProvider("b48c4bbd-51bb-4915-9367-16cff3220e4b") { final val ParticleTypes = Array( EnumParticleTypes.FIREWORKS_SPARK, EnumParticleTypes.TOWN_AURA, @@ -26,9 +25,9 @@ object ParticleProvider extends SimpleProvider { EnumParticleTypes.VILLAGER_HAPPY ) - override def doCreateBehaviors(player: EntityPlayer): Iterable[Behavior] = ParticleTypes.map(new ParticleBehavior(_, player)) + override def createScalaBehaviors(player: EntityPlayer): Iterable[Behavior] = ParticleTypes.map(new ParticleBehavior(_, player)) - override def doWriteToNBT(behavior: Behavior, nbt: NBTTagCompound): Unit = { + override def writeBehaviorToNBT(behavior: Behavior, nbt: NBTTagCompound): Unit = { behavior match { case particles: ParticleBehavior => nbt.setInteger("effectName", particles.effectType.getParticleID) @@ -36,12 +35,12 @@ object ParticleProvider extends SimpleProvider { } } - override def doReadFromNBT(player: EntityPlayer, nbt: NBTTagCompound): Behavior = { + override def readBehaviorFromNBT(player: EntityPlayer, nbt: NBTTagCompound): Behavior = { val effectType = EnumParticleTypes.getParticleFromId(nbt.getInteger("effectName")) new ParticleBehavior(effectType, player) } - class ParticleBehavior(var effectType: EnumParticleTypes, player: EntityPlayer) extends SimpleBehavior(player) { + class ParticleBehavior(var effectType: EnumParticleTypes, player: EntityPlayer) extends AbstractBehavior(player) { override def getNameHint = "particles" override def update(): Unit = { diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/PotionProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/PotionProvider.scala index b26db07b3..2b1667988 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/PotionProvider.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/PotionProvider.scala @@ -3,6 +3,8 @@ package li.cil.oc.common.nanomachines.provider import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.nanomachines.Behavior +import li.cil.oc.api.nanomachines.DisableReason +import li.cil.oc.api.prefab.AbstractBehavior import net.minecraft.entity.player.EntityPlayer import net.minecraft.nbt.NBTTagCompound import net.minecraft.potion.Potion @@ -10,23 +12,27 @@ import net.minecraft.potion.PotionEffect import scala.collection.convert.WrapAsScala._ -object PotionProvider extends SimpleProvider { - final val Id = "c29e4eec-5a46-479a-9b3d-ad0f06da784a" - +object PotionProvider extends ScalaProvider("c29e4eec-5a46-479a-9b3d-ad0f06da784a") { // Lazy to give other mods a chance to register their potions. - lazy val PotionBlacklist = Settings.get.nanomachinePotionBlacklist.map { - case name: String => Potion.potionTypes.find(p => p != null && p.getName == name) - case id: java.lang.Number if id.intValue() >= 0 && id.intValue() < Potion.potionTypes.length => Option(Potion.potionTypes(id.intValue())) - case _ => None - }.collect { - case Some(potion) => potion - }.toSet + lazy val PotionWhitelist = filterPotions(Settings.get.nanomachinePotionWhitelist) - override def doCreateBehaviors(player: EntityPlayer) = { - Potion.potionTypes.filter(_ != null).filterNot(PotionBlacklist.contains).map(new PotionBehavior(_, player)) + def filterPotions[T](list: Iterable[T]) = { + list.map { + case name: String => Potion.potionTypes.find(p => p != null && p.getName == name) + case id: java.lang.Number if id.intValue() >= 0 && id.intValue() < Potion.potionTypes.length => Option(Potion.potionTypes(id.intValue())) + case _ => None + }.collect { + case Some(potion) => potion + }.toSet } - override def doWriteToNBT(behavior: Behavior, nbt: NBTTagCompound): Unit = { + def isPotionEligible(potion: Potion) = potion != null && PotionWhitelist.contains(potion) + + override def createScalaBehaviors(player: EntityPlayer) = { + Potion.potionTypes.filter(isPotionEligible).map(new PotionBehavior(_, player)) + } + + override def writeBehaviorToNBT(behavior: Behavior, nbt: NBTTagCompound): Unit = { behavior match { case potionBehavior: PotionBehavior => nbt.setInteger("potionId", potionBehavior.potion.id) @@ -34,21 +40,19 @@ object PotionProvider extends SimpleProvider { } } - override def doReadFromNBT(player: EntityPlayer, nbt: NBTTagCompound) = { + override def readBehaviorFromNBT(player: EntityPlayer, nbt: NBTTagCompound) = { val potionId = nbt.getInteger("potionId") new PotionBehavior(Potion.potionTypes(potionId), player) } - class PotionBehavior(val potion: Potion, player: EntityPlayer) extends SimpleBehavior(player) { + class PotionBehavior(val potion: Potion, player: EntityPlayer) extends AbstractBehavior(player) { final val Duration = 600 def amplifier(player: EntityPlayer) = api.Nanomachines.getController(player).getInputCount(this) - 1 override def getNameHint: String = potion.getName.stripPrefix("potion.") - override def onEnable(): Unit = {} - - override def onDisable(): Unit = { + override def onDisable(reason: DisableReason): Unit = { player.removePotionEffect(potion.id) } diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/ScalaProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/ScalaProvider.scala new file mode 100644 index 000000000..93285282a --- /dev/null +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/ScalaProvider.scala @@ -0,0 +1,13 @@ +package li.cil.oc.common.nanomachines.provider + +import li.cil.oc.api.nanomachines.Behavior +import li.cil.oc.api.prefab.AbstractProvider +import net.minecraft.entity.player.EntityPlayer + +import scala.collection.convert.WrapAsJava._ + +abstract class ScalaProvider(id: String) extends AbstractProvider(id) { + def createScalaBehaviors(player: EntityPlayer): Iterable[Behavior] + + override def createBehaviors(player: EntityPlayer): java.lang.Iterable[Behavior] = asJavaIterable(createScalaBehaviors(player)) +} diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/SimpleBehavior.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/SimpleBehavior.scala deleted file mode 100644 index 25bc0d9a7..000000000 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/SimpleBehavior.scala +++ /dev/null @@ -1,14 +0,0 @@ -package li.cil.oc.common.nanomachines.provider - -import li.cil.oc.api.nanomachines.Behavior -import net.minecraft.entity.player.EntityPlayer - -class SimpleBehavior(val player: EntityPlayer) extends Behavior { - override def getNameHint: String = null - - override def onEnable(): Unit = {} - - override def onDisable(): Unit = {} - - override def update(): Unit = {} -} diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/SimpleProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/SimpleProvider.scala deleted file mode 100644 index 40f8ce6b3..000000000 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/SimpleProvider.scala +++ /dev/null @@ -1,35 +0,0 @@ -package li.cil.oc.common.nanomachines.provider - -import li.cil.oc.api.nanomachines.Behavior -import li.cil.oc.api.nanomachines.BehaviorProvider -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.nbt.NBTTagCompound - -import scala.collection.convert.WrapAsJava._ - -abstract class SimpleProvider extends BehaviorProvider { - // One-time generated UUID to identify our behaviors. - def Id: String - - def doCreateBehaviors(player: EntityPlayer): Iterable[Behavior] - - def doWriteToNBT(behavior: Behavior, nbt: NBTTagCompound): Unit = {} - - def doReadFromNBT(player: EntityPlayer, nbt: NBTTagCompound): Behavior - - override def createBehaviors(player: EntityPlayer): java.lang.Iterable[Behavior] = asJavaIterable(doCreateBehaviors(player)) - - override def writeToNBT(behavior: Behavior): NBTTagCompound = { - val nbt = new NBTTagCompound() - nbt.setString("provider", Id) - doWriteToNBT(behavior: Behavior, nbt: NBTTagCompound) - nbt - } - - override def readFromNBT(player: EntityPlayer, nbt: NBTTagCompound): Behavior = { - if (nbt.getString("provider") == Id) { - doReadFromNBT(player, nbt) - } - else null - } -} diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala index 5e546142f..9c46309b0 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala @@ -28,6 +28,7 @@ import li.cil.oc.common.item.Delegator import li.cil.oc.common.item.RedstoneCard import li.cil.oc.common.item.Tablet import li.cil.oc.common.nanomachines.provider.DisintegrationProvider +import li.cil.oc.common.nanomachines.provider.HungryProvider import li.cil.oc.common.nanomachines.provider.MagnetProvider import li.cil.oc.common.nanomachines.provider.ParticleProvider import li.cil.oc.common.nanomachines.provider.PotionProvider @@ -72,6 +73,7 @@ object ModOpenComputers extends ModProxy { ForgeChunkManager.setForcedChunkLoadingCallback(OpenComputers, ChunkloaderUpgradeHandler) FMLCommonHandler.instance.bus.register(EventHandler) + FMLCommonHandler.instance.bus.register(NanomachinesHandler.Common) FMLCommonHandler.instance.bus.register(SimpleComponentTickHandler.Instance) FMLCommonHandler.instance.bus.register(Tablet) @@ -84,7 +86,7 @@ object ModOpenComputers extends ModProxy { MinecraftForge.EVENT_BUS.register(GeolyzerHandler) MinecraftForge.EVENT_BUS.register(HoverBootsHandler) MinecraftForge.EVENT_BUS.register(Loot) - MinecraftForge.EVENT_BUS.register(NanomachinesEventHandler.Common) + MinecraftForge.EVENT_BUS.register(NanomachinesHandler.Common) MinecraftForge.EVENT_BUS.register(RobotCommonHandler) MinecraftForge.EVENT_BUS.register(SaveHandler) MinecraftForge.EVENT_BUS.register(Tablet) @@ -253,6 +255,7 @@ object ModOpenComputers extends ModProxy { api.Manual.addTab(new ItemStackTabIconRenderer(api.Items.get("cpu1").createItemStack(1)), "oc:gui.Manual.Items", "%LANGUAGE%/item/index.md") api.Nanomachines.addProvider(DisintegrationProvider) + api.Nanomachines.addProvider(HungryProvider) api.Nanomachines.addProvider(ParticleProvider) api.Nanomachines.addProvider(PotionProvider) api.Nanomachines.addProvider(MagnetProvider) diff --git a/src/main/scala/li/cil/oc/integration/util/DamageSourceWithRandomCause.scala b/src/main/scala/li/cil/oc/integration/util/DamageSourceWithRandomCause.scala new file mode 100644 index 000000000..d21ec97f7 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/util/DamageSourceWithRandomCause.scala @@ -0,0 +1,19 @@ +package li.cil.oc.integration.util + +import net.minecraft.entity.EntityLivingBase +import net.minecraft.util.ChatComponentTranslation +import net.minecraft.util.DamageSource +import net.minecraft.util.IChatComponent +import net.minecraft.util.StatCollector + +class DamageSourceWithRandomCause(name: String, numCauses: Int) extends DamageSource(name) { + override def getDeathMessage(damagee: EntityLivingBase): IChatComponent = { + val damager = damagee.func_94060_bK + val format = "death.attack." + damageType + "." + (damagee.worldObj.rand.nextInt(numCauses) + 1) + val withCauseFormat = format + ".player" + if (damager != null && StatCollector.canTranslate(withCauseFormat)) + new ChatComponentTranslation(withCauseFormat, damagee.getDisplayName, damager.getDisplayName) + else + new ChatComponentTranslation(format, damagee.getDisplayName) + } +}