summaryrefslogtreecommitdiff
path: root/src/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/api')
-rw-r--r--src/api/java/cofh/api/block/IDismantleable.java27
-rw-r--r--src/api/java/cofh/api/energy/EnergyStorage.java158
-rw-r--r--src/api/java/cofh/api/energy/IEnergyConnection.java21
-rw-r--r--src/api/java/cofh/api/energy/IEnergyContainerItem.java10
-rw-r--r--src/api/java/cofh/api/energy/IEnergyHandler.java57
-rw-r--r--src/api/java/cofh/api/energy/IEnergyProvider.java38
-rw-r--r--src/api/java/cofh/api/energy/IEnergyReceiver.java38
-rw-r--r--src/api/java/cofh/api/energy/IEnergyStorage.java46
-rw-r--r--src/api/java/cofh/api/energy/ItemEnergyContainer.java110
-rw-r--r--src/api/java/cofh/api/energy/TileEnergyHandler.java65
-rw-r--r--src/api/java/cofh/api/item/IToolHammer.java44
-rw-r--r--src/api/java/cofh/lib/util/helpers/BlockHelper.java567
-rw-r--r--src/api/java/ic2/api/Direction.java135
-rw-r--r--src/api/java/ic2/api/crops/BaseSeed.java82
-rw-r--r--src/api/java/ic2/api/crops/CropCard.java435
-rw-r--r--src/api/java/ic2/api/crops/Crops.java151
-rw-r--r--src/api/java/ic2/api/crops/ICropTile.java289
-rw-r--r--src/api/java/ic2/api/crops/package-info.java4
-rw-r--r--src/api/java/ic2/api/energy/EnergyNet.java18
-rw-r--r--src/api/java/ic2/api/energy/IEnergyNet.java86
-rw-r--r--src/api/java/ic2/api/energy/NodeStats.java25
-rw-r--r--src/api/java/ic2/api/energy/event/EnergyTileEvent.java25
-rw-r--r--src/api/java/ic2/api/energy/event/EnergyTileLoadEvent.java26
-rw-r--r--src/api/java/ic2/api/energy/event/EnergyTileUnloadEvent.java27
-rw-r--r--src/api/java/ic2/api/energy/event/package-info.java4
-rw-r--r--src/api/java/ic2/api/energy/package-info.java4
-rw-r--r--src/api/java/ic2/api/energy/prefab/BasicSink.java373
-rw-r--r--src/api/java/ic2/api/energy/prefab/BasicSource.java375
-rw-r--r--src/api/java/ic2/api/energy/prefab/package-info.java4
-rw-r--r--src/api/java/ic2/api/energy/tile/IEnergyAcceptor.java27
-rw-r--r--src/api/java/ic2/api/energy/tile/IEnergyConductor.java53
-rw-r--r--src/api/java/ic2/api/energy/tile/IEnergyEmitter.java28
-rw-r--r--src/api/java/ic2/api/energy/tile/IEnergySink.java45
-rw-r--r--src/api/java/ic2/api/energy/tile/IEnergySource.java38
-rw-r--r--src/api/java/ic2/api/energy/tile/IEnergyTile.java15
-rw-r--r--src/api/java/ic2/api/energy/tile/IHeatSource.java23
-rw-r--r--src/api/java/ic2/api/energy/tile/IKineticSource.java22
-rw-r--r--src/api/java/ic2/api/energy/tile/IMetaDelegate.java36
-rw-r--r--src/api/java/ic2/api/energy/tile/package-info.java4
-rw-r--r--src/api/java/ic2/api/energy/usage.txt144
-rw-r--r--src/api/java/ic2/api/event/ExplosionEvent.java44
-rw-r--r--src/api/java/ic2/api/event/LaserEvent.java114
-rw-r--r--src/api/java/ic2/api/event/PaintEvent.java32
-rw-r--r--src/api/java/ic2/api/event/RetextureEvent.java37
-rw-r--r--src/api/java/ic2/api/event/package-info.java4
-rw-r--r--src/api/java/ic2/api/info/IEnergyValueProvider.java17
-rw-r--r--src/api/java/ic2/api/info/IFuelValueProvider.java15
-rw-r--r--src/api/java/ic2/api/info/Info.java39
-rw-r--r--src/api/java/ic2/api/info/package-info.java4
-rw-r--r--src/api/java/ic2/api/item/ElectricItem.java59
-rw-r--r--src/api/java/ic2/api/item/IBackupElectricItemManager.java12
-rw-r--r--src/api/java/ic2/api/item/IBlockCuttingBlade.java6
-rw-r--r--src/api/java/ic2/api/item/IBoxable.java14
-rw-r--r--src/api/java/ic2/api/item/IC2Items.java598
-rw-r--r--src/api/java/ic2/api/item/ICustomDamageItem.java56
-rw-r--r--src/api/java/ic2/api/item/IDebuggable.java22
-rw-r--r--src/api/java/ic2/api/item/IElectricItem.java57
-rw-r--r--src/api/java/ic2/api/item/IElectricItemManager.java95
-rw-r--r--src/api/java/ic2/api/item/IItemHudInfo.java24
-rw-r--r--src/api/java/ic2/api/item/IKineticRotor.java23
-rw-r--r--src/api/java/ic2/api/item/ILatheItem.java70
-rw-r--r--src/api/java/ic2/api/item/IMetalArmor.java20
-rw-r--r--src/api/java/ic2/api/item/ISpecialElectricItem.java13
-rw-r--r--src/api/java/ic2/api/item/ITerraformingBP.java34
-rw-r--r--src/api/java/ic2/api/item/ItemWrapper.java50
-rw-r--r--src/api/java/ic2/api/item/package-info.java4
-rw-r--r--src/api/java/ic2/api/network/INetworkClientTileEntityEventListener.java17
-rw-r--r--src/api/java/ic2/api/network/INetworkDataProvider.java18
-rw-r--r--src/api/java/ic2/api/network/INetworkItemEventListener.java19
-rw-r--r--src/api/java/ic2/api/network/INetworkTileEntityEventListener.java14
-rw-r--r--src/api/java/ic2/api/network/INetworkUpdateListener.java14
-rw-r--r--src/api/java/ic2/api/network/NetworkHelper.java194
-rw-r--r--src/api/java/ic2/api/network/package-info.java4
-rw-r--r--src/api/java/ic2/api/package-info.java4
-rw-r--r--src/api/java/ic2/api/reactor/IReactor.java158
-rw-r--r--src/api/java/ic2/api/reactor/IReactorChamber.java20
-rw-r--r--src/api/java/ic2/api/reactor/IReactorComponent.java98
-rw-r--r--src/api/java/ic2/api/reactor/package-info.java4
-rw-r--r--src/api/java/ic2/api/recipe/ICannerBottleRecipeManager.java51
-rw-r--r--src/api/java/ic2/api/recipe/ICannerEnrichRecipeManager.java54
-rw-r--r--src/api/java/ic2/api/recipe/ICraftingRecipeManager.java26
-rw-r--r--src/api/java/ic2/api/recipe/IFluidHeatManager.java32
-rw-r--r--src/api/java/ic2/api/recipe/ILiquidAcceptManager.java10
-rw-r--r--src/api/java/ic2/api/recipe/ILiquidHeatExchangerManager.java39
-rw-r--r--src/api/java/ic2/api/recipe/IListRecipeManager.java41
-rw-r--r--src/api/java/ic2/api/recipe/IMachineRecipeManager.java44
-rw-r--r--src/api/java/ic2/api/recipe/IMachineRecipeManagerExt.java22
-rw-r--r--src/api/java/ic2/api/recipe/IPatternStorage.java11
-rw-r--r--src/api/java/ic2/api/recipe/IRecipeInput.java31
-rw-r--r--src/api/java/ic2/api/recipe/IScrapboxManager.java13
-rw-r--r--src/api/java/ic2/api/recipe/ISemiFluidFuelManager.java32
-rw-r--r--src/api/java/ic2/api/recipe/RecipeInputFluidContainer.java54
-rw-r--r--src/api/java/ic2/api/recipe/RecipeInputItemStack.java47
-rw-r--r--src/api/java/ic2/api/recipe/RecipeInputOreDict.java104
-rw-r--r--src/api/java/ic2/api/recipe/RecipeOutput.java53
-rw-r--r--src/api/java/ic2/api/recipe/Recipes.java68
-rw-r--r--src/api/java/ic2/api/recipe/package-info.java4
-rw-r--r--src/api/java/ic2/api/tile/ExplosionWhitelist.java47
-rw-r--r--src/api/java/ic2/api/tile/IEnergyStorage.java61
-rw-r--r--src/api/java/ic2/api/tile/IWrenchable.java61
-rw-r--r--src/api/java/ic2/api/tile/package-info.java4
-rw-r--r--src/api/java/ic2/api/util/IKeyboard.java14
-rw-r--r--src/api/java/ic2/api/util/Keys.java6
-rw-r--r--src/api/java/ic2/api/util/package-info.java4
-rw-r--r--src/api/java/mekanism/api/Chunk3D.java118
-rw-r--r--src/api/java/mekanism/api/Coord4D.java421
-rw-r--r--src/api/java/mekanism/api/EnumColor.java112
-rw-r--r--src/api/java/mekanism/api/IClientTicker.java8
-rw-r--r--src/api/java/mekanism/api/IConfigurable.java27
-rw-r--r--src/api/java/mekanism/api/IFilterAccess.java31
-rw-r--r--src/api/java/mekanism/api/IHeatTransfer.java28
-rw-r--r--src/api/java/mekanism/api/IMekWrench.java8
-rw-r--r--src/api/java/mekanism/api/ISalinationSolar.java11
-rw-r--r--src/api/java/mekanism/api/ItemRetriever.java112
-rw-r--r--src/api/java/mekanism/api/MekanismAPI.java50
-rw-r--r--src/api/java/mekanism/api/MekanismConfig.java137
-rw-r--r--src/api/java/mekanism/api/Pos3D.java458
-rw-r--r--src/api/java/mekanism/api/Range4D.java114
-rw-r--r--src/api/java/mekanism/api/TabProxy.java42
-rw-r--r--src/api/java/mekanism/api/energy/EnergizedItemManager.java60
-rw-r--r--src/api/java/mekanism/api/energy/EnergyStack.java14
-rw-r--r--src/api/java/mekanism/api/energy/ICableOutputter.java18
-rw-r--r--src/api/java/mekanism/api/energy/IEnergizedItem.java59
-rw-r--r--src/api/java/mekanism/api/energy/IStrictEnergyAcceptor.java25
-rw-r--r--src/api/java/mekanism/api/energy/IStrictEnergyStorage.java27
-rw-r--r--src/api/java/mekanism/api/energy/package-info.java3
-rw-r--r--src/api/java/mekanism/api/gas/Gas.java221
-rw-r--r--src/api/java/mekanism/api/gas/GasNetwork.java358
-rw-r--r--src/api/java/mekanism/api/gas/GasRegistry.java113
-rw-r--r--src/api/java/mekanism/api/gas/GasStack.java132
-rw-r--r--src/api/java/mekanism/api/gas/GasTank.java252
-rw-r--r--src/api/java/mekanism/api/gas/GasTransmission.java181
-rw-r--r--src/api/java/mekanism/api/gas/IGasHandler.java47
-rw-r--r--src/api/java/mekanism/api/gas/IGasItem.java81
-rw-r--r--src/api/java/mekanism/api/gas/IGasTransmitter.java10
-rw-r--r--src/api/java/mekanism/api/gas/ITubeConnection.java18
-rw-r--r--src/api/java/mekanism/api/gas/OreGas.java38
-rw-r--r--src/api/java/mekanism/api/gas/package-info.java3
-rw-r--r--src/api/java/mekanism/api/infuse/InfuseObject.java21
-rw-r--r--src/api/java/mekanism/api/infuse/InfuseRegistry.java113
-rw-r--r--src/api/java/mekanism/api/infuse/InfuseType.java47
-rw-r--r--src/api/java/mekanism/api/infuse/package-info.java3
-rw-r--r--src/api/java/mekanism/api/lasers/ILaserReceptor.java10
-rw-r--r--src/api/java/mekanism/api/lasers/package-info.java3
-rw-r--r--src/api/java/mekanism/api/package-info.java3
-rw-r--r--src/api/java/mekanism/api/reactor/IFusionReactor.java66
-rw-r--r--src/api/java/mekanism/api/reactor/INeutronCapture.java6
-rw-r--r--src/api/java/mekanism/api/reactor/IReactorBlock.java12
-rw-r--r--src/api/java/mekanism/api/reactor/package-info.java3
-rw-r--r--src/api/java/mekanism/api/recipe/RecipeHelper.java319
-rw-r--r--src/api/java/mekanism/api/recipe/package-info.java3
-rw-r--r--src/api/java/mekanism/api/transmitters/DynamicNetwork.java456
-rw-r--r--src/api/java/mekanism/api/transmitters/IBlockableConnection.java10
-rw-r--r--src/api/java/mekanism/api/transmitters/IGridTransmitter.java64
-rw-r--r--src/api/java/mekanism/api/transmitters/INetworkDataHandler.java10
-rw-r--r--src/api/java/mekanism/api/transmitters/ITransmitter.java11
-rw-r--r--src/api/java/mekanism/api/transmitters/ITransmitterTile.java6
-rw-r--r--src/api/java/mekanism/api/transmitters/TransmissionType.java80
-rw-r--r--src/api/java/mekanism/api/transmitters/TransmitterNetworkRegistry.java289
-rw-r--r--src/api/java/mekanism/api/transmitters/package-info.java3
-rw-r--r--src/api/java/mekanism/api/util/BlockInfo.java38
-rw-r--r--src/api/java/mekanism/api/util/ItemInfo.java38
-rw-r--r--src/api/java/mekanism/api/util/ListUtils.java282
-rw-r--r--src/api/java/mekanism/api/util/StackUtils.java254
-rw-r--r--src/api/java/mekanism/api/util/UnitDisplayUtils.java295
-rw-r--r--src/api/java/mekanism/api/util/package-info.java3
166 files changed, 12419 insertions, 5 deletions
diff --git a/src/api/java/cofh/api/block/IDismantleable.java b/src/api/java/cofh/api/block/IDismantleable.java
new file mode 100644
index 0000000..12c09e3
--- /dev/null
+++ b/src/api/java/cofh/api/block/IDismantleable.java
@@ -0,0 +1,27 @@
+package cofh.api.block;
+
+import java.util.ArrayList;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.world.World;
+
+/**
+ * Implemented on Blocks which have some method of being instantly dismantled.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IDismantleable {
+
+ /**
+ * Dismantles the block. If returnDrops is true, the drop(s) should be placed into the player's inventory.
+ */
+ ArrayList<ItemStack> dismantleBlock(EntityPlayer player, World world, int x, int y, int z, boolean returnDrops);
+
+ /**
+ * Return true if the block can be dismantled. The criteria for this is entirely up to the block.
+ */
+ boolean canDismantle(EntityPlayer player, World world, int x, int y, int z);
+
+}
diff --git a/src/api/java/cofh/api/energy/EnergyStorage.java b/src/api/java/cofh/api/energy/EnergyStorage.java
new file mode 100644
index 0000000..25e0126
--- /dev/null
+++ b/src/api/java/cofh/api/energy/EnergyStorage.java
@@ -0,0 +1,158 @@
+package cofh.api.energy;
+
+import net.minecraft.nbt.NBTTagCompound;
+
+/**
+ * Reference implementation of {@link IEnergyStorage}. Use/extend this or implement your own.
+ *
+ * @author King Lemming
+ *
+ */
+public class EnergyStorage implements IEnergyStorage {
+
+ protected int energy;
+ protected int capacity;
+ protected int maxReceive;
+ protected int maxExtract;
+
+ public EnergyStorage(int capacity) {
+
+ this(capacity, capacity, capacity);
+ }
+
+ public EnergyStorage(int capacity, int maxTransfer) {
+
+ this(capacity, maxTransfer, maxTransfer);
+ }
+
+ public EnergyStorage(int capacity, int maxReceive, int maxExtract) {
+
+ this.capacity = capacity;
+ this.maxReceive = maxReceive;
+ this.maxExtract = maxExtract;
+ }
+
+ public EnergyStorage readFromNBT(NBTTagCompound nbt) {
+
+ this.energy = nbt.getInteger("Energy");
+
+ if (energy > capacity) {
+ energy = capacity;
+ }
+ return this;
+ }
+
+ public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
+
+ if (energy < 0) {
+ energy = 0;
+ }
+ nbt.setInteger("Energy", energy);
+ return nbt;
+ }
+
+ public void setCapacity(int capacity) {
+
+ this.capacity = capacity;
+
+ if (energy > capacity) {
+ energy = capacity;
+ }
+ }
+
+ public void setMaxTransfer(int maxTransfer) {
+
+ setMaxReceive(maxTransfer);
+ setMaxExtract(maxTransfer);
+ }
+
+ public void setMaxReceive(int maxReceive) {
+
+ this.maxReceive = maxReceive;
+ }
+
+ public void setMaxExtract(int maxExtract) {
+
+ this.maxExtract = maxExtract;
+ }
+
+ public int getMaxReceive() {
+
+ return maxReceive;
+ }
+
+ public int getMaxExtract() {
+
+ return maxExtract;
+ }
+
+ /**
+ * This function is included to allow for server -&gt; client sync. Do not call this externally to the containing Tile Entity, as not all IEnergyHandlers
+ * are guaranteed to have it.
+ *
+ * @param energy
+ */
+ public void setEnergyStored(int energy) {
+
+ this.energy = energy;
+
+ if (this.energy > capacity) {
+ this.energy = capacity;
+ } else if (this.energy < 0) {
+ this.energy = 0;
+ }
+ }
+
+ /**
+ * This function is included to allow the containing tile to directly and efficiently modify the energy contained in the EnergyStorage. Do not rely on this
+ * externally, as not all IEnergyHandlers are guaranteed to have it.
+ *
+ * @param energy
+ */
+ public void modifyEnergyStored(int energy) {
+
+ this.energy += energy;
+
+ if (this.energy > capacity) {
+ this.energy = capacity;
+ } else if (this.energy < 0) {
+ this.energy = 0;
+ }
+ }
+
+ /* IEnergyStorage */
+ @Override
+ public int receiveEnergy(int maxReceive, boolean simulate) {
+
+ int energyReceived = Math.min(capacity - energy, Math.min(this.maxReceive, maxReceive));
+
+ if (!simulate) {
+ energy += energyReceived;
+ }
+ return energyReceived;
+ }
+
+ @Override
+ public int extractEnergy(int maxExtract, boolean simulate) {
+
+ int energyExtracted = Math.min(energy, Math.min(this.maxExtract, maxExtract));
+
+ if (!simulate) {
+ energy -= energyExtracted;
+ }
+ return energyExtracted;
+ }
+
+ @Override
+ public int getEnergyStored() {
+
+ return energy;
+ }
+
+ @Override
+ public int getMaxEnergyStored() {
+
+ return capacity;
+ }
+
+}
diff --git a/src/api/java/cofh/api/energy/IEnergyConnection.java b/src/api/java/cofh/api/energy/IEnergyConnection.java
new file mode 100644
index 0000000..301271e
--- /dev/null
+++ b/src/api/java/cofh/api/energy/IEnergyConnection.java
@@ -0,0 +1,21 @@
+package cofh.api.energy;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on TileEntities which should connect to energy transportation blocks. This is intended for blocks which generate energy but do not
+ * accept it; otherwise just use IEnergyHandler.
+ * <p>
+ * Note that {@link IEnergyHandler} is an extension of this.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnergyConnection {
+
+ /**
+ * Returns TRUE if the TileEntity can connect on a given side.
+ */
+ boolean canConnectEnergy(ForgeDirection from);
+
+}
diff --git a/src/api/java/cofh/api/energy/IEnergyContainerItem.java b/src/api/java/cofh/api/energy/IEnergyContainerItem.java
index 0bcfda6..3ef7257 100644
--- a/src/api/java/cofh/api/energy/IEnergyContainerItem.java
+++ b/src/api/java/cofh/api/energy/IEnergyContainerItem.java
@@ -4,17 +4,17 @@ import net.minecraft.item.ItemStack;
/**
* Implement this interface on Item classes that support external manipulation of their internal energy storages.
- *
+ * <p>
* A reference implementation is provided {@link ItemEnergyContainer}.
- *
+ *
* @author King Lemming
- *
+ *
*/
public interface IEnergyContainerItem {
/**
* Adds energy to a container item. Returns the quantity of energy that was accepted. This should always return 0 if the item cannot be externally charged.
- *
+ *
* @param container
* ItemStack to be charged.
* @param maxReceive
@@ -28,7 +28,7 @@ public interface IEnergyContainerItem {
/**
* Removes energy from a container item. Returns the quantity of energy that was removed. This should always return 0 if the item cannot be externally
* discharged.
- *
+ *
* @param container
* ItemStack to be discharged.
* @param maxExtract
diff --git a/src/api/java/cofh/api/energy/IEnergyHandler.java b/src/api/java/cofh/api/energy/IEnergyHandler.java
new file mode 100644
index 0000000..6a4600a
--- /dev/null
+++ b/src/api/java/cofh/api/energy/IEnergyHandler.java
@@ -0,0 +1,57 @@
+package cofh.api.energy;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on Tile Entities which should handle energy, generally storing it in one or more internal {@link IEnergyStorage} objects.
+ * <p>
+ * A reference implementation is provided {@link TileEnergyHandler}.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnergyHandler extends IEnergyProvider, IEnergyReceiver {
+
+ // merely a convenience interface (remove these methods in 1.8; provided here for back-compat via compiler doing things)
+
+ /**
+ * Add energy to an IEnergyReceiver, internal distribution is left entirely to the IEnergyReceiver.
+ *
+ * @param from
+ * Orientation the energy is received from.
+ * @param maxReceive
+ * Maximum amount of energy to receive.
+ * @param simulate
+ * If TRUE, the charge will only be simulated.
+ * @return Amount of energy that was (or would have been, if simulated) received.
+ */
+ @Override
+ int receiveEnergy(ForgeDirection from, int maxReceive, boolean simulate);
+
+ /**
+ * Remove energy from an IEnergyProvider, internal distribution is left entirely to the IEnergyProvider.
+ *
+ * @param from
+ * Orientation the energy is extracted from.
+ * @param maxExtract
+ * Maximum amount of energy to extract.
+ * @param simulate
+ * If TRUE, the extraction will only be simulated.
+ * @return Amount of energy that was (or would have been, if simulated) extracted.
+ */
+ @Override
+ int extractEnergy(ForgeDirection from, int maxExtract, boolean simulate);
+
+ /**
+ * Returns the amount of energy currently stored.
+ */
+ @Override
+ int getEnergyStored(ForgeDirection from);
+
+ /**
+ * Returns the maximum amount of energy that can be stored.
+ */
+ @Override
+ int getMaxEnergyStored(ForgeDirection from);
+
+}
diff --git a/src/api/java/cofh/api/energy/IEnergyProvider.java b/src/api/java/cofh/api/energy/IEnergyProvider.java
new file mode 100644
index 0000000..05287b3
--- /dev/null
+++ b/src/api/java/cofh/api/energy/IEnergyProvider.java
@@ -0,0 +1,38 @@
+package cofh.api.energy;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on Tile Entities which should provide energy, generally storing it in one or more internal {@link IEnergyStorage} objects.
+ * <p>
+ * A reference implementation is provided {@link TileEnergyHandler}.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnergyProvider extends IEnergyConnection {
+
+ /**
+ * Remove energy from an IEnergyProvider, internal distribution is left entirely to the IEnergyProvider.
+ *
+ * @param from
+ * Orientation the energy is extracted from.
+ * @param maxExtract
+ * Maximum amount of energy to extract.
+ * @param simulate
+ * If TRUE, the extraction will only be simulated.
+ * @return Amount of energy that was (or would have been, if simulated) extracted.
+ */
+ int extractEnergy(ForgeDirection from, int maxExtract, boolean simulate);
+
+ /**
+ * Returns the amount of energy currently stored.
+ */
+ int getEnergyStored(ForgeDirection from);
+
+ /**
+ * Returns the maximum amount of energy that can be stored.
+ */
+ int getMaxEnergyStored(ForgeDirection from);
+
+}
diff --git a/src/api/java/cofh/api/energy/IEnergyReceiver.java b/src/api/java/cofh/api/energy/IEnergyReceiver.java
new file mode 100644
index 0000000..c726e09
--- /dev/null
+++ b/src/api/java/cofh/api/energy/IEnergyReceiver.java
@@ -0,0 +1,38 @@
+package cofh.api.energy;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on Tile Entities which should receive energy, generally storing it in one or more internal {@link IEnergyStorage} objects.
+ * <p>
+ * A reference implementation is provided {@link TileEnergyHandler}.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnergyReceiver extends IEnergyConnection {
+
+ /**
+ * Add energy to an IEnergyReceiver, internal distribution is left entirely to the IEnergyReceiver.
+ *
+ * @param from
+ * Orientation the energy is received from.
+ * @param maxReceive
+ * Maximum amount of energy to receive.
+ * @param simulate
+ * If TRUE, the charge will only be simulated.
+ * @return Amount of energy that was (or would have been, if simulated) received.
+ */
+ int receiveEnergy(ForgeDirection from, int maxReceive, boolean simulate);
+
+ /**
+ * Returns the amount of energy currently stored.
+ */
+ int getEnergyStored(ForgeDirection from);
+
+ /**
+ * Returns the maximum amount of energy that can be stored.
+ */
+ int getMaxEnergyStored(ForgeDirection from);
+
+}
diff --git a/src/api/java/cofh/api/energy/IEnergyStorage.java b/src/api/java/cofh/api/energy/IEnergyStorage.java
new file mode 100644
index 0000000..414b265
--- /dev/null
+++ b/src/api/java/cofh/api/energy/IEnergyStorage.java
@@ -0,0 +1,46 @@
+package cofh.api.energy;
+
+/**
+ * An energy storage is the unit of interaction with Energy inventories.<br>
+ * This is not to be implemented on TileEntities. This is for internal use only.
+ * <p>
+ * A reference implementation can be found at {@link EnergyStorage}.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnergyStorage {
+
+ /**
+ * Adds energy to the storage. Returns quantity of energy that was accepted.
+ *
+ * @param maxReceive
+ * Maximum amount of energy to be inserted.
+ * @param simulate
+ * If TRUE, the insertion will only be simulated.
+ * @return Amount of energy that was (or would have been, if simulated) accepted by the storage.
+ */
+ int receiveEnergy(int maxReceive, boolean simulate);
+
+ /**
+ * Removes energy from the storage. Returns quantity of energy that was removed.
+ *
+ * @param maxExtract
+ * Maximum amount of energy to be extracted.
+ * @param simulate
+ * If TRUE, the extraction will only be simulated.
+ * @return Amount of energy that was (or would have been, if simulated) extracted from the storage.
+ */
+ int extractEnergy(int maxExtract, boolean simulate);
+
+ /**
+ * Returns the amount of energy currently stored.
+ */
+ int getEnergyStored();
+
+ /**
+ * Returns the maximum amount of energy that can be stored.
+ */
+ int getMaxEnergyStored();
+
+}
diff --git a/src/api/java/cofh/api/energy/ItemEnergyContainer.java b/src/api/java/cofh/api/energy/ItemEnergyContainer.java
new file mode 100644
index 0000000..2d3659c
--- /dev/null
+++ b/src/api/java/cofh/api/energy/ItemEnergyContainer.java
@@ -0,0 +1,110 @@
+package cofh.api.energy;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+/**
+ * Reference implementation of {@link IEnergyContainerItem}. Use/extend this or implement your own.
+ *
+ * @author King Lemming
+ *
+ */
+public class ItemEnergyContainer extends Item implements IEnergyContainerItem {
+
+ protected int capacity;
+ protected int maxReceive;
+ protected int maxExtract;
+
+ public ItemEnergyContainer() {
+
+ }
+
+ public ItemEnergyContainer(int capacity) {
+
+ this(capacity, capacity, capacity);
+ }
+
+ public ItemEnergyContainer(int capacity, int maxTransfer) {
+
+ this(capacity, maxTransfer, maxTransfer);
+ }
+
+ public ItemEnergyContainer(int capacity, int maxReceive, int maxExtract) {
+
+ this.capacity = capacity;
+ this.maxReceive = maxReceive;
+ this.maxExtract = maxExtract;
+ }
+
+ public ItemEnergyContainer setCapacity(int capacity) {
+
+ this.capacity = capacity;
+ return this;
+ }
+
+ public void setMaxTransfer(int maxTransfer) {
+
+ setMaxReceive(maxTransfer);
+ setMaxExtract(maxTransfer);
+ }
+
+ public void setMaxReceive(int maxReceive) {
+
+ this.maxReceive = maxReceive;
+ }
+
+ public void setMaxExtract(int maxExtract) {
+
+ this.maxExtract = maxExtract;
+ }
+
+ /* IEnergyContainerItem */
+ @Override
+ public int receiveEnergy(ItemStack container, int maxReceive, boolean simulate) {
+
+ if (container.stackTagCompound == null) {
+ container.stackTagCompound = new NBTTagCompound();
+ }
+ int energy = container.stackTagCompound.getInteger("Energy");
+ int energyReceived = Math.min(capacity - energy, Math.min(this.maxReceive, maxReceive));
+
+ if (!simulate) {
+ energy += energyReceived;
+ container.stackTagCompound.setInteger("Energy", energy);
+ }
+ return energyReceived;
+ }
+
+ @Override
+ public int extractEnergy(ItemStack container, int maxExtract, boolean simulate) {
+
+ if (container.stackTagCompound == null || !container.stackTagCompound.hasKey("Energy")) {
+ return 0;
+ }
+ int energy = container.stackTagCompound.getInteger("Energy");
+ int energyExtracted = Math.min(energy, Math.min(this.maxExtract, maxExtract));
+
+ if (!simulate) {
+ energy -= energyExtracted;
+ container.stackTagCompound.setInteger("Energy", energy);
+ }
+ return energyExtracted;
+ }
+
+ @Override
+ public int getEnergyStored(ItemStack container) {
+
+ if (container.stackTagCompound == null || !container.stackTagCompound.hasKey("Energy")) {
+ return 0;
+ }
+ return container.stackTagCompound.getInteger("Energy");
+ }
+
+ @Override
+ public int getMaxEnergyStored(ItemStack container) {
+
+ return capacity;
+ }
+
+}
diff --git a/src/api/java/cofh/api/energy/TileEnergyHandler.java b/src/api/java/cofh/api/energy/TileEnergyHandler.java
new file mode 100644
index 0000000..7cc655e
--- /dev/null
+++ b/src/api/java/cofh/api/energy/TileEnergyHandler.java
@@ -0,0 +1,65 @@
+package cofh.api.energy;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Reference implementation of {@link IEnergyHandler}. Use/extend this or implement your own.
+ *
+ * @author King Lemming
+ *
+ */
+public class TileEnergyHandler extends TileEntity implements IEnergyHandler {
+
+ protected EnergyStorage storage = new EnergyStorage(32000);
+
+ @Override
+ public void readFromNBT(NBTTagCompound nbt) {
+
+ super.readFromNBT(nbt);
+ storage.readFromNBT(nbt);
+ }
+
+ @Override
+ public void writeToNBT(NBTTagCompound nbt) {
+
+ super.writeToNBT(nbt);
+ storage.writeToNBT(nbt);
+ }
+
+ /* IEnergyConnection */
+ @Override
+ public boolean canConnectEnergy(ForgeDirection from) {
+
+ return true;
+ }
+
+ /* IEnergyReceiver */
+ @Override
+ public int receiveEnergy(ForgeDirection from, int maxReceive, boolean simulate) {
+
+ return storage.receiveEnergy(maxReceive, simulate);
+ }
+
+ /* IEnergyProvider */
+ @Override
+ public int extractEnergy(ForgeDirection from, int maxExtract, boolean simulate) {
+
+ return storage.extractEnergy(maxExtract, simulate);
+ }
+
+ /* IEnergyReceiver and IEnergyProvider */
+ @Override
+ public int getEnergyStored(ForgeDirection from) {
+
+ return storage.getEnergyStored();
+ }
+
+ @Override
+ public int getMaxEnergyStored(ForgeDirection from) {
+
+ return storage.getMaxEnergyStored();
+ }
+
+}
diff --git a/src/api/java/cofh/api/item/IToolHammer.java b/src/api/java/cofh/api/item/IToolHammer.java
new file mode 100644
index 0000000..f1b4bb9
--- /dev/null
+++ b/src/api/java/cofh/api/item/IToolHammer.java
@@ -0,0 +1,44 @@
+package cofh.api.item;
+
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Implement this interface on subclasses of Item to have that item work as a tool for CoFH mods.
+ */
+public interface IToolHammer {
+
+ /**
+ * Called to ensure that the tool can be used.
+ *
+ * @param item
+ * The itemstack for the tool. Not required to match equipped item (e.g., multi-tools that contain other tools)
+ * @param user
+ * The entity using the tool
+ * @param x
+ * X location of the block/tile
+ * @param y
+ * Y location of the block/tile
+ * @param z
+ * Z location of the block/tile
+ * @return True if this tool can be used
+ */
+ boolean isUsable(ItemStack item, EntityLivingBase user, int x, int y, int z);
+
+ /**
+ * Callback for when the tool has been used reactively.
+ *
+ * @param item
+ * The ItemStack for the tool. Not required to match equipped item (e.g., multi-tools that contain other tools)
+ * @param user
+ * The entity using the tool
+ * @param x
+ * X location of the block/tile
+ * @param y
+ * Y location of the block/tile
+ * @param z
+ * Z location of the block/tile
+ */
+ void toolUsed(ItemStack item, EntityLivingBase user, int x, int y, int z);
+
+}
diff --git a/src/api/java/cofh/lib/util/helpers/BlockHelper.java b/src/api/java/cofh/lib/util/helpers/BlockHelper.java
new file mode 100644
index 0000000..5342727
--- /dev/null
+++ b/src/api/java/cofh/lib/util/helpers/BlockHelper.java
@@ -0,0 +1,567 @@
+package cofh.lib.util.helpers;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.item.EntityItem;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.util.MathHelper;
+import net.minecraft.util.MovingObjectPosition;
+import net.minecraft.util.Vec3;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Contains various helper functions to assist with {@link Block} and Block-related manipulation and interaction.
+ *
+ * @author King Lemming
+ *
+ */
+public final class BlockHelper {
+
+ private BlockHelper() {
+
+ }
+
+ public static int MAX_ID = 1024;
+ public static byte[] rotateType = new byte[MAX_ID];
+ public static final int[][] SIDE_COORD_MOD = { { 0, -1, 0 }, { 0, 1, 0 }, { 0, 0, -1 }, { 0, 0, 1 }, { -1, 0, 0 }, { 1, 0, 0 } };
+ public static float[][] SIDE_COORD_AABB = { { 1, -2, 1 }, { 1, 2, 1 }, { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 1 }, { 2, 1, 1 } };
+ public static final byte[] SIDE_LEFT = { 4, 5, 5, 4, 2, 3 };
+ public static final byte[] SIDE_RIGHT = { 5, 4, 4, 5, 3, 2 };
+ public static final byte[] SIDE_OPPOSITE = { 1, 0, 3, 2, 5, 4 };
+ public static final byte[] SIDE_ABOVE = { 3, 2, 1, 1, 1, 1 };
+ public static final byte[] SIDE_BELOW = { 2, 3, 0, 0, 0, 0 };
+
+ // These assume facing is towards negative - looking AT side 1, 3, or 5.
+ public static final byte[] ROTATE_CLOCK_Y = { 0, 1, 4, 5, 3, 2 };
+ public static final byte[] ROTATE_CLOCK_Z = { 5, 4, 2, 3, 0, 1 };
+ public static final byte[] ROTATE_CLOCK_X = { 2, 3, 1, 0, 4, 5 };
+
+ public static final byte[] ROTATE_COUNTER_Y = { 0, 1, 5, 4, 2, 3 };
+ public static final byte[] ROTATE_COUNTER_Z = { 4, 5, 2, 3, 1, 0 };
+ public static final byte[] ROTATE_COUNTER_X = { 3, 2, 0, 1, 4, 5 };
+
+ public static final byte[] INVERT_AROUND_Y = { 0, 1, 3, 2, 5, 4 };
+ public static final byte[] INVERT_AROUND_Z = { 1, 0, 2, 3, 5, 4 };
+ public static final byte[] INVERT_AROUND_X = { 1, 0, 3, 2, 4, 5 };
+
+ // Map which gives relative Icon to use on a block which can be placed on any side.
+ public static final byte[][] ICON_ROTATION_MAP = new byte[6][];
+
+ static {
+ ICON_ROTATION_MAP[0] = new byte[] { 0, 1, 2, 3, 4, 5 };
+ ICON_ROTATION_MAP[1] = new byte[] { 1, 0, 2, 3, 4, 5 };
+ ICON_ROTATION_MAP[2] = new byte[] { 3, 2, 0, 1, 4, 5 };
+ ICON_ROTATION_MAP[3] = new byte[] { 3, 2, 1, 0, 5, 4 };
+ ICON_ROTATION_MAP[4] = new byte[] { 3, 2, 5, 4, 0, 1 };
+ ICON_ROTATION_MAP[5] = new byte[] { 3, 2, 4, 5, 1, 0 };
+ }
+
+ public static final class RotationType {
+
+ public static final int PREVENT = -1;
+ public static final int FOUR_WAY = 1;
+ public static final int SIX_WAY = 2;
+ public static final int RAIL = 3;
+ public static final int PUMPKIN = 4;
+ public static final int STAIRS = 5;
+ public static final int REDSTONE = 6;
+ public static final int LOG = 7;
+ public static final int SLAB = 8;
+ public static final int CHEST = 9;
+ public static final int LEVER = 10;
+ public static final int SIGN = 11;
+ }
+
+ static { // TODO: review which of these can be removed in favor of the vanilla handler
+ rotateType[Block.getIdFromBlock(Blocks.bed)] = RotationType.PREVENT;
+
+ rotateType[Block.getIdFromBlock(Blocks.stone_slab)] = RotationType.SLAB;
+ rotateType[Block.getIdFromBlock(Blocks.wooden_slab)] = RotationType.SLAB;
+
+ rotateType[Block.getIdFromBlock(Blocks.rail)] = RotationType.RAIL;
+ rotateType[Block.getIdFromBlock(Blocks.golden_rail)] = RotationType.RAIL;
+ rotateType[Block.getIdFromBlock(Blocks.detector_rail)] = RotationType.RAIL;
+ rotateType[Block.getIdFromBlock(Blocks.activator_rail)] = RotationType.RAIL;
+
+ rotateType[Block.getIdFromBlock(Blocks.pumpkin)] = RotationType.PUMPKIN;
+ rotateType[Block.getIdFromBlock(Blocks.lit_pumpkin)] = RotationType.PUMPKIN;
+
+ rotateType[Block.getIdFromBlock(Blocks.furnace)] = RotationType.FOUR_WAY;
+ rotateType[Block.getIdFromBlock(Blocks.lit_furnace)] = RotationType.FOUR_WAY;
+ rotateType[Block.getIdFromBlock(Blocks.ender_chest)] = RotationType.FOUR_WAY;
+
+ rotateType[Block.getIdFromBlock(Blocks.trapped_chest)] = RotationType.CHEST;
+ rotateType[Block.getIdFromBlock(Blocks.chest)] = RotationType.CHEST;
+
+ rotateType[Block.getIdFromBlock(Blocks.dispenser)] = RotationType.SIX_WAY;
+ rotateType[Block.getIdFromBlock(Blocks.sticky_piston)] = RotationType.SIX_WAY;
+ rotateType[Block.getIdFromBlock(Blocks.piston)] = RotationType.SIX_WAY;
+ rotateType[Block.getIdFromBlock(Blocks.hopper)] = RotationType.SIX_WAY;
+ rotateType[Block.getIdFromBlock(Blocks.dropper)] = RotationType.SIX_WAY;
+
+ rotateType[Block.getIdFromBlock(Blocks.unpowered_repeater)] = RotationType.REDSTONE;
+ rotateType[Block.getIdFromBlock(Blocks.unpowered_comparator)] = RotationType.REDSTONE;
+ rotateType[Block.getIdFromBlock(Blocks.powered_repeater)] = RotationType.REDSTONE;
+ rotateType[Block.getIdFromBlock(Blocks.powered_comparator)] = RotationType.REDSTONE;
+
+ rotateType[Block.getIdFromBlock(Blocks.lever)] = RotationType.LEVER;
+
+ rotateType[Block.getIdFromBlock(Blocks.standing_sign)] = RotationType.SIGN;
+
+ rotateType[Block.getIdFromBlock(Blocks.oak_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.stone_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.brick_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.stone_brick_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.nether_brick_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.sandstone_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.spruce_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.birch_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.jungle_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.quartz_stairs)] = RotationType.STAIRS;
+ }
+
+ public static int getMicroBlockAngle(int side, float hitX, float hitY, float hitZ) {
+
+ int direction = side ^ 1;
+ float degreeCenter = 0.32f / 2;
+
+ float x = 0, y = 0;
+ switch (side >> 1) {
+ case 0:
+ x = hitX;
+ y = hitZ;
+ break;
+ case 1:
+ x = hitX;
+ y = hitY;
+ break;
+ case 2:
+ x = hitY;
+ y = hitZ;
+ break;
+ }
+ x -= .5f;
+ y -= .5f;
+
+ if (x * x + y * y > degreeCenter * degreeCenter) {
+
+ int a = (int) ((Math.atan2(x, y) + Math.PI) * 4 / Math.PI);
+ a = ++a & 7;
+ switch (a >> 1) {
+ case 0:
+ case 4:
+ direction = 2;
+ break;
+ case 1:
+ direction = 4;
+ break;
+ case 2:
+ direction = 3;
+ break;
+ case 3:
+ direction = 5;
+ break;
+ }
+ }
+ return direction;
+ }
+
+ public static ForgeDirection getMicroBlockAngle(ForgeDirection side, float hitX, float hitY, float hitZ) {
+
+ return ForgeDirection.VALID_DIRECTIONS[getMicroBlockAngle(side.ordinal(), hitX, hitY, hitZ)];
+ }
+
+ public static int getHighestY(World world, int x, int z) {
+
+ return world.getChunkFromBlockCoords(x, z).getTopFilledSegment() + 16;
+ }
+
+ public static int getSurfaceBlockY(World world, int x, int z) {
+
+ int y = world.getChunkFromBlockCoords(x, z).getTopFilledSegment() + 16;
+
+ Block block;
+ do {
+ if (--y < 0) {
+ break;
+ }
+ block = world.getBlock(x, y, z);
+ } while (block.isAir(world, x, y, z) || block.isReplaceable(world, x, y, z) || block.isLeaves(world, x, y, z) || block.isFoliage(world, x, y, z)
+ || block.canBeReplacedByLeaves(world, x, y, z));
+ return y;
+ }
+
+ public static int getTopBlockY(World world, int x, int z) {
+
+ int y = world.getChunkFromBlockCoords(x, z).getTopFilledSegment() + 16;
+
+ Block block;
+ do {
+ if (--y < 0) {
+ break;
+ }
+ block = world.getBlock(x, y, z);
+ } while (block.isAir(world, x, y, z));
+ return y;
+ }
+
+ public static MovingObjectPosition getCurrentMovingObjectPosition(EntityPlayer player, double distance, boolean fluid) {
+
+ Vec3 posVec = Vec3.createVectorHelper(player.posX, player.posY, player.posZ);
+ Vec3 lookVec = player.getLook(1);
+ posVec.yCoord += player.getEyeHeight();
+ lookVec = posVec.addVector(lookVec.xCoord * distance, lookVec.yCoord * distance, lookVec.zCoord * distance);
+ return player.worldObj.rayTraceBlocks(posVec, lookVec, fluid);
+ }
+
+ public static MovingObjectPosition getCurrentMovingObjectPosition(EntityPlayer player, double distance) {
+
+ return getCurrentMovingObjectPosition(player, distance, false);
+ }
+
+ public static MovingObjectPosition getCurrentMovingObjectPosition(EntityPlayer player, boolean fluid) {
+
+ return getCurrentMovingObjectPosition(player, player.capabilities.isCreativeMode ? 5.0F : 4.5F, fluid);
+ }
+
+ public static MovingObjectPosition getCurrentMovingObjectPosition(EntityPlayer player) {
+
+ return getCurrentMovingObjectPosition(player, player.capabilities.isCreativeMode ? 5.0F : 4.5F, false);
+ }
+
+ public static int getCurrentMousedOverSide(EntityPlayer player) {
+
+ MovingObjectPosition mouseOver = getCurrentMovingObjectPosition(player);
+ return mouseOver == null ? 0 : mouseOver.sideHit;
+ }
+
+ public static int determineXZPlaceFacing(EntityLivingBase living) {
+
+ int quadrant = MathHelper.floor_double(living.rotationYaw * 4.0F / 360.0F + 0.5D) & 3;
+
+ switch (quadrant) {
+ case 0:
+ return 2;
+ case 1:
+ return 5;
+ case 2:
+ return 3;
+ case 3:
+ return 4;
+ }
+ return 3;
+ }
+
+ public static boolean isEqual(Block blockA, Block blockB) {
+
+ if (blockA == blockB) {
+ return true;
+ }
+ if (blockA == null | blockB == null) {
+ return false;
+ }
+ return blockA.equals(blockB) || blockA.isAssociatedBlock(blockB);
+ }
+
+ /* UNSAFE Tile Entity Retrieval */
+ // public static TileEntity getAdjacentTileEntityUnsafe(World world, int x, int y, int z, ForgeDirection dir) {
+ //
+ // if (world == null) {
+ // return null;
+ // }
+ // Chunk chunk = world.getChunkFromBlockCoords(x + dir.offsetX, z + dir.offsetZ);
+ // return chunk == null ? null : chunk.getChunkBlockTileEntityUnsafe((x + dir.offsetX) & 0xF, y + dir.offsetY, (z + dir.offsetZ) & 0xF);
+ // }
+ //
+ // public static TileEntity getAdjacentTileEntityUnsafe(World world, int x, int y, int z, int side) {
+ //
+ // return world == null ? null : getAdjacentTileEntityUnsafe(world, x, y, z, ForgeDirection.values()[side]);
+ // }
+ //
+ // public static TileEntity getAdjacentTileEntityUnsafe(TileEntity refTile, ForgeDirection dir) {
+ //
+ // return refTile == null ? null : getAdjacentTileEntityUnsafe(refTile.worldObj, refTile.xCoord, refTile.yCoord, refTile.zCoord, dir);
+ // }
+ //
+ // public static TileEntity getAdjacentTileEntityUnsafe(TileEntity refTile, int side) {
+ //
+ // return refTile == null ? null : getAdjacentTileEntityUnsafe(refTile.worldObj, refTile.xCoord, refTile.yCoord, refTile.zCoord,
+ // ForgeDirection.values()[side]);
+ // }
+
+ public static Block getAdjacentBlock(World world, int x, int y, int z, ForgeDirection dir) {
+
+ x += dir.offsetX;
+ y += dir.offsetY;
+ z += dir.offsetZ;
+ return world == null || !world.blockExists(x, y, z) ? Blocks.air : world.getBlock(x, y, z);
+ }
+
+ public static Block getAdjacentBlock(World world, int x, int y, int z, int side) {
+
+ return world == null ? Blocks.air : getAdjacentBlock(world, x, y, z, ForgeDirection.getOrientation(side));
+ }
+
+ /* Safe Tile Entity Retrieval */
+ public static TileEntity getAdjacentTileEntity(World world, int x, int y, int z, ForgeDirection dir) {
+
+ x += dir.offsetX;
+ y += dir.offsetY;
+ z += dir.offsetZ;
+ return world == null || !world.blockExists(x, y, z) ? null : world.getTileEntity(x, y, z);
+ }
+
+ public static TileEntity getAdjacentTileEntity(World world, int x, int y, int z, int side) {
+
+ return world == null ? null : getAdjacentTileEntity(world, x, y, z, ForgeDirection.getOrientation(side));
+ }
+
+ public static TileEntity getAdjacentTileEntity(TileEntity refTile, ForgeDirection dir) {
+
+ return refTile == null ? null : getAdjacentTileEntity(refTile.getWorldObj(), refTile.xCoord, refTile.yCoord, refTile.zCoord, dir);
+ }
+
+ public static TileEntity getAdjacentTileEntity(TileEntity refTile, int side) {
+
+ return refTile == null ? null : getAdjacentTileEntity(refTile.getWorldObj(), refTile.xCoord, refTile.yCoord, refTile.zCoord,
+ ForgeDirection.values()[side]);
+ }
+
+ public static int determineAdjacentSide(TileEntity refTile, int x, int y, int z) {
+
+ return y > refTile.yCoord ? 1 : y < refTile.yCoord ? 0 : z > refTile.zCoord ? 3 : z < refTile.zCoord ? 2 : x > refTile.xCoord ? 5 : 4;
+ }
+
+ /* COORDINATE TRANSFORM */
+ public static int[] getAdjacentCoordinatesForSide(MovingObjectPosition pos) {
+
+ return getAdjacentCoordinatesForSide(pos.blockX, pos.blockY, pos.blockZ, pos.sideHit);
+ }
+
+ public static int[] getAdjacentCoordinatesForSide(int x, int y, int z, int side) {
+
+ return new int[] { x + SIDE_COORD_MOD[side][0], y + SIDE_COORD_MOD[side][1], z + SIDE_COORD_MOD[side][2] };
+ }
+
+ public static AxisAlignedBB getAdjacentAABBForSide(MovingObjectPosition pos) {
+
+ return getAdjacentAABBForSide(pos.blockX, pos.blockY, pos.blockZ, pos.sideHit);
+ }
+
+ public static AxisAlignedBB getAdjacentAABBForSide(int x, int y, int z, int side) {
+
+ return AxisAlignedBB.getBoundingBox(x + SIDE_COORD_MOD[side][0], y + SIDE_COORD_MOD[side][1], z + SIDE_COORD_MOD[side][2],
+ x + SIDE_COORD_AABB[side][0], y + SIDE_COORD_AABB[side][1], z + SIDE_COORD_AABB[side][2]);
+ }
+
+ public static int getLeftSide(int side) {
+
+ return SIDE_LEFT[side];
+ }
+
+ public static int getRightSide(int side) {
+
+ return SIDE_RIGHT[side];
+ }
+
+ public static int getOppositeSide(int side) {
+
+ return SIDE_OPPOSITE[side];
+ }
+
+ public static int getAboveSide(int side) {
+
+ return SIDE_ABOVE[side];
+ }
+
+ public static int getBelowSide(int side) {
+
+ return SIDE_BELOW[side];
+ }
+
+ /* BLOCK ROTATION */
+ public static boolean canRotate(Block block) {
+
+ int bId = Block.getIdFromBlock(block);
+ return bId < MAX_ID ? rotateType[Block.getIdFromBlock(block)] != 0 : false;
+ }
+
+ public static int rotateVanillaBlock(World world, Block block, int x, int y, int z) {
+
+ int bId = Block.getIdFromBlock(block), bMeta = world.getBlockMetadata(x, y, z);
+ switch (rotateType[bId]) {
+ case RotationType.FOUR_WAY:
+ return SIDE_LEFT[bMeta];
+ case RotationType.SIX_WAY:
+ if (bMeta < 6) {
+ return ++bMeta % 6;
+ }
+ return bMeta;
+ case RotationType.RAIL:
+ if (bMeta < 2) {
+ return ++bMeta % 2;
+ }
+ return bMeta;
+ case RotationType.PUMPKIN:
+ return ++bMeta % 4;
+ case RotationType.STAIRS:
+ return ++bMeta % 8;
+ case RotationType.REDSTONE:
+ int upper = bMeta & 0xC;
+ int lower = bMeta & 0x3;
+ return upper + ++lower % 4;
+ case RotationType.LOG:
+ return (bMeta + 4) % 12;
+ case RotationType.SLAB:
+ return (bMeta + 8) % 16;
+ case RotationType.CHEST:
+ int coords[] = new int[3];
+ for (int i = 2; i < 6; i++) {
+ coords = getAdjacentCoordinatesForSide(x, y, z, i);
+ if (isEqual(world.getBlock(coords[0], coords[1], coords[2]), block)) {
+ world.setBlockMetadataWithNotify(coords[0], coords[1], coords[2], SIDE_OPPOSITE[bMeta], 1);
+ return SIDE_OPPOSITE[bMeta];
+ }
+ }
+ return SIDE_LEFT[bMeta];
+ case RotationType.LEVER:
+ int shift = 0;
+ if (bMeta > 7) {
+ bMeta -= 8;
+ shift = 8;
+ }
+ if (bMeta == 5) {
+ return 6 + shift;
+ } else if (bMeta == 6) {
+ return 5 + shift;
+ } else if (bMeta == 7) {
+ return 0 + shift;
+ } else if (bMeta == 0) {
+ return 7 + shift;
+ }
+ return bMeta + shift;
+ case RotationType.SIGN:
+ return ++bMeta % 16;
+ case RotationType.PREVENT:
+ default:
+ return bMeta;
+ }
+ }
+
+ public static int rotateVanillaBlockAlt(World world, Block block, int x, int y, int z) {
+
+ int bId = Block.getIdFromBlock(block), bMeta = world.getBlockMetadata(x, y, z);
+ switch (rotateType[bId]) {
+ case RotationType.FOUR_WAY:
+ return SIDE_RIGHT[bMeta];
+ case RotationType.SIX_WAY:
+ if (bMeta < 6) {
+ return (bMeta + 5) % 6;
+ }
+ return bMeta;
+ case RotationType.RAIL:
+ if (bMeta < 2) {
+ return ++bMeta % 2;
+ }
+ return bMeta;
+ case RotationType.PUMPKIN:
+ return (bMeta + 3) % 4;
+ case RotationType.STAIRS:
+ return (bMeta + 7) % 8;
+ case RotationType.REDSTONE:
+ int upper = bMeta & 0xC;
+ int lower = bMeta & 0x3;
+ return upper + (lower + 3) % 4;
+ case RotationType.LOG:
+ return (bMeta + 8) % 12;
+ case RotationType.SLAB:
+ return (bMeta + 8) % 16;
+ case RotationType.CHEST:
+ int coords[] = new int[3];
+ for (int i = 2; i < 6; i++) {
+ coords = getAdjacentCoordinatesForSide(x, y, z, i);
+ if (isEqual(world.getBlock(coords[0], coords[1], coords[2]), block)) {
+ world.setBlockMetadataWithNotify(coords[0], coords[1], coords[2], SIDE_OPPOSITE[bMeta], 1);
+ return SIDE_OPPOSITE[bMeta];
+ }
+ }
+ return SIDE_RIGHT[bMeta];
+ case RotationType.LEVER:
+ int shift = 0;
+ if (bMeta > 7) {
+ bMeta -= 8;
+ shift = 8;
+ }
+ if (bMeta == 5) {
+ return 6 + shift;
+ } else if (bMeta == 6) {
+ return 5 + shift;
+ } else if (bMeta == 7) {
+ return 0 + shift;
+ } else if (bMeta == 0) {
+ return 7 + shift;
+ }
+ case RotationType.SIGN:
+ return ++bMeta % 16;
+ case RotationType.PREVENT:
+ default:
+ return bMeta;
+ }
+ }
+
+ public static List<ItemStack> breakBlock(World worldObj, int x, int y, int z, Block block, int fortune, boolean doBreak, boolean silkTouch) {
+
+ return breakBlock(worldObj, null, x, y, z, block, fortune, doBreak, silkTouch);
+ }
+
+ public static List<ItemStack> breakBlock(World worldObj, EntityPlayer player, int x, int y, int z, Block block, int fortune, boolean doBreak,
+ boolean silkTouch) {
+
+ if (block.getBlockHardness(worldObj, x, y, z) == -1) {
+ return new LinkedList<ItemStack>();
+ }
+ int meta = worldObj.getBlockMetadata(x, y, z);
+ List<ItemStack> stacks = null;
+ if (silkTouch && block.canSilkHarvest(worldObj, player, x, y, z, meta)) {
+ stacks = new LinkedList<ItemStack>();
+ stacks.add(createStackedBlock(block, meta));
+ } else {
+ stacks = block.getDrops(worldObj, x, y, z, meta, fortune);
+ }
+ if (!doBreak) {
+ return stacks;
+ }
+ worldObj.playAuxSFXAtEntity(player, 2001, x, y, z, Block.getIdFromBlock(block) + (meta << 12));
+ worldObj.setBlockToAir(x, y, z);
+
+ List<EntityItem> result = worldObj.getEntitiesWithinAABB(EntityItem.class, AxisAlignedBB.getBoundingBox(x - 2, y - 2, z - 2, x + 3, y + 3, z + 3));
+ for (int i = 0; i < result.size(); i++) {
+ EntityItem entity = result.get(i);
+ if (entity.isDead || entity.getEntityItem().stackSize <= 0) {
+ continue;
+ }
+ stacks.add(entity.getEntityItem());
+ entity.worldObj.removeEntity(entity);
+ }
+ return stacks;
+ }
+
+ public static ItemStack createStackedBlock(Block block, int bMeta) {
+
+ Item item = Item.getItemFromBlock(block);
+ if (item.getHasSubtypes()) {
+ return new ItemStack(item, 1, bMeta);
+ }
+ return new ItemStack(item, 1, 0);
+ }
+
+}
diff --git a/src/api/java/ic2/api/Direction.java b/src/api/java/ic2/api/Direction.java
new file mode 100644
index 0000000..b550ca3
--- /dev/null
+++ b/src/api/java/ic2/api/Direction.java
@@ -0,0 +1,135 @@
+package ic2.api;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.World;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Represents the 6 possible directions along the axis of a block.
+ */
+public enum Direction {
+ /**
+ * -X
+ */
+ XN,
+ /**
+ * +X
+ */
+ XP,
+
+ /**
+ * -Y
+ */
+ YN, //MC-Code starts with 0 here
+ /**
+ * +Y
+ */
+ YP, // 1...
+
+ /**
+ * -Z
+ */
+ ZN,
+ /**
+ * +Z
+ */
+ ZP;
+
+ private Direction() {
+ int side = ordinal() / 2;
+ int sign = getSign();
+
+ xOffset = side == 0 ? sign : 0;
+ yOffset = side == 1 ? sign : 0;
+ zOffset = side == 2 ? sign : 0;
+ }
+
+ public static Direction fromSideValue(int side) {
+ return directions[(side + 2) % 6];
+ }
+
+ public static Direction fromForgeDirection(ForgeDirection dir) {
+ if (dir == ForgeDirection.UNKNOWN) return null;
+
+ return fromSideValue(dir.ordinal());
+ }
+
+ /**
+ * Get the tile entity next to a tile entity following this direction.
+ *
+ * @param tileEntity tile entity to check
+ * @return Adjacent tile entity or null if none exists
+ */
+ public TileEntity applyToTileEntity(TileEntity te) {
+ return applyTo(te.getWorldObj(), te.xCoord, te.yCoord, te.zCoord);
+ }
+
+ /**
+ * Get the tile entity next to a position following this direction.
+ *
+ * @param world World to check
+ * @param x X coordinate to check from
+ * @param y Y coordinate to check from
+ * @param z Z coordinate to check from
+ * @return Adjacent tile entity or null if none exists
+ */
+ public TileEntity applyTo(World world, int x, int y, int z) {
+ int coords[] = { x, y, z };
+
+ coords[ordinal() / 2] += getSign();
+
+ if (world != null && world.blockExists(coords[0], coords[1], coords[2])) {
+ try {
+ return world.getTileEntity(coords[0], coords[1], coords[2]);
+ } catch (Exception e) {
+ throw new RuntimeException("error getting TileEntity at dim "+world.provider.dimensionId+" "+coords[0]+"/"+coords[1]+"/"+coords[2]);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the inverse of this direction (XN -> XP, XP -> XN, etc.)
+ *
+ * @return Inverse direction
+ */
+ public Direction getInverse() {
+ return directions[ordinal() ^ 1];
+ }
+
+ /**
+ * Convert this direction to a Minecraft side value.
+ *
+ * @return Minecraft side value
+ */
+ public int toSideValue() {
+ return (ordinal() + 4) % 6;
+ }
+
+ /**
+ * Determine direction sign (N for negative or P for positive).
+ *
+ * @return -1 if the direction is negative, +1 if the direction is positive
+ */
+ private int getSign() {
+ return (ordinal() % 2) * 2 - 1;
+ }
+
+ public ForgeDirection toForgeDirection() {
+ return ForgeDirection.getOrientation(toSideValue());
+ }
+
+ public final int xOffset;
+ public final int yOffset;
+ public final int zOffset;
+
+ public static final Direction[] directions = Direction.values();
+ public static final Set<Direction> noDirections = EnumSet.noneOf(Direction.class);
+ public static final Set<Direction> allDirections = EnumSet.allOf(Direction.class);
+}
+
diff --git a/src/api/java/ic2/api/crops/BaseSeed.java b/src/api/java/ic2/api/crops/BaseSeed.java
new file mode 100644
index 0000000..89e7865
--- /dev/null
+++ b/src/api/java/ic2/api/crops/BaseSeed.java
@@ -0,0 +1,82 @@
+package ic2.api.crops;
+
+/**
+ * Base agriculture seed. Used to determine the state of a plant once it is planted from an item.
+ */
+public class BaseSeed {
+ /**
+ * Create a BaseSeed object.
+ *
+ * @param crop plant
+ * @param size plant size
+ * @param statGrowth1 plant growth stat
+ * @param statGain1 plant gain stat
+ * @param statResistance1 plant resistance stat
+ * @param stackSize1 for internal usage only
+ */
+ @SuppressWarnings("deprecation")
+ public BaseSeed(CropCard crop, int size, int statGrowth, int statGain, int statResistance, int stackSize) {
+ super();
+ this.crop = crop;
+ this.id = Crops.instance.getIdFor(crop);
+ this.size = size;
+ this.statGrowth = statGrowth;
+ this.statGain = statGain;
+ this.statResistance = statResistance;
+ this.stackSize = stackSize;
+ }
+
+ /**
+ * @deprecated Use the CropCard version.
+ */
+ @Deprecated
+ public BaseSeed(int id, int size, int statGrowth, int statGain, int statResistance, int stackSize) {
+ this(getCropFromId(id), size, statGrowth, statGain, statResistance, stackSize);
+ }
+
+ @SuppressWarnings("deprecation")
+ private static CropCard getCropFromId(int id) {
+ CropCard[] crops = Crops.instance.getCropList();
+
+ if (id < 0 || id >= crops.length) return null;
+
+ return crops[id];
+ }
+
+
+ /**
+ * Plant.
+ */
+ public final CropCard crop;
+
+ /**
+ * @deprecated IDs aren't used anymore.
+ */
+ @Deprecated
+ public int id;
+
+ /**
+ * Plant size.
+ */
+ public int size;
+
+ /**
+ * Plant growth stat.
+ */
+ public int statGrowth;
+
+ /**
+ * Plant gain stat.
+ */
+ public int statGain;
+
+ /**
+ * Plant resistance stat.
+ */
+ public int statResistance;
+
+ /**
+ * For internal usage only.
+ */
+ public int stackSize;
+}
diff --git a/src/api/java/ic2/api/crops/CropCard.java b/src/api/java/ic2/api/crops/CropCard.java
new file mode 100644
index 0000000..1d2b640
--- /dev/null
+++ b/src/api/java/ic2/api/crops/CropCard.java
@@ -0,0 +1,435 @@
+package ic2.api.crops;
+
+import net.minecraft.client.renderer.texture.IIconRegister;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.IIcon;
+
+import cpw.mods.fml.common.Loader;
+import cpw.mods.fml.common.ModContainer;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+/**
+ * Base agriculture crop.
+ *
+ * Any crop extending this can be registered using registerCrop to be added into the game.
+ */
+public abstract class CropCard {
+ public CropCard() {
+ modId = getModId(); // initialize mod id while we should be in the real owner's init event
+ }
+
+ /**
+ * Plant name for identifying this crop within your mod.
+ *
+ * The name has to be unique within the mod and is used for saving.
+ * By default this name will be also used to determine displayKey() and registerSprites().
+ *
+ * @note changing name or owner will cause existing crops in users' worlds to disappear.
+ *
+ * @return Plant name
+ */
+ public abstract String name();
+
+ /**
+ * Determine the mod id owning this crop.
+ *
+ * The owner serves as a name space. With every mod using a different owner, a mod only has to
+ * make sure it doesn't have conflicts with name() in itself.
+ * It's recommended to hard code this to your mod id as specified in the @Mod annotation.
+ * Do not use IC2's mod id here.
+ *
+ * @note changing name or owner will cause existing crops in users' worlds to disappear.
+ *
+ * @return Mod id.
+ */
+ public String owner() { // TODO: make abstract
+ return modId;
+ }
+
+ /**
+ * Translation key for display to the player.
+ *
+ * It's highly recommended to specify a valid key from your language file here, e.g. add
+ * "yourmod.crop.yourCropName = Your crop's name" to the language file, override name() to
+ * return "yourCropName" and override displayName() to return "yourmod.crop."+name().
+ *
+ * @return Unlocalized name.
+ */
+ public String displayName() {
+ return name(); // return the raw name for backwards compatibility
+ }
+
+ /**
+ * Your name here, will be shown in "Discovered by:" when analyzing seeds.
+ *
+ * @return Your name
+ */
+ public String discoveredBy() {
+ return "unknown";
+ }
+
+ /**
+ * Description of your plant. Keep it short, a few characters per line for up to two lines.
+ * Default is showing attributes of your plant, 2 per line.
+ *
+ * @param i line to get, starting from 0
+ * @return The line
+ */
+ public String desc(int i) {
+ String[] att = attributes();
+
+ if (att == null || att.length == 0) return "";
+
+ if (i == 0) {
+ String s = att[0];
+ if (att.length >= 2) {
+ s+=", "+att[1];
+ if (att.length >= 3) s+=",";
+ }
+ return s;
+ }
+ if (att.length < 3) return "";
+ String s = att[2];
+ if (att.length >= 4) s+=", "+att[3];
+ return s;
+ }
+
+ /**
+ * *
+ * @param crop reference to ICropTile
+ * @return roots lengt use in isBlockBelow
+ */
+ public int getrootslength(ICropTile crop) { // TODO: camel case
+ return 1;
+ }
+
+ /**
+ * Tier of the plant. Ranges from 1 to 16, 0 is Weed.
+ * Valuable and powerful crops have higher tiers, useless and weak ones have lower tiers.
+ *
+ * @return Tier
+ */
+ public abstract int tier();
+
+ /**
+ * Describe the plant through a set of stats, influencing breeding.
+ * Plants sharing stats and attributes will tend to cross-breed more often.
+ *
+ * Stats:
+ * - 0: Chemistry (Industrial uses based on chemical plant components)
+ * - 1: Consumable (Food, potion ingredients, stuff meant to be eaten or similarly used)
+ * - 2: Defensive (Plants with defense capabilities (damaging, explosive, chemical) or special abilities in general)
+ * - 3: Colorful (How colorful/aesthetically/beautiful is the plant, like dye-plants or plants without actual effects)
+ * - 4: Weed (Is this plant weed-like and rather unwanted/quick-spreading? Rare super-breed plants should have low values here)
+ *
+ * @param n index of the requested stat
+ * @return The requested value of the stats
+ */
+ public abstract int stat(int n); // TODO: change to fixed property object or builder with other attributes like tier
+
+ /**
+ * Additional attributes of the plant, also influencing breeding.
+ * Plants sharing stats and attributes will tend to cross-breed more often.
+ *
+ * @return Attributes as an array of strings
+ */
+ public abstract String[] attributes(); // TODO: default to none
+
+ /**
+ * Determine the max crop size.
+ *
+ * Currently only used for texture allocation.
+ */
+ public abstract int maxSize();
+
+ /**
+ * Instantiate your Icons here.
+ *
+ * This method will get called by IC2, don't call it yourself.
+ *
+ * It's highly recommended to use your own assert domain here, e.g. yourmod:crop/* instead of
+ * ic2:crop/*, which will then read assets/yourmod/textures/blocks/crop/*.png.
+ */
+ @SideOnly(Side.CLIENT)
+ public void registerSprites(IIconRegister iconRegister) {
+ textures = new IIcon[maxSize()];
+
+ for (int i = 1; i <= textures.length; i++) {
+ // ic2:crop/blockCrop.NAME.n is the legacy name for backwards compatiblity
+ textures[i - 1] = iconRegister.registerIcon("ic2:crop/blockCrop."+name()+"."+i);
+ }
+ }
+
+ /**
+ * Sprite the crop is meant to be rendered with.
+ *
+ * @param crop reference to ICropTile
+ * @return 0-255, representing the sprite index on the crop's spritesheet.
+ */
+ @SideOnly(Side.CLIENT)
+ public IIcon getSprite(ICropTile crop) {
+ if (crop.getSize() <= 0 || crop.getSize() > textures.length) return null;
+
+ return textures[crop.getSize() - 1];
+ }
+
+ /**
+ * Amount of growth points needed to increase the plant's size.
+ * Default is 200 * tier.
+ */
+ public int growthDuration(ICropTile crop) {
+ return tier() * 200;
+ }
+
+ /**
+ * Check whether the plant can grow further.
+ *
+ * Consider:
+ * - Humidity, nutrients and air quality
+ * - Current size
+ * - Light level
+ * - Special biomes or conditions, accessible through crop.worldObj
+ *
+ * This method will be called upon empty upgraded crops to check whether a neighboring plant can cross onto it! Don't check if the size is greater than 0 and if the ID is real.
+ *
+ * @param crop reference to ICropTile
+ * @return Whether the crop can grow
+ */
+ public abstract boolean canGrow(ICropTile crop);
+
+ /**
+ * Calculate the influence for the plant to grow based on humidity, nutrients and air.
+ * Normal behavior is rating the three stats "normal", with each of them ranging from 0-30.
+ * Basic rule: Assume everything returns 10. All together must equal 30. Add the factors to your likings, for example (humidity*0.7)+(nutrients*0.9)+(air*1.4)
+ *
+ * Default is humidity + nutrients + air (no factors).
+ *
+ * @param crop reference to ICropTile
+ * @param humidity ground humidity, influenced by hydration
+ * @param nutrients nutrient quality in ground, based on fertilizers
+ * @param air air quality, influences by open gardens and less crops surrounding this one
+ * @return 0-30
+ */
+ public int weightInfluences(ICropTile crop, float humidity, float nutrients, float air) {
+ return (int) (humidity + nutrients + air);
+ }
+
+ /**
+ * Used to determine whether the plant can crossbreed with another crop.
+ * Default is allow crossbreeding if the size is greater or equal than 3.
+ *
+ * @param crop crop to crossbreed with
+ */
+ public boolean canCross(ICropTile crop) {
+ return crop.getSize() >= 3;
+ }
+
+
+ /**
+ * Called when the plant is rightclicked by a player.
+ * Default action is harvesting.
+ *
+ * Only called Serverside.
+ *
+ * @param crop reference to ICropTile
+ * @param player player rightclicking the crop
+ * @return Whether the plant has changed
+ */
+ public boolean rightclick(ICropTile crop, EntityPlayer player) {
+ return crop.harvest(true);
+ }
+
+ /**
+ * Use in Crop Havester with insert Cropnalyzer to get best Output.
+ *
+ * @param crop reference to ICropTile
+ * @return need crop size for best output.
+ */
+
+ public abstract int getOptimalHavestSize(ICropTile crop); // TODO: default to maxSize
+
+ /**
+ * Check whether the crop can be harvested.
+ *
+ * @param crop reference to ICropTile
+ * @return Whether the crop can be harvested in its current state.
+ */
+ public abstract boolean canBeHarvested(ICropTile crop); // TODO: default to maxSize check
+
+ /**
+ * Base chance for dropping the plant's gains, specify values greater than 1 for multiple drops.
+ * Default is 0.95^tier.
+ *
+ * @return Chance to drop the gains
+ */
+ public float dropGainChance() { // TODO: change to double
+ return (float) Math.pow(0.95, tier());
+ }
+
+ /**
+ * Item obtained from harvesting the plant.
+ *
+ * @param crop reference to ICropTile
+ * @return Item obtained
+ */
+ public abstract ItemStack getGain(ICropTile crop);
+
+ /**
+ * Get the size of the plant after harvesting.
+ * Default is 1.
+ *
+ * @param crop reference to ICropTile
+ * @return Plant size after harvesting
+ */
+ public byte getSizeAfterHarvest(ICropTile crop) {return 1;} // TODO: change to int
+
+
+ /**
+ * Called when the plant is left clicked by a player.
+ * Default action is picking the plant.
+ *
+ * Only called server side.
+ *
+ * @param crop reference to ICropTile
+ * @param player player left clicked the crop
+ * @return Whether the plant has changed
+ */
+ public boolean leftclick(ICropTile crop, EntityPlayer player) { // TODO: camel case
+ return crop.pick(true);
+ }
+
+ /**
+ * Base chance for dropping seeds when the plant is picked.
+ * Default is 0.5*0.8^tier with a bigger chance for sizes greater than 2 and absolutely no chance for size 0.
+ *
+ * @param crop reference to ICropTile
+ * @return Chance to drop the seeds
+ */
+ public float dropSeedChance(ICropTile crop) {
+ if (crop.getSize() == 1) return 0;
+ float base = 0.5F;
+ if (crop.getSize() == 2) base/=2F;
+ for (int i = 0; i < tier(); i++) {base*=0.8;}
+ return base;
+ }
+
+ /**
+ * Obtain seeds dropped when the plant is picked.
+ * Multiple drops of the returned ItemStack can occur.
+ * Default action is generating a seed from this crop.
+ *
+ * @param crop reference to ICropTile
+ * @return Seeds
+ */
+ public ItemStack getSeeds(ICropTile crop) {
+ return crop.generateSeeds(crop.getCrop(), crop.getGrowth(), crop.getGain(), crop.getResistance(), crop.getScanLevel());
+ }
+
+ /**
+ * Called when a neighbor block to the crop has changed.
+ *
+ * @param crop reference to ICropTile
+ */
+ public void onNeighbourChange(ICropTile crop) {
+ //
+ }
+
+ /**
+ * Check if the crop should emit redstone.
+ *
+ * @return Whether the crop should emit redstone
+ */
+ public int emitRedstone(ICropTile crop) {return 0;}
+
+ /**
+ * Called when the crop is destroyed.
+ *
+ * @param crop reference to ICropTile
+ */
+ public void onBlockDestroyed(ICropTile crop) {
+ //
+ }
+
+ /**
+ * Get the light value emitted by the plant.
+ *
+ * @param crop reference to ICropTile
+ * @return Light value emitted
+ */
+ public int getEmittedLight(ICropTile crop) {
+ return 0;
+ }
+
+ /**
+ * Default is true if the entity is an EntityLiving in jumping or sprinting state.
+ *
+ * @param crop reference to ICropTile
+ * @param entity entity colliding
+ * @return Whether trampling calculation should happen, return false if the plant is no longer valid.
+ */
+ public boolean onEntityCollision(ICropTile crop, Entity entity) {
+ if (entity instanceof EntityLivingBase) {
+ return ((EntityLivingBase) entity).isSprinting();
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Called every time the crop ticks.
+ * Should be called every 256 ticks or around 13 seconds.
+ *
+ * @param crop reference to ICropTile
+ */
+ public void tick(ICropTile crop) {
+ // nothing by default
+ }
+
+ /**
+ * Check whether this plant spreads weed to surrounding tiles.
+ * Default is true if the plant has a high growth stat (or is weeds) and size greater or equal than 2.
+ *
+ * @param crop reference to ICropTile
+ * @return Whether the plant spreads weed
+ */
+ public boolean isWeed(ICropTile crop) {
+ return crop.getSize() >= 2 &&
+ (crop.getCrop() == Crops.weed || crop.getGrowth() >= 24);
+ }
+
+
+ /**
+ * Get this plant's ID.
+ *
+ * @return ID of this CropCard or -1 if it's not registered
+ * @deprecated IDs aren't used anymore.
+ */
+ @Deprecated
+ public final int getId() {
+ return Crops.instance.getIdFor(this);
+ }
+
+ private static String getModId() {
+ ModContainer modContainer = Loader.instance().activeModContainer();
+
+ if (modContainer != null) {
+ return modContainer.getModId();
+ } else {
+ // this is bad if you are not actually IC2
+ assert false;
+
+ return "unknown";
+ }
+ }
+
+ @SideOnly(Side.CLIENT)
+ protected IIcon textures[];
+
+ private final String modId; // TODO: make owner abstract, remove modId auto detection
+ // TODO: add a clean way to obtain world reference and position
+}
diff --git a/src/api/java/ic2/api/crops/Crops.java b/src/api/java/ic2/api/crops/Crops.java
new file mode 100644
index 0000000..a11059a
--- /dev/null
+++ b/src/api/java/ic2/api/crops/Crops.java
@@ -0,0 +1,151 @@
+package ic2.api.crops;
+
+import java.util.Collection;
+
+import net.minecraft.client.renderer.texture.IIconRegister;
+import net.minecraft.item.ItemStack;
+import net.minecraft.world.biome.BiomeGenBase;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+import net.minecraftforge.common.BiomeDictionary.Type;
+
+/**
+ * General management of the crop system.
+ */
+public abstract class Crops {
+ public static Crops instance;
+
+ public static CropCard weed; // weed has special properties, thus it's exposed here
+
+ /**
+ * Adds a crop nutrient biome bonus.
+ *
+ * +10/-10 0 indicates no bonus and negative values indicate a penalty.
+ *
+ * @param type Forge biome type to apply the bonus in
+ * @param nutrientsBonus Nutrient stat bonus
+ */
+ public abstract void addBiomenutrientsBonus(Type type, int nutrientsBonus);
+
+ /**
+ * Adds a crop humidity biome bonus.
+ *
+ * +10/-10 0 indicates no bonus and negative values indicate a penalty.
+ *
+ * @param type Forge biome type to apply the bonus in
+ * @param humidityBonus Humidity stat bonus
+ */
+ public abstract void addBiomehumidityBonus(Type type, int humidityBonus);
+
+
+ /**
+ * Gets the humidity bonus for a biome.
+ *
+ * @param biome Biome to check
+ * @return Humidity bonus or 0 if none
+ */
+ public abstract int getHumidityBiomeBonus(BiomeGenBase biome);
+
+ /**
+ * Gets the nutrient bonus for a biome.
+ *
+ * @param biome Biome to check
+ * @return Nutrient bonus or 0 if none
+ */
+ public abstract int getNutrientBiomeBonus(BiomeGenBase biome);
+
+ /**
+ * Get the crop card for the specified owner and name.
+ *
+ * @param owner CropCard owner mod id.
+ * @param name CropCard name.
+ * @return Matching CropCard.
+ */
+ public abstract CropCard getCropCard(String owner, String name);
+
+ /**
+ * Get the crop card for the specified seed item stack.
+ *
+ * @param stack ItemStack containing seeds for the crop.
+ * @return Matching CropCard.
+ */
+ public abstract CropCard getCropCard(ItemStack stack);
+
+ /**
+ * Returns a list of all crops.
+ *
+ * @return All registered crops.
+ */
+ public abstract Collection<CropCard> getCrops();
+
+ /**
+ * Returns the list of registered crops.
+ *
+ * @return Registered crops by ID
+ */
+ @Deprecated
+ public abstract CropCard[] getCropList();
+
+ /**
+ * Register a plant.
+ *
+ * @param crop Plant to register.
+ * @return Autoassigned id for legacy compatibility, TODO: change to void.
+ */
+ public abstract short registerCrop(CropCard crop);
+
+ /**
+ * Register a plant and provide a legacy id for migration.
+ *
+ * @param crop Plant to register.
+ * @param legacyId ID previously used for this crop.
+ * @return true, TODO: change to void.
+ */
+ public abstract boolean registerCrop(CropCard crop, int legacyId);
+
+ /**
+ * @deprecated use the CropCard version.
+ */
+ @Deprecated
+ public abstract boolean registerBaseSeed(ItemStack stack, int id, int size, int growth, int gain, int resistance);
+
+ /**
+ * Registers a base seed, an item used to plant a crop.
+ *
+ * @param stack item
+ * @param id plant ID
+ * @param size initial size
+ * @param growth initial growth stat
+ * @param gain initial gain stat
+ * @param resistance initial resistance stat
+ * @return True if successful
+ */
+ public abstract boolean registerBaseSeed(ItemStack stack, CropCard crop, int size, int growth, int gain, int resistance);
+
+ /**
+ * Finds a base seed from the given item.
+ *
+ * @return Base seed or null if none found
+ */
+ public abstract BaseSeed getBaseSeed(ItemStack stack);
+
+ /**
+ * Execute registerSprites for all registered crop cards.
+ *
+ * This method will get called by IC2, don't call it yourself.
+ */
+ @SideOnly(Side.CLIENT)
+ public abstract void startSpriteRegistration(IIconRegister iconRegister);
+
+ /**
+ * Returns the ID for the given crop.
+ *
+ * @param crop Crop to look up
+ * @return ID, or -1 if not found
+ * @deprecated IDs aren't used anymore.
+ */
+ @Deprecated
+ public abstract int getIdFor(CropCard crop);
+}
diff --git a/src/api/java/ic2/api/crops/ICropTile.java b/src/api/java/ic2/api/crops/ICropTile.java
new file mode 100644
index 0000000..dadf691
--- /dev/null
+++ b/src/api/java/ic2/api/crops/ICropTile.java
@@ -0,0 +1,289 @@
+package ic2.api.crops;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.ChunkCoordinates;
+import net.minecraft.world.World;
+
+/**
+ * Interface implemented by the crop tile entity.
+ */
+public interface ICropTile {
+ /**
+ * Get the crop.
+ *
+ * @return CropCard, or null if there is no plant currently on the crop
+ */
+ public CropCard getCrop();
+
+ /**
+ * Set the crop.
+ *
+ * @param cropCard CropCard or null for no plant
+ */
+ public void setCrop(CropCard cropCard);
+
+ /**
+ * @deprecated IDs have been removed.
+ */
+ @Deprecated
+ public short getID();
+
+ /**
+ * @deprecated IDs have been removed.
+ */
+ @Deprecated
+ public void setID(short id);
+
+ /**
+ * Get the crop's plant size.
+ *
+ * @return Plant size, starting with 1 and maximum varies depending on plant
+ */
+ public byte getSize();
+
+ /**
+ * Set the crop's plant size.
+ *
+ * @param size Plant size
+ */
+ public void setSize(byte size);
+
+ /**
+ * Get the crop's plant growth stat.
+ * Higher values indicate faster growth.
+ *
+ * @return Plant growth stat
+ */
+ public byte getGrowth();
+
+ /**
+ * Set the crop's plant growth stat.
+ *
+ * @param growth Plant growth stat
+ */
+ public void setGrowth(byte growth);
+
+ /**
+ * Get the crop's plant gain stat.
+ * Higher values indicate more drops.
+ *
+ * @return Plant gain stat
+ */
+ public byte getGain();
+
+ /**
+ * Set the crop's plant gain stat.
+ *
+ * @param gain Plant gain stat
+ */
+ public void setGain(byte gain);
+
+ /**
+ * Get the crop's plant resistance stat.
+ * Higher values indicate more resistance against trampling.
+ *
+ * @return Plant resistance stat
+ */
+ public byte getResistance();
+
+ /**
+ * Set the crop's plant resistance stat.
+ *
+ * @param resistance Plant resistance stat
+ */
+ public void setResistance(byte resistance);
+
+ /**
+ * Get the crop's plant scan level.
+ * Increases every time the seed is analyzed.
+ *
+ * @return Plant scan level
+ */
+ public byte getScanLevel();
+
+ /**
+ * Set the crop's plant scan level.
+ *
+ * @param scanLevel Plant scan level
+ */
+ public void setScanLevel(byte scanLevel);
+
+ /**
+ * Get the crop's plant custom data, stored alongside the crop.
+ * Can be modified in place.
+ *
+ * @return Plant custom data
+ */
+ public NBTTagCompound getCustomData();
+
+ /**
+ * Get the crop's nutrient storage.
+ * Ranges from 0 to 100.
+ *
+ * @return Crop nutrient storage
+ */
+ public int getNutrientStorage();
+
+ /**
+ * Set the crop's nutrient storage.
+ *
+ * @param nutrientStorage Crop nutrient storage
+ */
+ public void setNutrientStorage(int nutrientStorage);
+
+ /**
+ * Get the crop's hydration storage.
+ * 0 indicates nothing, 1-10 indicate water hydration and 11-100 for hydration cells.
+ *
+ * @return Crop hydration storage
+ */
+ public int getHydrationStorage();
+
+ /**
+ * Set the crop's hydration storage.
+ *
+ * @param hydrationStorage Crop hydration storage
+ */
+ public void setHydrationStorage(int hydrationStorage);
+
+ /**
+ * Get the crop's Weed-Ex storage.
+ *
+ * @return Crop Weed-Ex storage
+ */
+ public int getWeedExStorage();
+
+ /**
+ * Set the crop's Weed-Ex storage.
+ *
+ * @param weedExStorage Crop Weed-Ex storage
+ */
+ public void setWeedExStorage(int weedExStorage);
+
+ /**
+ * Get the crop's humidity.
+ * Ranges from 0 (dry) to 10 (humid).
+ * Updates every couple of seconds or when an update is requested.
+ *
+ * @see #updateState()
+ *
+ * @return Crop humidity level
+ */
+ public byte getHumidity();
+
+ /**
+ * Get the crop's nutrient level.
+ * Ranges from 0 (empty) to 10 (full).
+ * Updates every couple of seconds or when an update is requested.
+ *
+ * @see #updateState()
+ *
+ * @return Crop nutrient level
+ */
+ public byte getNutrients();
+
+ /**
+ * Get the crop's air quality.
+ * Ranges from 0 (cluttered) to 10 (fresh).
+ * Updates every couple of seconds or when an update is requested.
+ *
+ * @see #updateState()
+ *
+ * @return Crop air quality
+ */
+ public byte getAirQuality();
+
+ /**
+ * Get the crop's world.
+ *
+ * @return Crop world
+ */
+ public World getWorld();
+
+ /**
+ * Get the crop's location.
+ *
+ * @return Crop location
+ */
+ public ChunkCoordinates getLocation();
+
+ /**
+ * Get the crop's light level.
+ *
+ * @return Crop light level
+ */
+ public int getLightLevel();
+
+ /**
+ * Pick the crop, removing and giving seeds for the plant.
+ *
+ * @param manual whether it was done by hand (not automated)
+ * @return true if successfully picked
+ */
+ public boolean pick(boolean manual);
+
+ /**
+ * Harvest the crop, turning it into gain and resetting its size.
+ * drop output on ground
+ * @param manual
+ * @return true if successfully harvested
+ */
+ public boolean harvest(boolean manual);
+
+ /**
+ * Harvest the crop, turning it into gain and resetting its size.
+ * drop output on ground
+ * @param Optimal force check getOptimalHavestSize() for harvest
+ * @return ItemStack[] of harvest output
+ */
+
+ ItemStack[] harvest_automated(boolean Optimal);
+
+ /**
+ * Fully clears the crop without dropping anything.
+ */
+ public void reset();
+
+ /**
+ * Request a texture and lighting update.
+ */
+ public void updateState();
+
+ /**
+ * Check if a block is under the farmland containing the crop.
+ * Searches up to 2 blocks below the farmland or an air space, whichever appears first.
+ *
+ * @param block block to search
+ * @return Whether the block was found
+ */
+ public boolean isBlockBelow(Block block);
+
+ /**
+ * Check if a block is under the farmland containing the crop.
+ * Searches up to 2 blocks below the farmland or an air space, whichever appears first.
+ *
+ * @param oreDictionaryName blocks to search
+ * @return Whether the blocks were found
+ */
+ public boolean isBlockBelow(String oreDictionaryName);
+
+ /**
+ * Generate plant seeds with the given parameters.
+ *
+ * @param crop plant
+ * @param growth plant growth stat
+ * @param gain plant gain stat
+ * @param resis plant resistance stat
+ * @param scan plant scan level
+ * @return Plant seed item
+ */
+ public ItemStack generateSeeds(CropCard crop, byte growth, byte gain, byte resis, byte scan);
+
+ /**
+ * @deprecated Use the CropCard version instead.
+ */
+ @Deprecated
+ public ItemStack generateSeeds(short plant, byte growth, byte gain, byte resis, byte scan);
+}
diff --git a/src/api/java/ic2/api/crops/package-info.java b/src/api/java/ic2/api/crops/package-info.java
new file mode 100644
index 0000000..04b8253
--- /dev/null
+++ b/src/api/java/ic2/api/crops/package-info.java
@@ -0,0 +1,4 @@
+@API(apiVersion="1.0", owner="IC2", provides="IC2API")
+package ic2.api.crops;
+import cpw.mods.fml.common.API;
+
diff --git a/src/api/java/ic2/api/energy/EnergyNet.java b/src/api/java/ic2/api/energy/EnergyNet.java
new file mode 100644
index 0000000..822bfc2
--- /dev/null
+++ b/src/api/java/ic2/api/energy/EnergyNet.java
@@ -0,0 +1,18 @@
+package ic2.api.energy;
+
+
+/**
+ * Provides access to the energy network.
+ *
+ * The old EnergyNet methods missing in IEnergyNet have been migrated to events (load, unload) or
+ * removed (tiles no longer emit energy actively, the energy net implementation requests it).
+ *
+ * See ic2/api/energy/usage.txt for an overall description of the energy net api.
+ */
+public final class EnergyNet {
+ /**
+ * Instance of the global EnergyNet class.
+ */
+ public static IEnergyNet instance;
+}
+
diff --git a/src/api/java/ic2/api/energy/IEnergyNet.java b/src/api/java/ic2/api/energy/IEnergyNet.java
new file mode 100644
index 0000000..8f656e5
--- /dev/null
+++ b/src/api/java/ic2/api/energy/IEnergyNet.java
@@ -0,0 +1,86 @@
+package ic2.api.energy;
+
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.World;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Interface representing the methods provided by the global EnergyNet class.
+ *
+ * See ic2/api/energy/usage.txt for an overall description of the energy net api.
+ */
+public interface IEnergyNet {
+ /**
+ * Get the EnergyNet-registered tile entity at the specified position.
+ *
+ * This is not the same as World.getTileEntity(), it's possible to register delegate tile
+ * entities with the energy net which are different from what's actually in the world. Those
+ * delegates allow to use separate TileEntity objects just for the EnergyNet interfaces,
+ * simplifying cross-mod dependencies and multi-blocks.
+ *
+ * @param world World containing the tile entity
+ * @param x x-coordinate
+ * @param y y-coordinate
+ * @param z z-coordinate
+ * @return tile entity registered to the energy net or null if none is registered
+ */
+ TileEntity getTileEntity(World world, int x, int y, int z);
+
+ /**
+ * Get the EnergyNet-registered neighbor tile entity at the specified position.
+ *
+ * @param te TileEntity indicating the world and position to search from
+ * @param dir direction the neighbor is to be found
+ * @return neighbor tile entity registered to the energy net or null if none is registered
+ */
+ TileEntity getNeighbor(TileEntity te, ForgeDirection dir);
+
+ /**
+ * determine how much energy has been emitted by the EnergyEmitter specified
+ *
+ * @note call this twice with x ticks delay to get the avg. emitted power p = (call2 - call1) / x EU/tick
+ *
+ * @param tileEntity energy emitter
+ * @deprecated Discontinued, use getNodeStats instead.
+ */
+ @Deprecated
+ double getTotalEnergyEmitted(TileEntity tileEntity);
+
+ /**
+ * determine how much energy has been sunken by the EnergySink specified
+ *
+ * @note call this twice with x ticks delay to get the avg. sunken power p = (call2 - call1) / x EU/tick
+ *
+ * @param tileEntity energy emitter
+ * @deprecated Discontinued, use getNodeStats instead.
+ */
+ @Deprecated
+ double getTotalEnergySunken(TileEntity tileEntity);
+
+ /**
+ * Retrieve statistics for the tile entity specified.
+ *
+ * The statistics apply to the last simulated tick.
+ *
+ * @param te Tile entity to check.
+ * @return Statistics for the tile entity.
+ */
+ NodeStats getNodeStats(TileEntity te);
+
+ /**
+ * Determine the typical power used by the specific tier, e.g. 128 eu/t for tier 2.
+ *
+ * @param tier tier
+ * @return power in eu/t
+ */
+ double getPowerFromTier(int tier);
+
+ /**
+ * Determine minimum tier required to handle the specified power, e.g. tier 2 for 128 eu/t.
+ *
+ * @param power in eu/t
+ * @return tier
+ */
+ int getTierFromPower(double power);
+}
diff --git a/src/api/java/ic2/api/energy/NodeStats.java b/src/api/java/ic2/api/energy/NodeStats.java
new file mode 100644
index 0000000..db33da7
--- /dev/null
+++ b/src/api/java/ic2/api/energy/NodeStats.java
@@ -0,0 +1,25 @@
+package ic2.api.energy;
+
+public class NodeStats {
+ public NodeStats(double energyIn, double energyOut, double voltage) {
+ this.energyIn = energyIn;
+ this.energyOut = energyOut;
+ this.voltage = voltage;
+ }
+
+ public double getEnergyIn() {
+ return energyIn;
+ }
+
+ public double getEnergyOut() {
+ return energyOut;
+ }
+
+ public double getVoltage() {
+ return voltage;
+ }
+
+ protected double energyIn;
+ protected double energyOut;
+ protected double voltage;
+}
diff --git a/src/api/java/ic2/api/energy/event/EnergyTileEvent.java b/src/api/java/ic2/api/energy/event/EnergyTileEvent.java
new file mode 100644
index 0000000..86b8dc2
--- /dev/null
+++ b/src/api/java/ic2/api/energy/event/EnergyTileEvent.java
@@ -0,0 +1,25 @@
+package ic2.api.energy.event;
+
+import net.minecraft.tileentity.TileEntity;
+
+import net.minecraftforge.event.world.WorldEvent;
+
+import ic2.api.energy.tile.IEnergyTile;
+
+/**
+ * Base class for energy net events, don't use it directly.
+ *
+ * See ic2/api/energy/usage.txt for an overall description of the energy net api.
+ */
+public class EnergyTileEvent extends WorldEvent {
+ public final IEnergyTile energyTile;
+
+ public EnergyTileEvent(IEnergyTile energyTile1) {
+ super(((TileEntity) energyTile1).getWorldObj());
+
+ if (world == null) throw new NullPointerException("world is null");
+
+ this.energyTile = energyTile1;
+ }
+}
+
diff --git a/src/api/java/ic2/api/energy/event/EnergyTileLoadEvent.java b/src/api/java/ic2/api/energy/event/EnergyTileLoadEvent.java
new file mode 100644
index 0000000..bf019ae
--- /dev/null
+++ b/src/api/java/ic2/api/energy/event/EnergyTileLoadEvent.java
@@ -0,0 +1,26 @@
+package ic2.api.energy.event;
+
+import ic2.api.energy.tile.IEnergyTile;
+
+/**
+ * Event announcing new energy tiles.
+ *
+ * This event notifies subscribers of loaded energy tiles, e.g. after getting
+ * loaded through the chunk they are in or after being placed down by the
+ * player or another deployer mechanism.
+ *
+ * Every energy tile which wants to get connected to the IC2 Energy Network has
+ * to either post this event or alternatively call EnergyNet.addTileEntity().
+ *
+ * You may use this event to build a static representation of energy tiles for
+ * your own energy grid implementation if you need to. It's not required if you
+ * always lookup energy paths on demand.
+ *
+ * See ic2/api/energy/usage.txt for an overall description of the energy net api.
+ */
+public class EnergyTileLoadEvent extends EnergyTileEvent {
+ public EnergyTileLoadEvent(IEnergyTile energyTile1) {
+ super(energyTile1);
+ }
+}
+
diff --git a/src/api/java/ic2/api/energy/event/EnergyTileUnloadEvent.java b/src/api/java/ic2/api/energy/event/EnergyTileUnloadEvent.java
new file mode 100644
index 0000000..2b22ae3
--- /dev/null
+++ b/src/api/java/ic2/api/energy/event/EnergyTileUnloadEvent.java
@@ -0,0 +1,27 @@
+package ic2.api.energy.event;
+
+import ic2.api.energy.tile.IEnergyTile;
+
+/**
+ * Event announcing terminated energy tiles.
+ *
+ * This event notifies subscribers of unloaded energy tiles, e.g. after getting
+ * unloaded through the chunk they are in or after being destroyed by the
+ * player or another block pick/destruction mechanism.
+ *
+ * Every energy tile which wants to get disconnected from the IC2 Energy
+ * Network has to either post this event or alternatively call
+ * EnergyNet.removeTileEntity().
+ *
+ * You may use this event to build a static representation of energy tiles for
+ * your own energy grid implementation if you need to. It's not required if you
+ * always lookup energy paths on demand.
+ *
+ * See ic2/api/energy/usage.txt for an overall description of the energy net api.
+ */
+public class EnergyTileUnloadEvent extends EnergyTileEvent {
+ public EnergyTileUnloadEvent(IEnergyTile energyTile1) {
+ super(energyTile1);
+ }
+}
+
diff --git a/src/api/java/ic2/api/energy/event/package-info.java b/src/api/java/ic2/api/energy/event/package-info.java
new file mode 100644
index 0000000..40db6f8
--- /dev/null
+++ b/src/api/java/ic2/api/energy/event/package-info.java
@@ -0,0 +1,4 @@
+@API(apiVersion="1.0", owner="IC2", provides="IC2API")
+package ic2.api.energy.event;
+import cpw.mods.fml.common.API;
+
diff --git a/src/api/java/ic2/api/energy/package-info.java b/src/api/java/ic2/api/energy/package-info.java
new file mode 100644
index 0000000..81d0bfe
--- /dev/null
+++ b/src/api/java/ic2/api/energy/package-info.java
@@ -0,0 +1,4 @@
+@API(apiVersion="1.0", owner="IC2", provides="IC2API")
+package ic2.api.energy;
+import cpw.mods.fml.common.API;
+
diff --git a/src/api/java/ic2/api/energy/prefab/BasicSink.java b/src/api/java/ic2/api/energy/prefab/BasicSink.java
new file mode 100644
index 0000000..48de061
--- /dev/null
+++ b/src/api/java/ic2/api/energy/prefab/BasicSink.java
@@ -0,0 +1,373 @@
+package ic2.api.energy.prefab;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+
+import cpw.mods.fml.common.FMLCommonHandler;
+
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import ic2.api.energy.event.EnergyTileLoadEvent;
+import ic2.api.energy.event.EnergyTileUnloadEvent;
+import ic2.api.energy.tile.IEnergySink;
+import ic2.api.info.Info;
+import ic2.api.item.ElectricItem;
+
+/**
+ * BasicSink is a simple adapter to provide an ic2 energy sink.
+ *
+ * It's designed to be attached to a tile entity as a delegate. Functionally BasicSink acts as a
+ * one-time configurable input energy buffer, thus providing a common use case for machines.
+ *
+ * Sub-classing BasicSink instead of using it as a delegate works as well, but isn't recommended.
+ * The delegate can be extended with additional functionality through a sub class though.
+ *
+ * The constraints set by BasicSink like the strict tank-like energy buffering should provide a
+ * more easy to use and stable interface than using IEnergySink directly while aiming for
+ * optimal performance.
+ *
+ * Using BasicSink involves the following steps:
+ * - create a BasicSink instance in your TileEntity, typically in a field
+ * - forward invalidate, onChunkUnload, readFromNBT, writeToNBT and updateEntity to the BasicSink
+ * instance.
+ * If you have other means of determining when the tile entity is fully loaded, notify onLoaded
+ * that way instead of forwarding updateEntity.
+ * - call useEnergy whenever appropriate. canUseEnergy determines if enough energy is available
+ * without consuming the energy.
+ * - optionally use getEnergyStored to display the output buffer charge level
+ * - optionally use setEnergyStored to sync the stored energy to the client (e.g. in the Container)
+ *
+ * Example implementation code:
+ * @code{.java}
+ * public class SomeTileEntity extends TileEntity {
+ * // new basic energy sink, 1000 EU buffer, tier 1 (32 EU/t, LV)
+ * private BasicSink ic2EnergySink = new BasicSink(this, 1000, 1);
+ *
+ * @Override
+ * public void invalidate() {
+ * ic2EnergySink.invalidate(); // notify the energy sink
+ * ...
+ * super.invalidate(); // this is important for mc!
+ * }
+ *
+ * @Override
+ * public void onChunkUnload() {
+ * ic2EnergySink.onChunkUnload(); // notify the energy sink
+ * ...
+ * }
+ *
+ * @Override
+ * public void readFromNBT(NBTTagCompound tag) {
+ * super.readFromNBT(tag);
+ *
+ * ic2EnergySink.readFromNBT(tag);
+ * ...
+ * }
+ *
+ * @Override
+ * public void writeToNBT(NBTTagCompound tag) {
+ * super.writeToNBT(tag);
+ *
+ * ic2EnergySink.writeToNBT(tag);
+ * ...
+ * }
+ *
+ * @Override
+ * public void updateEntity() {
+ * ic2EnergySink.updateEntity(); // notify the energy sink
+ * ...
+ * if (ic2EnergySink.useEnergy(5)) { // use 5 eu from the sink's buffer this tick
+ * ... // do something with the energy
+ * }
+ * ...
+ * }
+ *
+ * ...
+ * }
+ * @endcode
+ */
+public class BasicSink extends TileEntity implements IEnergySink {
+ // **********************************
+ // *** Methods for use by the mod ***
+ // **********************************
+
+ /**
+ * Constructor for a new BasicSink delegate.
+ *
+ * @param parent1 TileEntity represented by this energy sink.
+ * @param capacity1 Maximum amount of eu to store.
+ * @param tier1 IC2 tier, 1 = LV, 2 = MV, ...
+ */
+ public BasicSink(TileEntity parent1, int capacity1, int tier1) {
+ this.parent = parent1;
+ this.capacity = capacity1;
+ this.tier = tier1;
+ }
+
+ // in-world te forwards >>
+
+ /**
+ * Forward for the base TileEntity's updateEntity(), used for creating the energy net link.
+ * Either updateEntity or onLoaded have to be used.
+ */
+ @Override
+ public void updateEntity() {
+ if (!addedToEnet) onLoaded();
+ }
+
+ /**
+ * Notification that the base TileEntity finished loaded, for advanced uses.
+ * Either updateEntity or onLoaded have to be used.
+ */
+ public void onLoaded() {
+ if (!addedToEnet &&
+ !FMLCommonHandler.instance().getEffectiveSide().isClient() &&
+ Info.isIc2Available()) {
+ worldObj = parent.getWorldObj();
+ xCoord = parent.xCoord;
+ yCoord = parent.yCoord;
+ zCoord = parent.zCoord;
+
+ MinecraftForge.EVENT_BUS.post(new EnergyTileLoadEvent(this));
+
+ addedToEnet = true;
+ }
+ }
+
+ /**
+ * Forward for the base TileEntity's invalidate(), used for destroying the energy net link.
+ * Both invalidate and onChunkUnload have to be used.
+ */
+ @Override
+ public void invalidate() {
+ super.invalidate();
+
+ onChunkUnload();
+ }
+
+ /**
+ * Forward for the base TileEntity's onChunkUnload(), used for destroying the energy net link.
+ * Both invalidate and onChunkUnload have to be used.
+ */
+ @Override
+ public void onChunkUnload() {
+ if (addedToEnet &&
+ Info.isIc2Available()) {
+ MinecraftForge.EVENT_BUS.post(new EnergyTileUnloadEvent(this));
+
+ addedToEnet = false;
+ }
+ }
+
+ /**
+ * Forward for the base TileEntity's readFromNBT(), used for loading the state.
+ *
+ * @param tag Compound tag as supplied by TileEntity.readFromNBT()
+ */
+ @Override
+ public void readFromNBT(NBTTagCompound tag) {
+ super.readFromNBT(tag);
+
+ NBTTagCompound data = tag.getCompoundTag("IC2BasicSink");
+
+ energyStored = data.getDouble("energy");
+ }
+
+ /**
+ * Forward for the base TileEntity's writeToNBT(), used for saving the state.
+ *
+ * @param tag Compound tag as supplied by TileEntity.writeToNBT()
+ */
+ @Override
+ public void writeToNBT(NBTTagCompound tag) {
+ try {
+ super.writeToNBT(tag);
+ } catch (RuntimeException e) {
+ // happens if this is a delegate, ignore
+ }
+
+ NBTTagCompound data = new NBTTagCompound();
+
+ data.setDouble("energy", energyStored);
+
+ tag.setTag("IC2BasicSink", data);
+ }
+
+ // << in-world te forwards
+ // methods for using this adapter >>
+
+ /**
+ * Get the maximum amount of energy this sink can hold in its buffer.
+ *
+ * @return Capacity in EU.
+ */
+ public int getCapacity() {
+ return capacity;
+ }
+
+ /**
+ * Set the maximum amount of energy this sink can hold in its buffer.
+ *
+ * @param capacity1 Capacity in EU.
+ */
+ public void setCapacity(int capacity1) {
+ this.capacity = capacity1;
+ }
+
+ /**
+ * Get the IC2 energy tier for this sink.
+ *
+ * @return IC2 Tier.
+ */
+ public int getTier() {
+ return tier;
+ }
+
+ /**
+ * Set the IC2 energy tier for this sink.
+ *
+ * @param tier1 IC2 Tier.
+ */
+ public void setTier(int tier1) {
+ this.tier = tier1;
+ }
+
+ /**
+ * Determine the energy stored in the sink's input buffer.
+ *
+ * @return amount in EU, may be above capacity
+ */
+ public double getEnergyStored() {
+ return energyStored;
+ }
+
+ /**
+ * Set the stored energy to the specified amount.
+ *
+ * This is intended for server -> client synchronization, e.g. to display the stored energy in
+ * a GUI through getEnergyStored().
+ *
+ * @param amount
+ */
+ public void setEnergyStored(double amount) {
+ energyStored = amount;
+ }
+
+ /**
+ * Determine if the specified amount of energy is available.
+ *
+ * @param amount in EU
+ * @return true if the amount is available
+ */
+ public boolean canUseEnergy(double amount) {
+ return energyStored >= amount;
+ }
+
+ /**
+ * Use the specified amount of energy, if available.
+ *
+ * @param amount amount to use
+ * @return true if the amount was available
+ */
+ public boolean useEnergy(double amount) {
+ if (canUseEnergy(amount) && !FMLCommonHandler.instance().getEffectiveSide().isClient()) {
+ energyStored -= amount;
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Discharge the supplied ItemStack into this sink's energy buffer.
+ *
+ * @param stack ItemStack to discharge (null is ignored)
+ * @param limit Transfer limit, values <= 0 will use the battery's limit
+ * @return true if energy was transferred
+ */
+ public boolean discharge(ItemStack stack, int limit) {
+ if (stack == null || !Info.isIc2Available()) return false;
+
+ double amount = capacity - energyStored;
+ if (amount <= 0) return false;
+
+ if (limit > 0 && limit < amount) amount = limit;
+
+ amount = ElectricItem.manager.discharge(stack, amount, tier, limit > 0, true, false);
+
+ energyStored += amount;
+
+ return amount > 0;
+ }
+
+ // << methods for using this adapter
+
+ // backwards compatibility (ignore these) >>
+
+ @Deprecated
+ public void onUpdateEntity() {
+ updateEntity();
+ }
+
+ @Deprecated
+ public void onInvalidate() {
+ invalidate();
+ }
+
+ @Deprecated
+ public void onOnChunkUnload() {
+ onChunkUnload();
+ }
+
+ @Deprecated
+ public void onReadFromNbt(NBTTagCompound tag) {
+ readFromNBT(tag);
+ }
+
+ @Deprecated
+ public void onWriteToNbt(NBTTagCompound tag) {
+ writeToNBT(tag);
+ }
+
+ // << backwards compatibility
+
+ // ******************************
+ // *** Methods for use by ic2 ***
+ // ******************************
+
+ // energy net interface >>
+
+ @Override
+ public boolean acceptsEnergyFrom(TileEntity emitter, ForgeDirection direction) {
+ return true;
+ }
+
+ @Override
+ public double getDemandedEnergy() {
+ return Math.max(0, capacity - energyStored);
+ }
+
+ @Override
+ public double injectEnergy(ForgeDirection directionFrom, double amount, double voltage) {
+ energyStored += amount;
+
+ return 0;
+ }
+
+ @Override
+ public int getSinkTier() {
+ return tier;
+ }
+
+ // << energy net interface
+
+
+ public final TileEntity parent;
+
+ protected int capacity;
+ protected int tier;
+ protected double energyStored;
+ protected boolean addedToEnet;
+}
diff --git a/src/api/java/ic2/api/energy/prefab/BasicSource.java b/src/api/java/ic2/api/energy/prefab/BasicSource.java
new file mode 100644
index 0000000..011e8cd
--- /dev/null
+++ b/src/api/java/ic2/api/energy/prefab/BasicSource.java
@@ -0,0 +1,375 @@
+package ic2.api.energy.prefab;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+
+import cpw.mods.fml.common.FMLCommonHandler;
+
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import ic2.api.energy.EnergyNet;
+import ic2.api.energy.event.EnergyTileLoadEvent;
+import ic2.api.energy.event.EnergyTileUnloadEvent;
+import ic2.api.energy.tile.IEnergySource;
+import ic2.api.info.Info;
+import ic2.api.item.ElectricItem;
+
+/**
+ * BasicSource is a simple adapter to provide an ic2 energy source.
+ *
+ * It's designed to be attached to a tile entity as a delegate. Functionally BasicSource acts as a
+ * one-time configurable output energy buffer, thus providing a common use case for generators.
+ *
+ * Sub-classing BasicSource instead of using it as a delegate works as well, but isn't recommended.
+ * The delegate can be extended with additional functionality through a sub class though.
+ *
+ * The constraints set by BasicSource like the strict tank-like energy buffering should provide a
+ * more easy to use and stable interface than using IEnergySource directly while aiming for
+ * optimal performance.
+ *
+ * Using BasicSource involves the following steps:
+ * - create a BasicSource instance in your TileEntity, typically in a field
+ * - forward invalidate, onChunkUnload, readFromNBT, writeToNBT and updateEntity to the BasicSource
+ * instance.
+ * If you have other means of determining when the tile entity is fully loaded, notify onLoaded
+ * that way instead of forwarding updateEntity.
+ * - call addEnergy whenever appropriate, using getFreeCapacity may determine if e.g. the generator
+ * should run
+ * - optionally use getEnergyStored to display the output buffer charge level
+ * - optionally use setEnergyStored to sync the stored energy to the client (e.g. in the Container)
+ *
+ * Example implementation code:
+ * @code{.java}
+ * public class SomeTileEntity extends TileEntity {
+ * // new basic energy source, 1000 EU buffer, tier 1 (32 EU/t, LV)
+ * private BasicSource ic2EnergySource = new BasicSource(this, 1000, 1);
+ *
+ * @Override
+ * public void invalidate() {
+ * ic2EnergySource.invalidate(); // notify the energy source
+ * ...
+ * super.invalidate(); // this is important for mc!
+ * }
+ *
+ * @Override
+ * public void onChunkUnload() {
+ * ic2EnergySource.onChunkUnload(); // notify the energy source
+ * ...
+ * }
+ *
+ * @Override
+ * public void readFromNBT(NBTTagCompound tag) {
+ * super.readFromNBT(tag);
+ *
+ * ic2EnergySource.readFromNBT(tag);
+ * ...
+ * }
+ *
+ * @Override
+ * public void writeToNBT(NBTTagCompound tag) {
+ * super.writeToNBT(tag);
+ *
+ * ic2EnergySource.writeToNBT(tag);
+ * ...
+ * }
+ *
+ * @Override
+ * public void updateEntity() {
+ * ic2EnergySource.updateEntity(); // notify the energy source
+ * ...
+ * ic2EnergySource.addEnergy(5); // add 5 eu to the source's buffer this tick
+ * ...
+ * }
+ *
+ * ...
+ * }
+ * @endcode
+ */
+public class BasicSource extends TileEntity implements IEnergySource {
+ // **********************************
+ // *** Methods for use by the mod ***
+ // **********************************
+
+ /**
+ * Constructor for a new BasicSource delegate.
+ *
+ * @param parent1 Base TileEntity represented by this energy source.
+ * @param capacity1 Maximum amount of eu to store.
+ * @param tier1 IC2 tier, 1 = LV, 2 = MV, ...
+ */
+ public BasicSource(TileEntity parent1, double capacity1, int tier1) {
+ double power = EnergyNet.instance.getPowerFromTier(tier1);
+
+ this.parent = parent1;
+ this.capacity = capacity1 < power ? power : capacity1;
+ this.tier = tier1;
+ this.power = power;
+ }
+
+ // in-world te forwards >>
+
+ /**
+ * Forward for the base TileEntity's updateEntity(), used for creating the energy net link.
+ * Either updateEntity or onLoaded have to be used.
+ */
+ @Override
+ public void updateEntity() {
+ if (!addedToEnet) onLoaded();
+ }
+
+ /**
+ * Notification that the base TileEntity finished loading, for advanced uses.
+ * Either updateEntity or onLoaded have to be used.
+ */
+ public void onLoaded() {
+ if (!addedToEnet &&
+ !FMLCommonHandler.instance().getEffectiveSide().isClient() &&
+ Info.isIc2Available()) {
+ worldObj = parent.getWorldObj();
+ xCoord = parent.xCoord;
+ yCoord = parent.yCoord;
+ zCoord = parent.zCoord;
+
+ MinecraftForge.EVENT_BUS.post(new EnergyTileLoadEvent(this));
+
+ addedToEnet = true;
+ }
+ }
+
+ /**
+ * Forward for the base TileEntity's invalidate(), used for destroying the energy net link.
+ * Both invalidate and onChunkUnload have to be used.
+ */
+ @Override
+ public void invalidate() {
+ super.invalidate();
+
+ onChunkUnload();
+ }
+
+ /**
+ * Forward for the base TileEntity's onChunkUnload(), used for destroying the energy net link.
+ * Both invalidate and onChunkUnload have to be used.
+ */
+ @Override
+ public void onChunkUnload() {
+ if (addedToEnet &&
+ Info.isIc2Available()) {
+ MinecraftForge.EVENT_BUS.post(new EnergyTileUnloadEvent(this));
+
+ addedToEnet = false;
+ }
+ }
+
+ /**
+ * Forward for the base TileEntity's readFromNBT(), used for loading the state.
+ *
+ * @param tag Compound tag as supplied by TileEntity.readFromNBT()
+ */
+ @Override
+ public void readFromNBT(NBTTagCompound tag) {
+ super.readFromNBT(tag);
+
+ NBTTagCompound data = tag.getCompoundTag("IC2BasicSource");
+
+ energyStored = data.getDouble("energy");
+ }
+
+ /**
+ * Forward for the base TileEntity's writeToNBT(), used for saving the state.
+ *
+ * @param tag Compound tag as supplied by TileEntity.writeToNBT()
+ */
+ @Override
+ public void writeToNBT(NBTTagCompound tag) {
+ try {
+ super.writeToNBT(tag);
+ } catch (RuntimeException e) {
+ // happens if this is a delegate, ignore
+ }
+
+ NBTTagCompound data = new NBTTagCompound();
+
+ data.setDouble("energy", energyStored);
+
+ tag.setTag("IC2BasicSource", data);
+ }
+
+ // << in-world te forwards
+ // methods for using this adapter >>
+
+ /**
+ * Get the maximum amount of energy this source can hold in its buffer.
+ *
+ * @return Capacity in EU.
+ */
+ public double getCapacity() {
+ return capacity;
+ }
+
+ /**
+ * Set the maximum amount of energy this source can hold in its buffer.
+ *
+ * @param capacity1 Capacity in EU.
+ */
+ public void setCapacity(double capacity1) {
+ if (capacity1 < power) capacity1 = power;
+
+ this.capacity = capacity1;
+ }
+
+ /**
+ * Get the IC2 energy tier for this source.
+ *
+ * @return IC2 Tier.
+ */
+ public int getTier() {
+ return tier;
+ }
+
+ /**
+ * Set the IC2 energy tier for this source.
+ *
+ * @param tier1 IC2 Tier.
+ */
+ public void setTier(int tier1) {
+ double power = EnergyNet.instance.getPowerFromTier(tier1);
+
+ if (capacity < power) capacity = power;
+
+ this.tier = tier1;
+ this.power = power;
+ }
+
+
+ /**
+ * Determine the energy stored in the source's output buffer.
+ *
+ * @return amount in EU
+ */
+ public double getEnergyStored() {
+ return energyStored;
+ }
+
+ /**
+ * Set the stored energy to the specified amount.
+ *
+ * This is intended for server -> client synchronization, e.g. to display the stored energy in
+ * a GUI through getEnergyStored().
+ *
+ * @param amount
+ */
+ public void setEnergyStored(double amount) {
+ energyStored = amount;
+ }
+
+ /**
+ * Determine the free capacity in the source's output buffer.
+ *
+ * @return amount in EU
+ */
+ public double getFreeCapacity() {
+ return capacity - energyStored;
+ }
+
+ /**
+ * Add some energy to the output buffer.
+ *
+ * @param amount maximum amount of energy to add
+ * @return amount added/used, NOT remaining
+ */
+ public double addEnergy(double amount) {
+ if (FMLCommonHandler.instance().getEffectiveSide().isClient()) return 0;
+ if (amount > capacity - energyStored) amount = capacity - energyStored;
+
+ energyStored += amount;
+
+ return amount;
+ }
+
+ /**
+ * Charge the supplied ItemStack from this source's energy buffer.
+ *
+ * @param stack ItemStack to charge (null is ignored)
+ * @return true if energy was transferred
+ */
+ public boolean charge(ItemStack stack) {
+ if (stack == null || !Info.isIc2Available()) return false;
+
+ double amount = ElectricItem.manager.charge(stack, energyStored, tier, false, false);
+
+ energyStored -= amount;
+
+ return amount > 0;
+ }
+
+ // << methods for using this adapter
+
+ // backwards compatibility (ignore these) >>
+
+ @Deprecated
+ public void onUpdateEntity() {
+ updateEntity();
+ }
+
+ @Deprecated
+ public void onInvalidate() {
+ invalidate();
+ }
+
+ @Deprecated
+ public void onOnChunkUnload() {
+ onChunkUnload();
+ }
+
+ @Deprecated
+ public void onReadFromNbt(NBTTagCompound tag) {
+ readFromNBT(tag);
+ }
+
+ @Deprecated
+ public void onWriteToNbt(NBTTagCompound tag) {
+ writeToNBT(tag);
+ }
+
+ // << backwards compatibility
+
+ // ******************************
+ // *** Methods for use by ic2 ***
+ // ******************************
+
+ // energy net interface >>
+
+ @Override
+ public boolean emitsEnergyTo(TileEntity receiver, ForgeDirection direction) {
+ return true;
+ }
+
+ @Override
+ public double getOfferedEnergy() {
+ return Math.min(energyStored, power);
+ }
+
+ @Override
+ public void drawEnergy(double amount) {
+ energyStored -= amount;
+ }
+
+ @Override
+ public int getSourceTier() {
+ return tier;
+ }
+
+ // << energy net interface
+
+
+ public final TileEntity parent;
+
+ protected double capacity;
+ protected int tier;
+ protected double power;
+ protected double energyStored;
+ protected boolean addedToEnet;
+}
diff --git a/src/api/java/ic2/api/energy/prefab/package-info.java b/src/api/java/ic2/api/energy/prefab/package-info.java
new file mode 100644
index 0000000..c38f7e0
--- /dev/null
+++ b/src/api/java/ic2/api/energy/prefab/package-info.java
@@ -0,0 +1,4 @@
+@API(apiVersion="1.0", owner="IC2", provides="IC2API")
+package ic2.api.energy.prefab;
+import cpw.mods.fml.common.API;
+
diff --git a/src/api/java/ic2/api/energy/tile/IEnergyAcceptor.java b/src/api/java/ic2/api/energy/tile/IEnergyAcceptor.java
new file mode 100644
index 0000000..1735ff5
--- /dev/null
+++ b/src/api/java/ic2/api/energy/tile/IEnergyAcceptor.java
@@ -0,0 +1,27 @@
+package ic2.api.energy.tile;
+
+import net.minecraft.tileentity.TileEntity;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * For internal/multi-block usage only.
+ *
+ * @see IEnergySink
+ * @see IEnergyConductor
+ *
+ * See ic2/api/energy/usage.txt for an overall description of the energy net api.
+ */
+public interface IEnergyAcceptor extends IEnergyTile {
+ /**
+ * Determine if this acceptor can accept current from an adjacent emitter in a direction.
+ *
+ * The TileEntity in the emitter parameter is what was originally added to the energy net,
+ * which may be normal in-world TileEntity, a delegate or an IMetaDelegate.
+ *
+ * @param emitter energy emitter, may also be null or an IMetaDelegate
+ * @param direction direction the energy is being received from
+ */
+ boolean acceptsEnergyFrom(TileEntity emitter, ForgeDirection direction);
+}
+
diff --git a/src/api/java/ic2/api/energy/tile/IEnergyConductor.java b/src/api/java/ic2/api/energy/tile/IEnergyConductor.java
new file mode 100644
index 0000000..08dab88
--- /dev/null
+++ b/src/api/java/ic2/api/energy/tile/IEnergyConductor.java
@@ -0,0 +1,53 @@
+package ic2.api.energy.tile;
+
+/**
+ * Tile entities which conduct energy pulses without buffering (mostly cables) have to implement this
+ * interface.
+ *
+ * See ic2/api/energy/usage.txt for an overall description of the energy net api.
+ */
+public interface IEnergyConductor extends IEnergyAcceptor, IEnergyEmitter {
+ /**
+ * Energy loss for the conductor in EU per block.
+ *
+ * @return Energy loss
+ */
+ double getConductionLoss();
+
+ /**
+ * Amount of energy the insulation will handle before shocking nearby players and mobs.
+ *
+ * @return Insulation energy absorption in EU
+ */
+ double getInsulationEnergyAbsorption();
+
+ /**
+ * Amount of energy the insulation will handle before it is destroyed.
+ * Ensure that this value is greater than the insulation energy absorption + 64.
+ *
+ * @return Insulation-destroying energy in EU
+ */
+ double getInsulationBreakdownEnergy();
+
+ /**
+ * Amount of energy the conductor will handle before it melts.
+ *
+ * @return Conductor-destroying energy in EU
+ */
+ double getConductorBreakdownEnergy();
+
+ /**
+ * Remove the conductor's insulation if the insulation breakdown energy was exceeded.
+ *
+ * @see #getInsulationBreakdownEnergy()
+ */
+ void removeInsulation();
+
+ /**
+ * Remove the conductor if the conductor breakdown energy was exceeded.
+ *
+ * @see #getConductorBreakdownEnergy()
+ */
+ void removeConductor();
+}
+
diff --git a/src/api/java/ic2/api/energy/tile/IEnergyEmitter.java b/src/api/java/ic2/api/energy/tile/IEnergyEmitter.java
new file mode 100644
index 0000000..013865e
--- /dev/null
+++ b/src/api/java/ic2/api/energy/tile/IEnergyEmitter.java
@@ -0,0 +1,28 @@
+package ic2.api.energy.tile;
+
+import net.minecraft.tileentity.TileEntity;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * For internal/multi-block usage only.
+ *
+ * @see IEnergySource
+ * @see IEnergyConductor
+ *
+ * See ic2/api/energy/usage.txt for an overall description of the energy net api.
+ */
+public interface IEnergyEmitter extends IEnergyTile {
+ /**
+ * Determine if this emitter can emit energy to an adjacent receiver.
+ *
+ * The TileEntity in the receiver parameter is what was originally added to the energy net,
+ * which may be normal in-world TileEntity, a delegate or an IMetaDelegate.
+ *
+ * @param receiver receiver, may also be null or an IMetaDelegate
+ * @param direction direction the receiver is from the emitter
+ * @return Whether energy should be emitted
+ */
+ boolean emitsEnergyTo(TileEntity receiver, ForgeDirection direction);
+}
+
diff --git a/src/api/java/ic2/api/energy/tile/IEnergySink.java b/src/api/java/ic2/api/energy/tile/IEnergySink.java
new file mode 100644
index 0000000..4ae8e79
--- /dev/null
+++ b/src/api/java/ic2/api/energy/tile/IEnergySink.java
@@ -0,0 +1,45 @@
+package ic2.api.energy.tile;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Allows a tile entity (mostly a machine) to receive energy.
+ *
+ * See ic2/api/energy/usage.txt for an overall description of the energy net api.
+ */
+public interface IEnergySink extends IEnergyAcceptor {
+ /**
+ * Determine how much energy the sink accepts.
+ *
+ * Make sure that injectEnergy() does accepts energy if demandsEnergy() returns anything > 0.
+ *
+ * @note Modifying the energy net from this method is disallowed.
+ *
+ * @return max accepted input in eu
+ */
+ double getDemandedEnergy();
+
+ /**
+ * Determine the tier of this energy sink.
+ * 1 = LV, 2 = MV, 3 = HV, 4 = EV etc.
+ *
+ * @note Modifying the energy net from this method is disallowed.
+ * @note Return Integer.MAX_VALUE to allow any voltage.
+ *
+ * @return tier of this energy sink
+ */
+ int getSinkTier();
+
+ /**
+ * Transfer energy to the sink.
+ *
+ * It's highly recommended to accept all energy by letting the internal buffer overflow to
+ * increase the performance and accuracy of the distribution simulation.
+ *
+ * @param directionFrom direction from which the energy comes from
+ * @param amount energy to be transferred
+ * @return Energy not consumed (leftover)
+ */
+ double injectEnergy(ForgeDirection directionFrom, double amount, double voltage);
+}
+
diff --git a/src/api/java/ic2/api/energy/tile/IEnergySource.java b/src/api/java/ic2/api/energy/tile/IEnergySource.java
new file mode 100644
index 0000000..44b952a
--- /dev/null
+++ b/src/api/java/ic2/api/energy/tile/IEnergySource.java
@@ -0,0 +1,38 @@
+package ic2.api.energy.tile;
+
+/**
+ * Allows a tile entity (mostly a generator) to emit energy.
+ *
+ * See ic2/api/energy/usage.txt for an overall description of the energy net api.
+ */
+public interface IEnergySource extends IEnergyEmitter {
+ /**
+ * Energy output provided by the source this tick.
+ * This is typically Math.min(stored energy, max output/tick).
+ *
+ * @note Modifying the energy net from this method is disallowed.
+ *
+ * @return Energy offered this tick
+ */
+ double getOfferedEnergy();
+
+ /**
+ * Draw energy from this source's buffer.
+ *
+ * If the source doesn't have a buffer, this is a no-op.
+ *
+ * @param amount amount of EU to draw, may be negative
+ */
+ void drawEnergy(double amount);
+
+ /**
+ * Determine the tier of this energy source.
+ * 1 = LV, 2 = MV, 3 = HV, 4 = EV etc.
+ *
+ * @note Modifying the energy net from this method is disallowed.
+ *
+ * @return tier of this energy source
+ */
+ int getSourceTier();
+}
+
diff --git a/src/api/java/ic2/api/energy/tile/IEnergyTile.java b/src/api/java/ic2/api/energy/tile/IEnergyTile.java
new file mode 100644
index 0000000..3938190
--- /dev/null
+++ b/src/api/java/ic2/api/energy/tile/IEnergyTile.java
@@ -0,0 +1,15 @@
+package ic2.api.energy.tile;
+
+/**
+ * For internal usage only, base class for all energy tiles.
+ *
+ * @see IEnergySink
+ * @see IEnergySource
+ * @see IEnergyConductor
+ *
+ * See ic2/api/energy/usage.txt for an overall description of the energy net api.
+ */
+public interface IEnergyTile {
+ //
+}
+
diff --git a/src/api/java/ic2/api/energy/tile/IHeatSource.java b/src/api/java/ic2/api/energy/tile/IHeatSource.java
new file mode 100644
index 0000000..2e5db41
--- /dev/null
+++ b/src/api/java/ic2/api/energy/tile/IHeatSource.java
@@ -0,0 +1,23 @@
+package ic2.api.energy.tile;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+public interface IHeatSource {
+
+ /*
+ * Return max heat transmission peer Tick (only theoretical bandwidth not available amount)
+ */
+ int maxrequestHeatTick(ForgeDirection directionFrom);
+
+ /*
+ * @param requested amount of heat to transfer
+ *
+ * @return transmitted amount of heat
+ *
+ * example: You Request 100 units of heat but the Source have only 50 units left
+ *
+ * requestHeat(100) : return 50 : so 50 units of heat remove from HeatSource
+ */
+
+ int requestHeat(ForgeDirection directionFrom, int requestheat);
+}
diff --git a/src/api/java/ic2/api/energy/tile/IKineticSource.java b/src/api/java/ic2/api/energy/tile/IKineticSource.java
new file mode 100644
index 0000000..a651408
--- /dev/null
+++ b/src/api/java/ic2/api/energy/tile/IKineticSource.java
@@ -0,0 +1,22 @@
+package ic2.api.energy.tile;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+public interface IKineticSource {
+ /*
+ * Return max kinetic energy transmission peer Tick (only theoretical bandwidth not available amount)
+ */
+ int maxrequestkineticenergyTick(ForgeDirection directionFrom);
+
+ /*
+ * @param requested amount of kinetic energy to transfer
+ *
+ * @return transmitted amount of kineticenergy
+ *
+ * example: You Request 100 units of kinetic energy but the Source have only 50 units left
+ *
+ * requestkineticenergy(100) : return 50 : so 50 units of kinetic energy remove from KineticSource
+ */
+
+ int requestkineticenergy(ForgeDirection directionFrom, int requestkineticenergy);
+}
diff --git a/src/api/java/ic2/api/energy/tile/IMetaDelegate.java b/src/api/java/ic2/api/energy/tile/IMetaDelegate.java
new file mode 100644
index 0000000..f7319d0
--- /dev/null
+++ b/src/api/java/ic2/api/energy/tile/IMetaDelegate.java
@@ -0,0 +1,36 @@
+package ic2.api.energy.tile;
+
+import java.util.List;
+
+import net.minecraft.tileentity.TileEntity;
+
+/**
+ * Interface for grouping multi-block structures to a single energy net delegate.
+ *
+ * The energy net uses TileEntity to refer to a specific xyz+world position. If multiple of those
+ * positions should belong to the same functional structure, i.e. they consume or produce energy
+ * only once for the whole multi-block instead of once per every single block, this interface
+ * allows to do so.
+ *
+ * The tile entity implementing IMetaDelegate has to be added/removed to/from the energy net
+ * instead of every single sub-TileEntity. The energy net interaction will be handled by the
+ * IMetaDelegate TileEntity as well.
+ *
+ * The sub tile array TileEntity[] just provides optional connectivity (IEnergyAcceptor,
+ * IEnergyEmitter) and mandatory position (x, y, z, World) data.
+ * If the connectivity data on the sub tile is missing, the meta delegate is queried instead.
+ *
+ * See ic2/api/energy/usage.txt for an overall description of the energy net api.
+ */
+public interface IMetaDelegate extends IEnergyTile {
+ /**
+ * Get the sub-TileEntities belonging to this Meta TileEntity.
+ *
+ * @note The list has to be consistent between the EnergyNet Load and Unload events.
+ * @note The list must have at least 1 element.
+ * @note The parent te implementing IMetaDelegate isn't automatically included.
+ *
+ * @return sub-TileEntity array
+ */
+ List<TileEntity> getSubTiles();
+}
diff --git a/src/api/java/ic2/api/energy/tile/package-info.java b/src/api/java/ic2/api/energy/tile/package-info.java
new file mode 100644
index 0000000..3c60323
--- /dev/null
+++ b/src/api/java/ic2/api/energy/tile/package-info.java
@@ -0,0 +1,4 @@
+@API(apiVersion="1.0", owner="IC2", provides="IC2API")
+package ic2.api.energy.tile;
+import cpw.mods.fml.common.API;
+
diff --git a/src/api/java/ic2/api/energy/usage.txt b/src/api/java/ic2/api/energy/usage.txt
new file mode 100644
index 0000000..49bd995
--- /dev/null
+++ b/src/api/java/ic2/api/energy/usage.txt
@@ -0,0 +1,144 @@
+--------------------------------
+-- Energy Network Description --
+--------------------------------
+
+There are currently three different types of energy network blocks:
+- energy sources, e.g. generators or the output side of a storage block/transformer
+- energy sinks, e.g. machines or the input side of a storage block/transformer
+- conductors, e.g. cables
+
+Note that storage blocks or transformers are both sources and sinks.
+
+All those blocks have to have a tile entity which has to implement the interface corresponding to
+its function and also post events to the Forge event bus.
+
+The energy generation, distribution and consumption is strictly limited to the simulating (server)
+side, use the proper side checks before posting the related events. One possibility is to check for
+FMLCommonHandler.instance().getEffectiveSide().isClient() being false.
+
+The energy network works by calculating the energy flow between the sources which offer energy
+through getOfferedEnergy() and the sinks which request energy through demandedEnergyUnits().
+Conductors will carry the energy over a distance. Once the energy distribution is calculated, the
+energy net will update the sources and sinks through drawEnergy() and injectEnergyUnits() respectively.
+
+
+---------------------------
+-- Energy Network Events --
+---------------------------
+
+The energy network currently requires 2 events to manage its internal representation of the grids:
+
+-- EnergyTileLoadEvent --
+
+For all energy network tiles (sources, sinks, conductors) you have to post an EnergyTileLoadEvent.
+
+The event has to be posted as soon as the implementing tile entity is fully loaded, usually after
+loading the chunk which contains it or after the user placing the block.
+
+The energy net implementation will use the event to add it to its energy grid map, taking it into
+account for further energy transfers.
+
+You can detect the loading by either using the 1st iteration of updateEntity() or by waiting for
+the next world tick after TileEntity.validate(). The 2nd approach is obviously more sophisticated
+and requires to use some tick queuing mechanism.
+
+The event can by posted as following:
+ MinecraftForge.EVENT_BUS.post(new EnergyTileLoadEvent(this));
+
+-- EnergyTileUnloadEvent --
+
+Another event every energy tile has to post is the EnergyTileUnloadEvent.
+
+The event has to be posted as soon as the implementing tile entity is being unloaded, either by
+unloading the containing chunk or by destroying the block containing it.
+
+It's possible to detect the unloading by triggering on both the beginning of
+TileEntity.invalidate() and the beginning of TileEntity.onChunkUnload().
+
+It is important that the tile entity is still properly linked to the world while posting the unload
+event, otherwise the energy net can't find all affected connections.
+
+
+--------------------------------------
+-- Participating Block Requirements --
+--------------------------------------
+
+The energy net blocks have to do the following to work properly:
+
+-- energy source --
+
+An energy source has to post the following events:
+ - EnergyTileLoadEvent on load
+ - EnergyTileUnloadEvent on unload
+
+Additionally the interface IEnergySource has to be implemented.
+
+-- energy sink --
+
+An energy sink has to post the following events:
+ - EnergyTileLoadEvent on load
+ - EnergyTileUnloadEvent on unload
+
+Additionally the interface IEnergySink has to be implemented.
+
+-- energy conductor --
+
+An energy conductor has to post the following events:
+ - EnergyTileLoadEvent on load
+ - EnergyTileUnloadEvent on unload
+
+Additionally the interface IEnergyConductor has to be implemented.
+
+
+------------------------------
+-- Energy Network Delegates --
+------------------------------
+
+A delegate is a separate object which performs tasks for the original object, in this case handling
+the energy net interaction.
+
+The TileEntity instances used by the events don't have to be the same as the in-world TileEntity
+instance for the corresponding position, it can be delegated to a separate TileEntity instance.
+This separate instance (delegate) needs to have its world and xyz coordinate fields set to match
+the in-world instance. The delegate implements the energy net interfaces and is added and removed
+to/from the energy net through EnergyTileLoadEvent and EnergyTileUnloadEvent.
+
+Separating the interfaces through a delegate allows to isolate the IC2 API usage into separate
+classes an potentially share common code like an input buffer with battery discharging outside the
+class hierarchy.
+It's even possible to use an ic2 energy net delegate alongside an in-world TileEntity which isn't
+designed to work with ic2 energy at all, like making a furnace electric by receiving energy through
+a delegate and adding the corresponding fuel amount to the in-world furnace TileEntity.
+
+Getting the in-world TileEntity for a delegate involves calling World.getBlockTileEntity() with the
+delegate's world and xyz coordinate information.
+
+
+------------------
+-- Multi Blocks --
+------------------
+
+Multi blocks are a group of blocks which act as one functional entity.
+
+The IMetaDelegate interface groups multiple TileEntity instances (=sub tiles) representing the
+individual blocks to a single Energy Net relevant node. The sub tiles may be normal in-world or
+delegate TileEntity instances.
+
+The meta delegate is added to energy net once for the whole multi block structure and implements
+the energy net interfaces as well. The sub tiles may optionally implement IEnergyEmitter and/or
+IEnergyAcceptor to specify their connectivity rules independently, otherwise the meta delegate will
+be queried.
+
+
+--------------------------------------------------
+-- How to implement/add your own energy network --
+--------------------------------------------------
+
+If you want to create an alternative way of distributing energy, e.g. to have different
+distribution rules or to use energy networks provided by other mods, you can register to the energy
+tile events and use the interfaces to handle the energy distribution yourself. It's no longer
+required to use conversion blocks.
+
+IC2's EnergyNet itself is built on top of the api events and interfaces, providing their default
+use case.
+
diff --git a/src/api/java/ic2/api/event/ExplosionEvent.java b/src/api/java/ic2/api/event/ExplosionEvent.java
new file mode 100644
index 0000000..c2df419
--- /dev/null
+++ b/src/api/java/ic2/api/event/ExplosionEvent.java
@@ -0,0 +1,44 @@
+package ic2.api.event;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.world.World;
+
+import cpw.mods.fml.common.eventhandler.Cancelable;
+
+import net.minecraftforge.event.world.WorldEvent;
+
+@Cancelable
+public class ExplosionEvent extends WorldEvent {
+ public ExplosionEvent(World world, Entity entity,
+ double x, double y, double z,
+ double power,
+ EntityLivingBase igniter,
+ int radiationRange, double rangeLimit) {
+ super(world);
+
+ this.entity = entity;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.power = power;
+ this.igniter = igniter;
+ this.radiationRange = radiationRange;
+ this.rangeLimit = rangeLimit;
+ }
+
+ /**
+ * Entity representing the explosive, may be null.
+ */
+ public final Entity entity;
+ public double x;
+ public double y;
+ public double z;
+ public double power;
+ /**
+ * Entity causing the explosion, may be null.
+ */
+ public final EntityLivingBase igniter;
+ public final int radiationRange;
+ public final double rangeLimit;
+}
diff --git a/src/api/java/ic2/api/event/LaserEvent.java b/src/api/java/ic2/api/event/LaserEvent.java
new file mode 100644
index 0000000..6d5ea28
--- /dev/null
+++ b/src/api/java/ic2/api/event/LaserEvent.java
@@ -0,0 +1,114 @@
+package ic2.api.event;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.item.ItemStack;
+import net.minecraft.world.World;
+
+import cpw.mods.fml.common.eventhandler.Cancelable;
+
+import net.minecraftforge.event.world.WorldEvent;
+
+/**
+ * A bunch of Events to handle the power of the Mining Laser.
+ */
+@Cancelable
+public class LaserEvent extends WorldEvent {
+ // the Laser Entity
+ public final Entity lasershot;
+
+ // the following variables can be changed and the Laser will adjust to them
+
+ // the Player firing the Laser. If the Laser gets "reflected" the Player could change.
+ public EntityLivingBase owner;
+ // Range of the Laser Shot. Determine the amount of broken blocks, as well, as each broken block will subtract ~1F from range.
+ public float range, power;
+ public int blockBreaks;
+ // Determines whether the laser will explode upon hitting something
+ public boolean explosive, smelt;
+
+ public LaserEvent(World world1, Entity lasershot1, EntityLivingBase owner1, float range1, float power1, int blockBreaks1, boolean explosive1, boolean smelt1) {
+ super(world1);
+ this.lasershot = lasershot1;
+ this.owner = owner1;
+ this.range = range1;
+ this.power = power1;
+ this.blockBreaks = blockBreaks1;
+ this.explosive = explosive1;
+ this.smelt = smelt1;
+ }
+
+ /**
+ * Event when the Laser is getting shot by a Player.
+ *
+ * The Item is the Laser Gun which the "Player" has shot
+ */
+ public static class LaserShootEvent extends LaserEvent {
+ ItemStack laseritem;
+
+ public LaserShootEvent(World world1, Entity lasershot1, EntityLivingBase owner1, float range1, float power1, int blockBreaks1, boolean explosive1, boolean smelt1, ItemStack laseritem1) {
+ super(world1, lasershot1, owner1, range1, power1, blockBreaks1, explosive1, smelt1);
+ this.laseritem = laseritem1;
+ }
+ }
+
+ /**
+ * Event when the Laser is exploding for some Reason.
+ *
+ * The Laser will no longer exist after this Event is posted as it either Explodes or despawns after the Event is fired.
+ */
+ public static class LaserExplodesEvent extends LaserEvent {
+ // explosion strength, even that can be changed!
+ public float explosionpower, explosiondroprate, explosionentitydamage;
+
+ public LaserExplodesEvent(World world1, Entity lasershot1, EntityLivingBase owner1, float range1, float power1, int blockBreaks1, boolean explosive1, boolean smelt1, float explosionpower1, float explosiondroprate1, float explosionentitydamage1) {
+ super(world1, lasershot1, owner1, range1, power1, blockBreaks1, explosive1, smelt1);
+ this.explosionpower = explosionpower1;
+ this.explosiondroprate = explosiondroprate1;
+ this.explosionentitydamage = explosionentitydamage1;
+ }
+ }
+
+ /**
+ * Event when the Laser is hitting a Block
+ * x, y and z are the Coords of the Block.
+ *
+ * Canceling this Event stops the Laser from attempting to break the Block, what is very useful for Glass.
+ * Use lasershot.setDead() to remove the Shot entirely.
+ */
+ public static class LaserHitsBlockEvent extends LaserEvent {
+ // targeted block, even that can be changed!
+ public int x, y, z;
+ public int side;
+ // removeBlock determines if the Block will be removed. dropBlock determines if the Block should drop something.
+ public boolean removeBlock, dropBlock;
+ public float dropChance;
+
+ public LaserHitsBlockEvent(World world1, Entity lasershot1, EntityLivingBase owner1, float range1, float power1, int blockBreaks1, boolean explosive1, boolean smelt1, int x1, int y1, int z1, int side1, float dropChance1, boolean removeBlock1, boolean dropBlock1) {
+ super(world1, lasershot1, owner1, range1, power1, blockBreaks1, explosive1, smelt1);
+ this.x = x1;
+ this.y = y1;
+ this.z = z1;
+ this.side = side1;
+ this.removeBlock = removeBlock1;
+ this.dropBlock = dropBlock1;
+ this.dropChance = dropChance1;
+ }
+ }
+
+ /**
+ * Event when the Laser is getting at a Living Entity
+ *
+ * Canceling this Event ignores the Entity
+ * Use lasershot.setDead() to remove the Shot entirely.
+ */
+ public static class LaserHitsEntityEvent extends LaserEvent {
+ // the Entity which the Laser has shot at, even the target can be changed!
+ public Entity hitentity;
+
+ public LaserHitsEntityEvent(World world1, Entity lasershot1, EntityLivingBase owner1, float range1, float power1, int blockBreaks1, boolean explosive1, boolean smelt1, Entity hitentity1) {
+ super(world1, lasershot1, owner1, range1, power1, blockBreaks1, explosive1, smelt1);
+ this.hitentity = hitentity1;
+ }
+ }
+}
diff --git a/src/api/java/ic2/api/event/PaintEvent.java b/src/api/java/ic2/api/event/PaintEvent.java
new file mode 100644
index 0000000..c6565c2
--- /dev/null
+++ b/src/api/java/ic2/api/event/PaintEvent.java
@@ -0,0 +1,32 @@
+package ic2.api.event;
+
+import net.minecraft.world.World;
+
+import cpw.mods.fml.common.eventhandler.Cancelable;
+
+import net.minecraftforge.event.world.WorldEvent;
+
+@Cancelable
+public class PaintEvent extends WorldEvent {
+ // target block
+ public final int x;
+ public final int y;
+ public final int z;
+ public final int side;
+
+ // color to paint the block
+ public final int color;
+
+ // set to true to confirm the operation
+ public boolean painted = false;
+
+ public PaintEvent(World world1, int x1, int y1, int z1, int side1, int color1) {
+ super(world1);
+
+ this.x = x1;
+ this.y = y1;
+ this.z = z1;
+ this.side = side1;
+ this.color = color1;
+ }
+}
diff --git a/src/api/java/ic2/api/event/RetextureEvent.java b/src/api/java/ic2/api/event/RetextureEvent.java
new file mode 100644
index 0000000..d530578
--- /dev/null
+++ b/src/api/java/ic2/api/event/RetextureEvent.java
@@ -0,0 +1,37 @@
+package ic2.api.event;
+
+import net.minecraft.block.Block;
+import net.minecraft.world.World;
+
+import cpw.mods.fml.common.eventhandler.Cancelable;
+
+import net.minecraftforge.event.world.WorldEvent;
+
+@Cancelable
+public class RetextureEvent extends WorldEvent {
+ // target block
+ public final int x;
+ public final int y;
+ public final int z;
+ public final int side;
+
+ // referenced block (to grab the texture from)
+ public final Block referencedBlock;
+ public final int referencedMeta;
+ public final int referencedSide;
+
+ // set to true to confirm the operation
+ public boolean applied = false;
+
+ public RetextureEvent(World world1, int x1, int y1, int z1, int side1, Block referencedBlock, int referencedMeta1, int referencedSide1) {
+ super(world1);
+
+ this.x = x1;
+ this.y = y1;
+ this.z = z1;
+ this.side = side1;
+ this.referencedBlock = referencedBlock;
+ this.referencedMeta = referencedMeta1;
+ this.referencedSide = referencedSide1;
+ }
+}
diff --git a/src/api/java/ic2/api/event/package-info.java b/src/api/java/ic2/api/event/package-info.java
new file mode 100644
index 0000000..d331b92
--- /dev/null
+++ b/src/api/java/ic2/api/event/package-info.java
@@ -0,0 +1,4 @@
+@API(apiVersion="1.0", owner="IC2", provides="IC2API")
+package ic2.api.event;
+import cpw.mods.fml.common.API;
+
diff --git a/src/api/java/ic2/api/info/IEnergyValueProvider.java b/src/api/java/ic2/api/info/IEnergyValueProvider.java
new file mode 100644
index 0000000..1e7b2cc
--- /dev/null
+++ b/src/api/java/ic2/api/info/IEnergyValueProvider.java
@@ -0,0 +1,17 @@
+package ic2.api.info;
+
+import net.minecraft.item.ItemStack;
+
+public interface IEnergyValueProvider {
+ /**
+ * Determine the energy value for a single item in the supplied stack.
+ * The value is used by most machines in the discharge slot.
+ *
+ * This only applies to basic single use items, others are to be queried
+ * through e.g. ElectricItem.manager.getCharge().
+ *
+ * @param itemStack ItemStack containing the item to evaluate.
+ * @return energy in EU
+ */
+ double getEnergyValue(ItemStack itemStack);
+}
diff --git a/src/api/java/ic2/api/info/IFuelValueProvider.java b/src/api/java/ic2/api/info/IFuelValueProvider.java
new file mode 100644
index 0000000..7c7fe0f
--- /dev/null
+++ b/src/api/java/ic2/api/info/IFuelValueProvider.java
@@ -0,0 +1,15 @@
+package ic2.api.info;
+
+import net.minecraft.item.ItemStack;
+
+public interface IFuelValueProvider {
+ /**
+ * Determine the fuel value for a single item in the supplied stack.
+ * The information currently applies to Generators and the Iron Furnace.
+ *
+ * @param itemStack ItemStack containing the item to evaluate.
+ * @param allowLava Determine if lava has a fuel value, currently only true for the Iron Furnace.
+ * @return fuel value
+ */
+ int getFuelValue(ItemStack itemStack, boolean allowLava);
+}
diff --git a/src/api/java/ic2/api/info/Info.java b/src/api/java/ic2/api/info/Info.java
new file mode 100644
index 0000000..f8e30ad
--- /dev/null
+++ b/src/api/java/ic2/api/info/Info.java
@@ -0,0 +1,39 @@
+package ic2.api.info;
+
+import net.minecraft.potion.Potion;
+import net.minecraft.util.DamageSource;
+
+import cpw.mods.fml.common.Loader;
+import cpw.mods.fml.common.LoaderState;
+
+public class Info {
+ public static IEnergyValueProvider itemEnergy;
+ public static IFuelValueProvider itemFuel;
+ public static Object ic2ModInstance;
+
+ /**
+ * Damage Sources used by IC2.
+ * Getting assigned in preload.
+ */
+ public static DamageSource DMG_ELECTRIC, DMG_NUKE_EXPLOSION, DMG_RADIATION;
+
+ /**
+ * Potions used by IC2.
+ * Getting assigned in preload.
+ */
+ public static Potion POTION_RADIATION;
+
+ public static boolean isIc2Available() {
+ if (ic2Available != null) return ic2Available;
+
+ boolean loaded = Loader.isModLoaded("IC2");
+
+ if (Loader.instance().hasReachedState(LoaderState.CONSTRUCTING)) {
+ ic2Available = loaded;
+ }
+
+ return loaded;
+ }
+
+ private static Boolean ic2Available = null;
+} \ No newline at end of file
diff --git a/src/api/java/ic2/api/info/package-info.java b/src/api/java/ic2/api/info/package-info.java
new file mode 100644
index 0000000..59936e5
--- /dev/null
+++ b/src/api/java/ic2/api/info/package-info.java
@@ -0,0 +1,4 @@
+@API(apiVersion="1.0", owner="IC2", provides="IC2API")
+package ic2.api.info;
+import cpw.mods.fml.common.API;
+
diff --git a/src/api/java/ic2/api/item/ElectricItem.java b/src/api/java/ic2/api/item/ElectricItem.java
new file mode 100644
index 0000000..19e5fc1
--- /dev/null
+++ b/src/api/java/ic2/api/item/ElectricItem.java
@@ -0,0 +1,59 @@
+package ic2.api.item;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * Allows for charging, discharging and using electric items (IElectricItem).
+ */
+public final class ElectricItem {
+ /**
+ * IElectricItemManager to use for interacting with IElectricItem ItemStacks.
+ *
+ * This manager will act as a gateway and delegate the tasks to the final implementation
+ * (rawManager or a custom one) as necessary.
+ */
+ public static IElectricItemManager manager;
+
+ /**
+ * Standard IElectricItemManager implementation, only call it directly from another
+ * IElectricItemManager. Use manager instead.
+ */
+ public static IElectricItemManager rawManager;
+
+ /**
+ * Register an electric item manager for items not implementing IElectricItem.
+ *
+ * This method is only designed for special purposes, implementing IElectricItem or
+ * ISpecialElectricItem instead of using this is faster.
+ *
+ * Managers used with ISpecialElectricItem shouldn't be registered.
+ *
+ * @param manager Manager to register.
+ */
+ public static void registerBackupManager(IBackupElectricItemManager manager) {
+ backupManagers.add(manager);
+ }
+
+ /**
+ * Get the electric item manager suitable for the supplied item stack.
+ *
+ * This method is for internal use only.
+ *
+ * @param stack ItemStack to be handled.
+ * @return First suitable electric item manager.
+ */
+ public static IBackupElectricItemManager getBackupManager(ItemStack stack) {
+ for (IBackupElectricItemManager manager : backupManagers) {
+ if (manager.handles(stack)) return manager;
+ }
+
+ return null;
+ }
+
+
+ private static final List<IBackupElectricItemManager> backupManagers = new ArrayList<IBackupElectricItemManager>();
+}
+
diff --git a/src/api/java/ic2/api/item/IBackupElectricItemManager.java b/src/api/java/ic2/api/item/IBackupElectricItemManager.java
new file mode 100644
index 0000000..2ae254a
--- /dev/null
+++ b/src/api/java/ic2/api/item/IBackupElectricItemManager.java
@@ -0,0 +1,12 @@
+package ic2.api.item;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * This interface specifies a manager applicable to items not implementing IElectricItem.
+ *
+ * The manager implementing this has to be registered with ElectricItem.registerBackupManager().
+ */
+public interface IBackupElectricItemManager extends IElectricItemManager {
+ boolean handles(ItemStack stack);
+}
diff --git a/src/api/java/ic2/api/item/IBlockCuttingBlade.java b/src/api/java/ic2/api/item/IBlockCuttingBlade.java
new file mode 100644
index 0000000..24a8f00
--- /dev/null
+++ b/src/api/java/ic2/api/item/IBlockCuttingBlade.java
@@ -0,0 +1,6 @@
+package ic2.api.item;
+
+
+public interface IBlockCuttingBlade {
+ int gethardness();
+}
diff --git a/src/api/java/ic2/api/item/IBoxable.java b/src/api/java/ic2/api/item/IBoxable.java
new file mode 100644
index 0000000..bca2cc0
--- /dev/null
+++ b/src/api/java/ic2/api/item/IBoxable.java
@@ -0,0 +1,14 @@
+package ic2.api.item;
+
+import net.minecraft.item.ItemStack;
+
+
+public interface IBoxable {
+ /**
+ * Determine whether an item can be stored in a toolbox or not.
+ *
+ * @param itemstack item to be stored
+ * @return Whether to store the item in the toolbox or not
+ */
+ public abstract boolean canBeStoredInToolbox(ItemStack itemstack);
+}
diff --git a/src/api/java/ic2/api/item/IC2Items.java b/src/api/java/ic2/api/item/IC2Items.java
new file mode 100644
index 0000000..9b8c7df
--- /dev/null
+++ b/src/api/java/ic2/api/item/IC2Items.java
@@ -0,0 +1,598 @@
+package ic2.api.item;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * Provides access to IC2 blocks and items.
+ *
+ * Some items can be acquired through the ore dictionary which is the recommended way.
+ * The items are initialized while IC2 is being loaded - try to use ModsLoaded() or load your mod after IC2.
+ * Some blocks/items can be disabled by a config setting, so it's recommended to check if they're null first.
+ *
+ * Getting the associated Block/Item for an ItemStack x:
+ * Blocks: Block.blocksList[x.itemID]
+ * Items: x.getItem()
+ */
+public final class IC2Items {
+ /**
+ * Get an ItemStack for a specific item name, example: Items.getItem("resin")
+ * See the list below for item names.
+ * Make sure to copy() the ItemStack if you want to modify it.
+ *
+ * @param name item name
+ * @return The item or null if the item does not exist or an error occurred
+ */
+ public static ItemStack getItem(String name) {
+ try {
+ if (Ic2Items == null) Ic2Items = Class.forName(getPackage() + ".core.Ic2Items");
+
+ Object ret = Ic2Items.getField(name).get(null);
+
+ if (ret instanceof ItemStack) {
+ return (ItemStack) ret;
+ }
+ return null;
+ } catch (Exception e) {
+ System.out.println("IC2 API: Call getItem failed for "+name);
+
+ return null;
+ }
+ }
+
+ /* Possible values:
+
+ // ores
+ copperOre; // Copper Ore block, currently not meta sensitive, meta in ItemStack set to 0, ore dictionary: oreCopper, null with enableWorldGenOreCopper = false
+ tinOre; // Tin Ore block, currently not meta sensitive, meta in ItemStack set to 0, ore dictionary: oreTin, null with enableWorldGenOreTin = false
+ uraniumOre; // Tin Ore block, currently not meta sensitive, meta in ItemStack set to 0, ore dictionary: oreUranium, null with enableWorldGenOreUranium = false
+ leadOre; // Lead Ore Block, currently not meta sensitive, meta in ItemStack set to 0, ore dictionary: oreLead, null with enableWorldGenOreLead = false
+
+ // rubber related
+
+ Rubber wood block, meta reflects the state, meta in ItemStack set to 0, ore dictionary: woodRubber (with meta 0), null with enableWorldGenTreeRubber = false
+ dropped (as an item) -> metadata 0
+ block, no resin spot -> metadata 0 or 1
+ block, wet resin spot -> metadata 2-5 (according to the side)
+ block, dry resin spot -> metadata 8-11 (wet state + 6)
+
+ rubberWood;
+ rubberLeaves; // Rubber Leaves block, currently not meta sensitive, meta in ItemStack set to 0, null with enableWorldGenTreeRubber = false
+ rubberSapling; // Rubber Sapling block, currently not meta sensitive, meta in ItemStack set to 0, null with enableWorldGenTreeRubber = false
+ resinSheet; // Resin Sheet block, currently not meta sensitive
+ rubberTrampoline; // Rubber Trampoline block, meta reflects internal state, meta in ItemStack set to 0
+
+ // building/storage
+ ironFence; // Iron Fence block, currently not meta sensitive
+
+ reinforcedStone; // Reinforced Stone block, currently not meta sensitive
+ reinforcedGlass; // Reinforced Glass block, currently not meta sensitive
+ reinforcedDoorBlock; // Reinforced Door block, meta reflects the state (see vanilla doors), meta in ItemStack set to 0
+
+ constructionreinforcedFoam; // Construction Reinforced Foam block, currently not meta sensitive
+ constructionFoam; // Construction Foam block, currently not meta sensitive
+ constructionFoamWall; // Construction Foam Wall block, meta = color, implements IPaintableBlock
+ scaffold; // Scaffold block, meta reflects internal physical model data
+ ironScaffold; // Scaffold block, meta reflects internal physical model data
+
+ bronzeBlock; // Bronze block, meta sensitive
+ copperBlock; // Copper block, meta sensitive
+ tinBlock; // Tin block, meta sensitive
+ uraniumBlock; // Uranium block, meta sensitive
+ leadBlock; // Uranium block, meta sensitive
+
+ // cables (when placed as a block, inventory items are different; TE implements IEnergyConductor)
+
+ copperCableBlock; // Copper Cable block, meta sensitive
+ insulatedCopperCableBlock; // Insulated Copper Cable block, meta sensitive
+
+ goldCableBlock; // Gold Cable block, meta sensitive
+ insulatedGoldCableBlock; // Insulated Gold Cable block, meta sensitive
+ doubleInsulatedGoldCableBlock; // Double Insulated Gold Cable block, meta sensitive
+
+ ironCableBlock; // Iron Cable block, meta sensitive
+ insulatedIronCableBlock; // Insulated Iron Cable block, meta sensitive
+ doubleInsulatedIronCableBlock; // Double Insulated Iron Cable block, meta sensitive
+ trippleInsulatedIronCableBlock; // Tripple Insulated Iron Cable block, meta sensitive
+
+ glassFiberCableBlock; // Glass Fiber Cable block, meta sensitive
+
+ tinCableBlock; // Tin Cable block, meta sensitive
+ insulatedtinCableBlock; // Insulated Tin Cable item, meta sensitive
+ detectorCableBlock; // Detector Cable block, meta sensitive
+ splitterCableBlock; // Splitter Cable block, meta sensitive
+
+ // generators + related (TE implements IEnergySource ex. reactorChamber)
+
+ generator; // Generator block, meta sensitive
+ geothermalGenerator; // Geothermal Generator block, meta sensitive
+ waterMill; // Water Mill block, meta sensitive
+ solarPanel; // Solar Panel block, meta sensitive
+ windMill; // Wind Mill block, meta sensitive
+ nuclearReactor; // Nuclear Reactor block, meta sensitive
+ reactorChamber; // Reactor Chamber block, currently not meta sensitive
+ RTGenerator; // Radioisotope Thermoelectric Generator block, meta sensitive
+ semifluidGenerator; // Semifluid Generator block, meta sensitive
+
+
+ // energy storages (TE implements IEnergySource and IEnergyConductor)
+
+ batBox; // BatBox block, meta sensitive
+ cesuUnit; // CESU Unit block, meta sensitive
+ mfeUnit; // MFE Unit block, meta sensitive
+ mfsUnit; // MFS Unit block, meta sensitive
+
+ // transformers (TE implements IEnergySource and IEnergyConductor)
+
+ lvTransformer; // LV Transformer block, meta sensitive
+ mvTransformer; // MV Transformer block, meta sensitive
+ hvTransformer; // HV Transformer block, meta sensitive
+ evTransformer; // EV Transformer block, meta sensitive
+
+ // machines + related (TE implements IEnergySink ex. machine, miningPipe, miningPipeTip)
+
+ machine; // Machine block, meta sensitive
+ advancedMachine; // Advanced Machine block, meta sensitive
+
+ ironFurnace; // Iron Furnace block, meta sensitive
+ electroFurnace; // Electro Furnace block, meta sensitive
+ macerator; // Macerator block, meta sensitive
+ extractor; // Extractor block, meta sensitive
+ compressor; // Compressor block, meta sensitive
+ canner; // Canner block, meta sensitive
+ miner; // Miner block, meta sensitive
+ pump; // Pump block, meta sensitive
+ magnetizer; // Magnetizer block, meta sensitive
+ electrolyzer; // Electrolyzer block, meta sensitive
+ recycler; // Recycler block, meta sensitive
+ inductionFurnace; // Induction Furnace block, meta sensitive
+ massFabricator; // Mass Fabricator block, meta sensitive
+ terraformer; // Terraformer block, meta sensitive
+ teleporter; // Teleporter block, meta sensitive
+ teslaCoil; // Tesla Coil block, meta sensitive
+ luminator; // Passive (dark) Luminator block, meta = facing
+ activeLuminator; // Active (bright) Luminator block, meta = facing
+ centrifuge; // Centrifuge block, meta sensitive
+ metalformer; // MetalFormer block , meta sensitive
+ orewashingplant; // Ore Wasching Plant, Meta sensitive
+ patternstorage; // Pattern Storage, Meta sensitive
+ scanner; // Scanner, Meta sensitive
+ replicator; // Replicator, Meta sensitive
+
+ miningPipe; // Mining Pipe block, currently not meta sensitive, meta in ItemStack set to 0
+ miningPipeTip; // Mining Pipe Tip block, currently not meta sensitive, meta in ItemStack set to 0
+
+
+ // personal blocks
+
+ personalSafe; // Personal Safe block, meta sensitive
+ tradeOMat; // Trade-O-Mat block, meta sensitive
+ energyOMat; // Energy-O-Mat block, meta sensitive
+
+ // explosives
+
+ industrialTnt; // Industrial TNT block, currently not meta sensitive
+ nuke; // Nuke block, currently not meta sensitive
+ dynamiteStick; // Dynamite Stick block, meta = placement, meta in ItemStack set to 0
+ dynamiteStickWithRemote; // Dynamite Stick with Remote block, meta = placement, meta in ItemStack set to 0
+
+ // Agriculture Stuff
+
+ crop; // Crop Block, empty, not meta sensitive
+ cropmatron; // Cropmatron machien block, meta sensititve
+
+ // ----- items -----
+
+ // rubber + related
+ resin; // Resin item, currently not meta sensitive
+ rubber; // Rubber item, currently not meta sensitive, ore dictionary: itemRubber
+
+ FluidCell;
+
+ // Lithium -> Tritium
+
+ reactorLithiumCell; // LithiumCell use in Reaktor, , meta = damage value
+ TritiumCell; // Tritium, currently not meta sensitive
+
+ // Nuclear Fuel
+
+ UranFuel; // , currently not meta sensitive
+ MOXFuel; // , currently not meta sensitive
+ Plutonium; // , currently not meta sensitive
+ smallPlutonium; // , currently not meta sensitive
+ Uran235; // , currently not meta sensitive
+ smallUran235; // , currently not meta sensitive
+ Uran238; // , currently not meta sensitive
+
+ reactorDepletedUraniumSimple; // Depleted Uranium Cell items, currently not meta sensitive
+ reactorDepletedUraniumDual;
+ reactorDepletedUraniumQuad;
+ reactorDepletedMOXSimple; // Depleted MOX Cell items, currently not meta sensitive
+ reactorDepletedMOXDual;
+ reactorDepletedMOXQuad;
+ reactorMOXSimple; // Depleted MOX Cell items, currently not meta sensitive
+ reactorMOXDual;
+ reactorMOXQuad;
+ RTGPellets;
+
+
+ // Recipe Parts
+
+ coil; // Coil, meta sensitive
+ elemotor; // electric motor, meta sensitive
+ powerunit; // Item Power Unit, meta sensitive
+ powerunitsmall; // Item Power Unit, meta sensitive
+
+
+ // ItemCasing
+
+ casingcopper; // Copper ItemCasing, meta sensitive
+ casingtin; // Tin ItemCasing, meta sensitive
+ casingbronze; // Bronze ItemCasing, meta sensitive
+ casinggold; // Gold ItemCasing, meta sensitive
+ casingiron; // Iron ItemCasing, meta sensitive
+ @Deprecated
+ casingadviron; // Refined Iron ItemCasing, meta sensitive
+ casinglead; // Lead ItemCasing, meta sensitive
+
+ // Crushed Ore
+ crushedIronOre; // Crushed Iron Ore, meta sensitive
+ crushedCopperOre; // Crushed Copper Ore, meta sensitive
+ crushedGoldOre; // Crushed Gold Ore, meta sensitive
+ crushedTinOre; // Crushed Tin Ore, meta sensitive
+ crushedUraniumOre; // Crushed Uranium Ore, meta sensitive
+ crushedSilverOre; // Crushed Silver Ore, meta sensitive
+ crushedLeadOre; // Crushed Lead Ore, meta sensitive
+
+
+ //Purify Crushed Ore
+ purifiedCrushedIronOre; // Purify Crushed Iron Ore, meta sensitive
+ purifiedCrushedCopperOre; // Purify Crushed Copper Ore, meta sensitive
+ purifiedCrushedGoldOre; // Purify Crushed Gold Ore, meta sensitive
+ purifiedCrushedTinOre; // Purify Crushed Tin Ore, meta sensitive
+ purifiedCrushedUraniumOre; // Purify Crushed Uranium Ore, meta sensitive
+ purifiedCrushedSilverOre; // Purify Crushed Silver Ore, meta sensitive
+ purifiedCrushedLeadOre; // Purify Crushed Lead Ore, meta sensitive
+
+ // dusts
+ stoneDust;
+ bronzeDust; // Bronze Dust item, meta sensitive, ore dictionary: dustBronze
+ clayDust; // Clay Dust item, meta sensitive, ore dictionary: dustClay
+ coalDust; // Coal Dust item, meta sensitive, ore dictionary: dustCoal
+ copperDust; // Copper Dust item, meta sensitive, ore dictionary: dustCopper
+ goldDust; // Gold Dust item, meta sensitive, ore dictionary: dustGold
+ ironDust; // Iron Dust item, meta sensitive, ore dictionary: dustIron
+ silverDust; // Silver Dust item, meta sensitive, ore dictionary: dustSilver
+ tinDust; // Tin Dust item, meta sensitive, ore dictionary: dustTin
+ hydratedCoalDust; // Hydrated Coal Dust item, meta sensitive
+ leadDust; // Lead Dust item, meta sensitive, ore dictionary: dustLead
+ obsidianDust; // Obsidian Dust item, meta sensitive, ore dictionary: dustObsidian
+ lapiDust; // Lapi Dust item, meta sensitive, ore dictionary: dustLapi
+ sulfurDust; // Sulfur Dust item, meta sensitive, ore dictionary: dustSulfur
+ lithiumDust; // Lithium dust, meta sensitive, ore dictionary: dustLithium
+
+ // small dusts
+
+ smallIronDust; // Small Iron Dust item, meta sensitive
+ smallCopperDust; // Small Copper Dust item, meta sensitive
+ smallGoldDust; // Small Gold Dust item, meta sensitive
+ smallTinDust; // Small Tin Dust item, meta sensitive
+ smallSilverDust; // Small Silver Dust item, meta sensitive
+ smallLeadDust; // Small Lead Dust item, meta sensitive
+ smallSulfurDust; // Small Sulfur Dust item, meta sensitive
+ smallLithiumDust; // Small Lithium Dust item, meta sensitive
+
+
+ // ingots
+ @Deprecated
+ refinedIronIngot; // Refined Iron Ingot item, currently not meta sensitive, ore dictionary: ingotRefinedIron
+ copperIngot; // Copper Ingot item, currently not meta sensitive, ore dictionary: ingotCopper
+ tinIngot; // Tin Ingot item, currently not meta sensitive, ore dictionary: ingotTin
+ bronzeIngot; // Bronze Ingot item, currently not meta sensitive, ore dictionary: ingotBronze
+ mixedMetalIngot; // Mixed Metal Ingot item, currently not meta sensitive
+ leadIngot; // Lead Ingot item, currently not meta sensitive
+
+
+ // tools/weapons (without electric tools)
+ treetap; // Treetap item, meta = damage value
+ wrench; // Wrench item, meta = damage value
+ cutter; // Insulation Cutter item, meta = damage value
+ constructionFoamSprayer; // Construction Foam Sprayer item, meta = charges (as of v1.45)
+
+ bronzePickaxe; // Bronze Pickaxe item, meta = damage value
+ bronzeAxe; // Bronze Axe item, meta = damage value
+ bronzeSword; // Bronze Sword item, meta = damage value
+ bronzeShovel; // Bronze Shovel item, meta = damage value
+ bronzeHoe; // Bronze Hoe item, meta = damage value
+
+ ForgeHammer; // Refine Iron Hammer item, meta = damage value
+
+ // el. tools/devices/weapons
+ miningDrill; // Mining Drill item, meta = damage value for charge level
+ diamondDrill; // Diamond Tipped Mining Drill item, meta = damage value for charge level
+ iridiumDrill; // Iridium Tipped Mining Drill item, meta = damage value for charge level
+ chainsaw; // Chainsaw item, meta = damage value for charge level
+ electricWrench; // Electric Wrench item, meta = damage value for charge level
+ electricTreetap; // Electric Treetap item, meta = damage value for charge level
+ miningLaser; // Mining Laser item, meta = damage value for charge level
+
+ ecMeter; // EC-Mater item, meta = itemdata db index (as of v1.45)
+ odScanner; // Ore Density Scanner item, meta = damage value for charge level
+ ovScanner; // Ore Value Scanner item, meta = damage value for charge level
+ obscurator; // Obscurator item, meta = damage value for charge level
+
+ frequencyTransmitter; // Frequency Transmitter item, meta = itemdata db index (as of v1.45)
+
+ nanoSaber; // Idle Nano Saber item, meta = damage value for charge level
+ enabledNanoSaber; // Enabled Nano Saber item, meta = damage value for charge level
+
+ toolbox; // Open/Empty toolbox, meta = Open (0) / Closed (1)
+
+ // armor/wearable
+ hazmatHelmet; // Hazmat Helmet item, meta = damage value
+ hazmatChestplate; // Hazmat Chestplate item, meta = damage value
+ hazmatLeggings; // Hazmat Leggings item, meta = damage value
+ hazmatBoots; // Hazmat Boots item, meta = damage value
+
+ bronzeHelmet; // Bronze Helmet Armor item, meta = damage value
+ bronzeChestplate; // Bronze Chestplate Armor item, meta = damage value
+ bronzeLeggings; // Bronze Leggings Armor item, meta = damage value
+ bronzeBoots; // Bronze Boots Armor item, meta = damage value
+
+ compositeArmor; // Composite Armor item, meta = damage value for charge level
+
+ nanoHelmet; // Nano Helmet Armor item, meta = damage value for charge level
+ nanoBodyarmor; // Nano Bodyarmor item, meta = damage value for charge level
+ nanoLeggings; // Nano Leggings Armor item, meta = damage value for charge level
+ nanoBoots; // Nano Boots Armor item, meta = damage value for charge level
+
+ quantumHelmet; // Quantum Helmet Armor item, meta = damage value for charge level
+ quantumBodyarmor; // Quantum Bodyarmor item, meta = damage value for charge level
+ quantumLeggings; // Quantum Leggings Armor item, meta = damage value for charge level
+ quantumBoots; // Quantum Boots Armor item, meta = damage value for charge level
+
+ jetpack; // Jetpack item, meta = damage value for fuel level
+ electricJetpack; // Electric Jetpack item, meta = damage value for charge level
+
+ batPack; // BatPack item, meta = damage value for charge level
+ advbatPack; // Adv.BatPack item, meta = damage value for charge level
+ lapPack; // LapPack item, meta = damage value for charge level
+ energyPack; // EnergyPack item, meta = damage value for charge level
+
+ cfPack; // CF Pack item, meta = charges (as of v1.45)
+ solarHelmet; // Solar Helmet, currently not meta sensitive
+ staticBoots; // Static Boots, currently not meta sensitive
+ nightvisionGoggles; // Nightvision Goggles, meta = damage value for charge level
+
+ // batteries
+ reBattery; // Empty RE Battery item, currently not meta sensitive
+ chargedReBattery; // RE Battery item, meta = damage value for charge level
+ advBattery; // Adv Batteryitem, meta = damage value for charge level
+ energyCrystal; // Energy Crystal item, meta = damage value for charge level
+ lapotronCrystal; // Lapotron Crystal item, meta = damage value for charge level
+ suBattery; // SU Battery item, meta = damage value for charge level
+
+ // cables
+ copperCableItem; // Copper Cable item, meta sensitive
+ insulatedCopperCableItem; // Insulated Copper Cable item, meta sensitive
+
+ goldCableItem; // Gold Cable item, meta sensitive
+ insulatedGoldCableItem; // Insulated Gold Cable item, meta sensitive
+
+ @Deprecated
+ doubleInsulatedGoldCableItem; // Double Insulated Gold Cable item, meta sensitive
+
+ ironCableItem; // Iron Cable item, meta sensitive
+ insulatedIronCableItem; // Insulated Iron Cable item, meta sensitive
+
+ @Deprecated
+ doubleInsulatedIronCableItem; // Double Insulated Iron Cable item, meta sensitive
+ @Deprecated
+ trippleInsulatedIronCableItem; // Tripple Insulated Iron Cable item, meta sensitive
+ insulatedTinCableItem;
+ glassFiberCableItem; // Glass Fiber Cable item, meta sensitive
+ tinCableItem; // Tin Cable item, meta sensitive
+
+
+ detectorCableItem; // Detector Cable item, meta sensitive
+ splitterCableItem; // Splitter Cable item, meta sensitive
+
+ // cells/containers (without reactor components)
+
+ cell; // Empty Cell item, meta sensitive
+ lavaCell; // Lava Cell item, meta sensitive
+ waterCell; // Water Cell item, meta sensitive
+ UuMatterCell; // UUMatter Cell item, meta sensitive
+ CFCell; // constructionFoam Cell item, meta sensitive
+
+
+ fuelRod; // Empy Fuel Rod item, currently not meta sensitive
+ hydratedCoalCell; // Hydrated Coal Cell item, currently not meta sensitive
+ bioCell; // Bio Cell item, currently not meta sensitive
+ coalfuelCell; // Coalfuel Cell item, currently not meta sensitive
+ biofuelCell; // Biofuel Cell item, currently not meta sensitive
+ electrolyzedWaterCell; // Electrolyzed Water Cell item, currently not meta sensitive
+ airCell; // Compressed Air item, currently not meta sensitive
+
+ fuelCan; // Empty Fuel Can item, currently not meta sensitive
+ filledFuelCan; // Fuel Can item, meta = fuel value (as of v1.45)
+
+ tinCan; // Empty Tin Can item, currently not meta sensitive
+ filledTinCan; // Filled Tin Can item, currently not meta sensitive
+
+ // reactor components
+ reactorUraniumSimple; // Uranium Cell items, meta = consumed uranium ticks
+ reactorUraniumDual;
+ reactorUraniumQuad;
+
+ reactorCoolantSimple;
+ reactorCoolantTriple ; // Coolant Cell item, NBT for heat-storage, meta is 0-10000 for display
+ reactorCoolantSix;
+
+ reactorPlating; // Integrated Reactor Plating item, currently not meta sensitive
+ reactorPlatingHeat;
+ reactorPlatingExplosive;
+
+ reactorHeatSwitch; // Integrated Heat Disperser item, NBT for heat-storage, meta is 0-10000 for display
+ reactorHeatSwitchCore;
+ reactorHeatSwitchSpread;
+ reactorHeatSwitchDiamond;
+
+ reactorVent; // Heat Venting component, NBT for heat-storage, meta is 0-10000 for display
+ reactorVentCore;
+ reactorVentGold;
+ reactorVentSpread;// Special: Does not store heat
+ reactorVentDiamond;
+
+ reactorReflector; // Increase efficiency without additional ticks, NBT for heat-storage, meta is 0-10000 for display
+ reactorReflectorThick; // Increase efficiency without additional ticks, NBT for heat-storage, meta is 0-10000 for display
+ reactorCondensator; // Consumes redstone to absorb heat, NBT for storage, meta is 0-10000 for display
+ reactorCondensatorLap; // Consumes redstone/lapis to absorb heat, mNBT for storage, meta is 0-10000 for display
+
+ // terraformer blueprints
+ terraformerBlueprint; // Empty Terraformer Blueprint item, currently not meta sensitive
+ cultivationTerraformerBlueprint; // Cultivation Terraformer Blueprint item, currently not meta sensitive
+ irrigationTerraformerBlueprint; // Irrigation Terraformer Blueprint item, currently not meta sensitive
+ chillingTerraformerBlueprint; // Chilling Terraformer Blueprint item, currently not meta sensitive
+ desertificationTerraformerBlueprint; // Desertification Terraformer Blueprint item, currently not meta sensitive
+ flatificatorTerraformerBlueprint; // Flatificator Terraformer Blueprint item, currently not meta sensitive
+ mushroomTerraformerBlueprint; // Mushroom Terraformer Blueprint item, currently not meta sensitive
+
+ // diamond chain
+ coalBall; // Coal Ball item, currently not meta sensitive
+ compressedCoalBall; // Compressed Coal Ball item, currently not meta sensitive
+ coalChunk; // Coal Chunk item, currently not meta sensitive
+ industrialDiamond; // Industrial Diamond item, currently not meta sensitive, DEPRECATED
+
+ // recycler chain
+ scrap; // Scrap item, currently not meta sensitive
+ scrapBox; // Scrap Box item, currently not meta sensitive
+
+ // fuel production chain
+ hydratedCoalClump; // Hydrated Coal Clump item, currently not meta sensitive
+ plantBall; // Plant Ball item, currently not meta sensitive
+ compressedPlantBall; // Compressed Plant Ball item, currently not meta sensitive
+
+ // painting
+ painter; // Painter item, currently not meta sensitive
+
+ blackPainter; // Black Painter item, meta = damage value
+ redPainter; // Red Painter item, meta = damage value
+ greenPainter; // Green Painter item, meta = damage value
+ brownPainter; // Brown Painter item, meta = damage value
+ bluePainter; // Blue Painter item, meta = damage value
+ purplePainter; // Purple Painter item, meta = damage value
+ cyanPainter; // Cyan Painter item, meta = damage value
+ lightGreyPainter; // Light Grey Painter item, meta = damage value
+ darkGreyPainter; // Dark Grey Painter item, meta = damage value
+ pinkPainter; // Pink Painter item, meta = damage value
+ limePainter; // Lime Painter item, meta = damage value
+ yellowPainter; // Yellow Painter item, meta = damage value
+ cloudPainter; // Cloud Painter item, meta = damage value
+ magentaPainter; // Magenta Painter item, meta = damage value
+ orangePainter; // Orange Painter item, meta = damage value
+ whitePainter; // White Painter item, meta = damage value
+
+ // explosives + related
+ dynamite; // Throwable Dynamite item, currently not meta sensitive
+ stickyDynamite; // Throwable Sticky Dynamite item, currently not meta sensitive
+
+ remote; // Dynamite Remote item, currently not meta sensitive
+
+ // misc intermediate recipe ingredients
+ electronicCircuit; // Electronic Circuit item, currently not meta sensitive
+ advancedCircuit; // Advanced Circuit item, currently not meta sensitive
+
+ advancedAlloy; // Advanced Alloy item, currently not meta sensitive
+
+ carbonFiber; // Raw Carbon Fiber item, currently not meta sensitive
+ carbonMesh; // Raw Carbon Mesh item, currently not meta sensitive
+ carbonPlate; // Carbon Plate item, currently not meta sensitive
+
+ matter; // UUA item, currently not meta sensitive
+ iridiumOre; // Iridium Ore item, currently not meta sensitive
+ iridiumPlate; // Iridium Plate item, currently not meta sensitive
+
+
+ // Metal Plates
+
+ platecopper; // Metal plate item, meta sensitive
+ platetin; // Metal plate item, meta sensitive
+ platebronze; // Metal plate item, meta sensitive
+ plategold; // Metal plate item, meta sensitive
+ plateiron; // Metal plate item, meta sensitive
+ platelead; // Metal plate item, meta sensitive
+ platelapi; // Metal plate item, meta sensitive
+ plateobsidian; // Metal plate item, meta sensitive
+ plateadviron; // Metal plate item, meta sensitive
+
+ // Metal Dense Plates
+ denseplatecopper; // Metal dense plate item, meta sensitive
+ denseplatetin; // Metal dense plate item, meta sensitive
+ denseplatebronze; // Metal dense plate item, meta sensitive
+ denseplategold; // Metal dense plate item, meta sensitive
+ denseplateiron; // Metal dense plate item, meta sensitive
+ @Deprecated
+ denseplateadviron; // Metal dense plate item, meta sensitive
+ denseplatelead; // Metal dense plate item, meta sensitive
+ denseplatelapi; // Metal dense plate item, meta sensitive
+ denseplateobsidian; // Metal dense plate item, meta sensitive
+
+
+ // upgrade modules
+ overclockerUpgrade; // overclocker upgrade item, meta sensitive
+ transformerUpgrade; // transformer upgrade item, meta sensitive
+ energyStorageUpgrade; // energy storage upgrade item, meta sensitive
+ ejectorUpgrade; // ejector upgrade item, meta sensitive
+
+ // misc
+ coin; // Coin item, currently not meta sensitive
+ reinforcedDoor; // Reinforced Door item, currently not meta sensitive
+ constructionFoamPowder; // Construction Foam Powder item, currently not meta sensitive
+ grinPowder; // Poisonous ingrident, currently not meta sensitive
+ debug; // Debug item, currently not meta sensitive
+ boatCarbon; // Carbon Fiber Canoe item, meta sensitive
+ boatRubber; // Rubber Dinghy item, meta sensitive
+ boatRubberBroken; // Damaged Rubber Dinghy item, meta sensitive
+ boatElectric; // Electric Boat item, meta sensitive
+
+ //Agriculture
+ cropSeed; // Crop seeds, stuff stored in NBT, don't use for crafting recipes!
+ cropnalyzer; // Cropnalyzer handheld device
+ fertilizer; // Basic IC2Item, used to provide nutrients toCropBlocks
+ hydratingCell; // Cell used to hydrate Crops, meta = Content, 0= Full, 9999 = Near empty
+ electricHoe; // Electric Hoe, Metadata indicates charge level
+ terraWart; // Mystic opposite of NEtherWart, cures StatusEffects, simply consumeable
+ weedEx; // Spraying can of WEED-EX, meta indicates usages left
+
+ //Boozeception
+ mugEmpty; // Simple stone mug
+ coffeeBeans; // Harvested CoffeeBeans
+ coffeePowder; // Processed Coffee Beans, used to craft drinkable Coffee
+ mugCoffee; // Mug of Coffee, Meta indicates status 0 = cold, 1 = Normal, 2 = Sugar'd
+ hops; // Hops, harvested freshly from crop
+ barrel; // Carried Barrel, metadata encrypts the information about the liquid inside
+ blockBarrel; // Unobtainable "placed barrel", TileEntity controlling the Fermentation process
+ mugBooze; // Mug filled with booze, metadata encrypts the information about the liquid inside
+
+ */
+
+ /**
+ * Get the base IC2 package name, used internally.
+ *
+ * @return IC2 package name, if unable to be determined defaults to ic2
+ */
+ private static String getPackage() {
+ Package pkg = IC2Items.class.getPackage();
+
+ if (pkg != null) {
+ String packageName = pkg.getName();
+
+ return packageName.substring(0, packageName.length() - ".api.item".length());
+ }
+
+ return "ic2";
+ }
+
+ private static Class<?> Ic2Items;
+}
+
diff --git a/src/api/java/ic2/api/item/ICustomDamageItem.java b/src/api/java/ic2/api/item/ICustomDamageItem.java
new file mode 100644
index 0000000..4470fa0
--- /dev/null
+++ b/src/api/java/ic2/api/item/ICustomDamageItem.java
@@ -0,0 +1,56 @@
+package ic2.api.item;
+
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.item.ItemStack;
+
+/**
+ * ICustomDamageItem allows items to have custom damage handling without the massive overhead of
+ * Item.getDamage() / Item.setDamage(), which turn ItemStack.getItemDamage into virtual calls.
+ *
+ * The custom damage value is typically stored in the item stacks nbt data.
+ *
+ * If an Item implements ICusomDamageItem, the normal ItemStack damage won't be changed by IC2.
+ * It's up to the implementer to potentially manipulate it for suitable visual effect on the
+ * rendered damage bar or to provide a suitable item renderer.
+ *
+ * Item.isDamageable() still applies.
+ *
+ * @author Player
+ */
+public interface ICustomDamageItem {
+ /**
+ * Retrieve the custom damage value for the supplied item stack.
+ *
+ * @param stack ItemStack to be queried.
+ * @return Custom damage value.
+ */
+ int getCustomDamage(ItemStack stack);
+
+ /**
+ * Retrieve the maximum custom damage value for the supplied item stack.
+ * @param stack ItemStack to be queried.
+ * @return Custom damage value limit.
+ */
+ int getMaxCustomDamage(ItemStack stack);
+
+ /**
+ * Set the custom damage value for the supplied item stack.
+ *
+ * @param stack ItemStack to be manipulated.
+ * @param damage New damage value.
+ */
+ void setCustomDamage(ItemStack stack, int damage);
+
+ /**
+ * Increase the custom damage value for the supplied item stack.
+ *
+ * It's up to the implementation to not apply any damage, e.g. based on some randomness or
+ * properties of src.
+ *
+ * @param stack ItemStack to be manipulated.
+ * @param damage Extra damage to be applied.
+ * @param src Entity damaging the item, may be null.
+ * @return true if damage was applied.
+ */
+ boolean applyCustomDamage(ItemStack stack, int damage, EntityLivingBase src);
+}
diff --git a/src/api/java/ic2/api/item/IDebuggable.java b/src/api/java/ic2/api/item/IDebuggable.java
new file mode 100644
index 0000000..404c7eb
--- /dev/null
+++ b/src/api/java/ic2/api/item/IDebuggable.java
@@ -0,0 +1,22 @@
+package ic2.api.item;
+
+
+/**
+ * Allows a tile entity to output a debug message when the debugItem is used on it.
+ * Suggestions by Myrathi
+ */
+public abstract interface IDebuggable {
+ /**
+ * Checks if the tile entity is in a state that can be debugged.
+ *
+ * @return True if the tile entity can be debugged
+ */
+ public abstract boolean isDebuggable();
+
+ /**
+ * Gets the debug text for the tile entity.
+ *
+ * @return The text that the debugItem should show
+ */
+ public abstract String getDebugText();
+}
diff --git a/src/api/java/ic2/api/item/IElectricItem.java b/src/api/java/ic2/api/item/IElectricItem.java
new file mode 100644
index 0000000..4f546d5
--- /dev/null
+++ b/src/api/java/ic2/api/item/IElectricItem.java
@@ -0,0 +1,57 @@
+package ic2.api.item;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Provides the ability to store energy on the implementing item.
+ *
+ * The item should have a maximum damage of 13.
+ */
+public interface IElectricItem {
+ /**
+ * Determine if the item can be used in a machine or as an armor part to supply energy.
+ *
+ * @return Whether the item can supply energy
+ */
+ boolean canProvideEnergy(ItemStack itemStack);
+
+ /**
+ * Get the item ID to use for a charge energy greater than 0.
+ *
+ * @return Item ID to use
+ */
+ Item getChargedItem(ItemStack itemStack);
+
+ /**
+ * Get the item ID to use for a charge energy of 0.
+ *
+ * @return Item ID to use
+ */
+ Item getEmptyItem(ItemStack itemStack);
+
+ /**
+ * Get the item's maximum charge energy in EU.
+ *
+ * @return Maximum charge energy
+ */
+ double getMaxCharge(ItemStack itemStack);
+
+ /**
+ * Get the item's tier, lower tiers can't send energy to higher ones.
+ *
+ * Batteries are Tier 1, Advanced Batteries are Tier 2, Energy Crystals are Tier 3, Lapotron
+ * Crystals are Tier 4.
+ *
+ * @return Item's tier
+ */
+ int getTier(ItemStack itemStack);
+
+ /**
+ * Get the item's transfer limit in EU per transfer operation.
+ *
+ * @return Transfer limit
+ */
+ double getTransferLimit(ItemStack itemStack);
+}
+
diff --git a/src/api/java/ic2/api/item/IElectricItemManager.java b/src/api/java/ic2/api/item/IElectricItemManager.java
new file mode 100644
index 0000000..5de43d7
--- /dev/null
+++ b/src/api/java/ic2/api/item/IElectricItemManager.java
@@ -0,0 +1,95 @@
+package ic2.api.item;
+
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.item.ItemStack;
+
+/**
+ * This interface specifies a manager to handle the various tasks for electric items.
+ *
+ * The default implementation does the following:
+ * - store and retrieve the charge
+ * - handle charging, taking amount, tier, transfer limit, canProvideEnergy and simulate into account
+ * - replace item IDs if appropriate (getChargedItemId() and getEmptyItemId())
+ * - update and manage the damage value for the visual charge indicator
+ *
+ * @note If you're implementing your own variant (ISpecialElectricItem), you can delegate to the
+ * default implementations through ElectricItem.rawManager. The default implementation is designed
+ * to minimize its dependency on its own constraints/structure and delegates most work back to the
+ * more atomic features in the gateway manager.
+ */
+public interface IElectricItemManager {
+ /**
+ * Charge an item with a specified amount of energy.
+ *
+ * @param itemStack electric item's stack
+ * @param amount amount of energy to charge in EU
+ * @param tier tier of the charging device, has to be at least as high as the item to charge
+ * @param ignoreTransferLimit ignore the transfer limit specified by getTransferLimit()
+ * @param simulate don't actually change the item, just determine the return value
+ * @return Energy transferred into the electric item
+ */
+ double charge(ItemStack stack, double amount, int tier, boolean ignoreTransferLimit, boolean simulate);
+
+ /**
+ * Discharge an item by a specified amount of energy
+ *
+ * @param itemStack electric item's stack
+ * @param amount amount of energy to discharge in EU
+ * @param tier tier of the discharging device, has to be at least as high as the item to discharge
+ * @param ignoreTransferLimit ignore the transfer limit specified by getTransferLimit()
+ * @param externally use the supplied item externally, i.e. to power something else as if it was a battery
+ * @param simulate don't actually discharge the item, just determine the return value
+ * @return Energy retrieved from the electric item
+ */
+ double discharge(ItemStack stack, double amount, int tier, boolean ignoreTransferLimit, boolean externally, boolean simulate);
+
+ /**
+ * Determine the charge level for the specified item.
+ *
+ * @param itemStack ItemStack containing the electric item
+ * @return charge level in EU
+ */
+ double getCharge(ItemStack stack);
+
+ /**
+ * Determine if the specified electric item has at least a specific amount of EU.
+ * This is supposed to be used in the item code during operation, for example if you want to implement your own electric item.
+ * BatPacks are not taken into account.
+ *
+ * @param itemStack electric item's stack
+ * @param amount minimum amount of energy required
+ * @return true if there's enough energy
+ */
+ boolean canUse(ItemStack stack, double amount);
+
+ /**
+ * Try to retrieve a specific amount of energy from an Item, and if applicable, a BatPack.
+ * This is supposed to be used in the item code during operation, for example if you want to implement your own electric item.
+ *
+ * @param itemStack electric item's stack
+ * @param amount amount of energy to discharge in EU
+ * @param entity entity holding the item
+ * @return true if the operation succeeded
+ */
+ boolean use(ItemStack stack, double amount, EntityLivingBase entity);
+
+ /**
+ * Charge an item from the BatPack a player is wearing.
+ * This is supposed to be used in the item code during operation, for example if you want to implement your own electric item.
+ * use() already contains this functionality.
+ *
+ * @param itemStack electric item's stack
+ * @param entity entity holding the item
+ */
+ void chargeFromArmor(ItemStack stack, EntityLivingBase entity);
+
+ /**
+ * Get the tool tip to display for electric items.
+ *
+ * @param itemStack ItemStack to determine the tooltip for
+ * @return tool tip string or null for none
+ */
+ String getToolTip(ItemStack stack);
+
+ // TODO: add tier getter
+}
diff --git a/src/api/java/ic2/api/item/IItemHudInfo.java b/src/api/java/ic2/api/item/IItemHudInfo.java
new file mode 100644
index 0000000..da97318
--- /dev/null
+++ b/src/api/java/ic2/api/item/IItemHudInfo.java
@@ -0,0 +1,24 @@
+package ic2.api.item;
+
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+
+public interface IItemHudInfo {
+ /*
+ Add Info to Nano- and Quantum-Suit Helm Hud
+ for itemStack
+
+ @Override
+ public List<String> getHudInfo(ItemStack itemStack) {
+ List<String> info = new LinkedList<String>();
+ info.add("i am a Cool Item");
+ info.add("and have Cool info");
+ return info;
+ }
+
+
+ */
+
+ public List<String> getHudInfo(ItemStack itemStack);
+}
diff --git a/src/api/java/ic2/api/item/IKineticRotor.java b/src/api/java/ic2/api/item/IKineticRotor.java
new file mode 100644
index 0000000..a61d162
--- /dev/null
+++ b/src/api/java/ic2/api/item/IKineticRotor.java
@@ -0,0 +1,23 @@
+package ic2.api.item;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.ResourceLocation;
+
+public interface IKineticRotor {
+ int getDiameter(ItemStack stack);
+
+ ResourceLocation getRotorRenderTexture(ItemStack stack);
+
+ float getEfficiency(ItemStack stack);
+
+ int getMinWindStrength(ItemStack stack);
+
+ int getMaxWindStrength(ItemStack stack);
+
+ boolean isAcceptedType(ItemStack stack, GearboxType type);
+
+ public static enum GearboxType {
+ WATER,
+ WIND,
+ }
+}
diff --git a/src/api/java/ic2/api/item/ILatheItem.java b/src/api/java/ic2/api/item/ILatheItem.java
new file mode 100644
index 0000000..45988c4
--- /dev/null
+++ b/src/api/java/ic2/api/item/ILatheItem.java
@@ -0,0 +1,70 @@
+package ic2.api.item;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.ResourceLocation;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+/**
+ * Interface used for Items that can be processed in the Turning Table
+ */
+public interface ILatheItem {
+
+ /**
+ * Returns the radius of this Lathe Item
+ * Should be no more than 15 to still be able to display it properly.
+ */
+ int getWidth(ItemStack stack);
+
+ /**
+ * Returns the current state of this Lathe Item
+ * The length of the array should be 5
+ * The value should be the current size of the item at this time.
+ * It should also not be greater than getWidth()
+ */
+ int[] getCurrentState(ItemStack stack);
+
+ /**
+ * This will set the current state of the Item on that position to that value.
+ */
+ void setState(ItemStack stack, int position, int value);
+
+ /**
+ * Returns the output Item (normally dust) when you lathe on this position.
+ */
+ ItemStack getOutputItem(ItemStack stack, int position);
+
+ /**
+ * How common it is if the above item will be put into the output slot.
+ * 1.0 means it will always be outputted, 0.0 means it will never be outputted.
+ */
+ float getOutputChance(ItemStack stack, int position);
+
+ /**
+ * Returns the ResourceLocation of the texture used to display the item inside of the Lathe GUI
+ */
+ @SideOnly(Side.CLIENT)
+ ResourceLocation getTexture(ItemStack stack);
+
+ /**
+ * This is similar to the Block HarvestLevel. Requires a different tool for a different hardness
+ * for ex. the Iron Turning Blank has a Hardness of 2 (Like Iron Ore (Harvest Level)).
+ * In this case however it requires a Tool hardness of one above (3)
+ */
+ int getHardness(ItemStack stack);
+
+ /**
+ * Interface used for Tools that can be used to modify {@link ILatheItem}
+ */
+ public static interface ILatheTool extends ICustomDamageItem {
+
+ /**
+ * This is similar to the Tool HarvestLevel. Requires a different tool for a different hardness
+ * for ex. the Iron Turning Blank has a Hardness of 2 (Like Iron Ore (Harvest Level)).
+ * The tool requires a hardness of one level above (in this case 3)
+ */
+ int getHardness(ItemStack stack);
+ }
+
+}
diff --git a/src/api/java/ic2/api/item/IMetalArmor.java b/src/api/java/ic2/api/item/IMetalArmor.java
new file mode 100644
index 0000000..16ffd24
--- /dev/null
+++ b/src/api/java/ic2/api/item/IMetalArmor.java
@@ -0,0 +1,20 @@
+package ic2.api.item;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Armor items implementing this can be considered metal armor.
+ *
+ * Currently used for determining which boots can be used to slide up a magnetic pole.
+ */
+public interface IMetalArmor {
+ /**
+ * Determine if the given armor piece is metal armor.
+ *
+ * @param itemstack Armor piece as worn by the player
+ * @param player The player
+ * @return Whether the armor piece is metal armor
+ */
+ public boolean isMetalArmor(ItemStack itemstack, EntityPlayer player);
+}
diff --git a/src/api/java/ic2/api/item/ISpecialElectricItem.java b/src/api/java/ic2/api/item/ISpecialElectricItem.java
new file mode 100644
index 0000000..a7302f5
--- /dev/null
+++ b/src/api/java/ic2/api/item/ISpecialElectricItem.java
@@ -0,0 +1,13 @@
+package ic2.api.item;
+
+import net.minecraft.item.ItemStack;
+
+public interface ISpecialElectricItem extends IElectricItem {
+ /**
+ * Supply a custom IElectricItemManager.
+ *
+ * @param itemStack ItemStack to get the manager for
+ * @return IElectricItemManager instance
+ */
+ IElectricItemManager getManager(ItemStack itemStack);
+}
diff --git a/src/api/java/ic2/api/item/ITerraformingBP.java b/src/api/java/ic2/api/item/ITerraformingBP.java
new file mode 100644
index 0000000..59b46ba
--- /dev/null
+++ b/src/api/java/ic2/api/item/ITerraformingBP.java
@@ -0,0 +1,34 @@
+package ic2.api.item;
+
+import net.minecraft.world.World;
+
+/**
+ * Allows an item to act as a terraformer blueprint.
+ */
+public interface ITerraformingBP {
+ /**
+ * Get the energy consumption per operation of the blueprint.
+ *
+ * @return Energy consumption in EU
+ */
+ public abstract int getConsume();
+
+ /**
+ * Get the maximum range of the blueprint.
+ * Should be a divisor of 5.
+ *
+ * @return Maximum range in blocks
+ */
+ public abstract int getRange();
+
+ /**
+ * Perform the terraforming operation.
+ *
+ * @param world world to terraform
+ * @param x X position to terraform
+ * @param z Z position to terraform
+ * @param yCoord Y position of the terraformer
+ * @return Whether the operation was successful and the terraformer should consume energy.
+ */
+ public abstract boolean terraform(World world, int x, int z, int yCoord);
+}
diff --git a/src/api/java/ic2/api/item/ItemWrapper.java b/src/api/java/ic2/api/item/ItemWrapper.java
new file mode 100644
index 0000000..58f8073
--- /dev/null
+++ b/src/api/java/ic2/api/item/ItemWrapper.java
@@ -0,0 +1,50 @@
+package ic2.api.item;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+
+/**
+ * Wrapper for inserting interfaces into items you don't own.
+ *
+ * @author Richard
+ */
+public class ItemWrapper {
+ private static final Multimap<Item, IBoxable> boxableItems = ArrayListMultimap.create();
+ private static final Multimap<Item, IMetalArmor> metalArmorItems = ArrayListMultimap.create();
+
+ public static void registerBoxable(Item item, IBoxable boxable) {
+ boxableItems.put(item, boxable);
+ }
+
+ public static boolean canBeStoredInToolbox(ItemStack stack) {
+ Item item = stack.getItem();
+ // use customs first to allow for overriding behavior
+ for (IBoxable boxable : boxableItems.get(item)) {
+ if (boxable.canBeStoredInToolbox(stack)) return true;
+ }
+
+ if (item instanceof IBoxable && ((IBoxable) item).canBeStoredInToolbox(stack)) return true;
+
+ return false;
+ }
+
+ public static void registerMetalArmor(Item item, IMetalArmor armor) {
+ metalArmorItems.put(item, armor);
+ }
+
+ public static boolean isMetalArmor(ItemStack stack, EntityPlayer player) {
+ Item item = stack.getItem();
+ // use customs first to allow for overriding behavior
+ for (IMetalArmor metalArmor : metalArmorItems.get(item)) {
+ if (metalArmor.isMetalArmor(stack, player)) return true;
+ }
+
+ if (item instanceof IMetalArmor && ((IMetalArmor) item).isMetalArmor(stack, player)) return true;
+
+ return false;
+ }
+}
diff --git a/src/api/java/ic2/api/item/package-info.java b/src/api/java/ic2/api/item/package-info.java
new file mode 100644
index 0000000..44ecdce
--- /dev/null
+++ b/src/api/java/ic2/api/item/package-info.java
@@ -0,0 +1,4 @@
+@API(apiVersion="1.0", owner="IC2", provides="IC2API")
+package ic2.api.item;
+import cpw.mods.fml.common.API;
+
diff --git a/src/api/java/ic2/api/network/INetworkClientTileEntityEventListener.java b/src/api/java/ic2/api/network/INetworkClientTileEntityEventListener.java
new file mode 100644
index 0000000..ed3ea38
--- /dev/null
+++ b/src/api/java/ic2/api/network/INetworkClientTileEntityEventListener.java
@@ -0,0 +1,17 @@
+package ic2.api.network;
+
+import net.minecraft.entity.player.EntityPlayer;
+
+/**
+ * Allows a tile entity to receive network events received from clients.
+ */
+public interface INetworkClientTileEntityEventListener {
+ /**
+ * Called when a network event is received.
+ *
+ * @param player client which sent the event
+ * @param event event ID
+ */
+ void onNetworkEvent(EntityPlayer player, int event);
+}
+
diff --git a/src/api/java/ic2/api/network/INetworkDataProvider.java b/src/api/java/ic2/api/network/INetworkDataProvider.java
new file mode 100644
index 0000000..c3f283e
--- /dev/null
+++ b/src/api/java/ic2/api/network/INetworkDataProvider.java
@@ -0,0 +1,18 @@
+package ic2.api.network;
+
+import java.util.List;
+
+/**
+ * Tile entities which want to synchronized specific fields between client and server have to implement this.
+ *
+ * The fields don't update themselves, a field update must be sent every time a synchronized field changes.
+ */
+public interface INetworkDataProvider {
+ /**
+ * Get the list of synchronized fields.
+ *
+ * @return Names of the synchronized fields
+ */
+ List<String> getNetworkedFields();
+}
+
diff --git a/src/api/java/ic2/api/network/INetworkItemEventListener.java b/src/api/java/ic2/api/network/INetworkItemEventListener.java
new file mode 100644
index 0000000..5f3228c
--- /dev/null
+++ b/src/api/java/ic2/api/network/INetworkItemEventListener.java
@@ -0,0 +1,19 @@
+package ic2.api.network;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Allows an item to receive network events received from the server.
+ */
+public interface INetworkItemEventListener {
+ /**
+ * Called when a network event is received.
+ *
+ * @param itemStack item stack
+ * @param player player containing the item
+ * @param event event ID
+ */
+ void onNetworkEvent(ItemStack stack, EntityPlayer player, int event);
+}
+
diff --git a/src/api/java/ic2/api/network/INetworkTileEntityEventListener.java b/src/api/java/ic2/api/network/INetworkTileEntityEventListener.java
new file mode 100644
index 0000000..fbb4753
--- /dev/null
+++ b/src/api/java/ic2/api/network/INetworkTileEntityEventListener.java
@@ -0,0 +1,14 @@
+package ic2.api.network;
+
+/**
+ * Allows a tile entity to receive network events received from the server.
+ */
+public interface INetworkTileEntityEventListener {
+ /**
+ * Called when a network event is received.
+ *
+ * @param event Event ID
+ */
+ void onNetworkEvent(int event);
+}
+
diff --git a/src/api/java/ic2/api/network/INetworkUpdateListener.java b/src/api/java/ic2/api/network/INetworkUpdateListener.java
new file mode 100644
index 0000000..09414ae
--- /dev/null
+++ b/src/api/java/ic2/api/network/INetworkUpdateListener.java
@@ -0,0 +1,14 @@
+package ic2.api.network;
+
+/**
+ * Allows a tile entity to receive field sync updates received from the server.
+ */
+public interface INetworkUpdateListener {
+ /**
+ * Called when a field is synchronized.
+ *
+ * @param field field synchronized
+ */
+ void onNetworkUpdate(String field);
+}
+
diff --git a/src/api/java/ic2/api/network/NetworkHelper.java b/src/api/java/ic2/api/network/NetworkHelper.java
new file mode 100644
index 0000000..a91d03a
--- /dev/null
+++ b/src/api/java/ic2/api/network/NetworkHelper.java
@@ -0,0 +1,194 @@
+package ic2.api.network;
+
+import java.lang.reflect.Method;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+
+/**
+ * Provides methods to initiate events and synchronize tile entity fields in SMP.
+ *
+ * The methods are transparent between singleplayer and multiplayer - if a method is called in
+ * singleplayer, the associated callback will be locally executed. The implementation is different
+ * between the client and server versions of IC2.
+ *
+ * You'll usually want to use the server->client methods defined here to synchronize information
+ * which is needed by the clients outside the GUI, such as rendering the block, playing sounds or
+ * producing effects. Anything which is only visible inside the GUI should be synchronized through
+ * the Container class associated to the GUI in Container.updateProgressBar().
+ */
+public final class NetworkHelper {
+ // server -> client
+
+
+ /**
+ * Schedule a TileEntity's field to be updated to the clients in range.
+ *
+ * The updater will query the field's value during the next update, updates happen usually
+ * every 2 ticks. If low latency is important use initiateTileEntityEvent instead.
+ *
+ * IC2's network updates have to get triggered every time, it doesn't continuously poll/send
+ * the field value. Just call updateTileEntityField after every change to a field which needs
+ * network synchronization.
+ *
+ * The following field data types are currently supported:
+ * - int, int[], short, short[], byte, byte[], long, long[]
+ * - float, float[], double, double[]
+ * - boolean, boolean[]
+ * - String, String[]
+ * - ItemStack
+ * - NBTBase (includes NBTTagCompound)
+ * - Block, Item, Achievement, Potion, Enchantment
+ * - ChunkCoordinates, ChunkCoordIntPair
+ * - TileEntity (does not sync the actual tile entity, instead looks up the tile entity by its position in the client world)
+ * - World (does not sync the actual world, instead looks up the world by its dimension ID)
+ *
+ * Once the update has been processed by the client, it'll call onNetworkUpdate on the client-
+ * side TileEntity if it implements INetworkUpdateListener.
+ *
+ * If this method is being executed on the client (i.e. Singleplayer), it'll just call
+ * INetworkUpdateListener.onNetworkUpdate (if implemented by the te).
+ *
+ * @param te TileEntity to update
+ * @param field Name of the field to update
+ */
+ public static void updateTileEntityField(TileEntity te, String field) {
+ try {
+ if (NetworkManager_updateTileEntityField == null) NetworkManager_updateTileEntityField = Class.forName(getPackage() + ".core.network.NetworkManager").getMethod("updateTileEntityField", TileEntity.class, String.class);
+ if (instance == null) instance = getInstance();
+
+ NetworkManager_updateTileEntityField.invoke(instance, te, field);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Immediately send an event for the specified TileEntity to the clients in range.
+ *
+ * If this method is being executed on the client (i.e. Singleplayer), it'll just call
+ * INetworkTileEntityEventListener.onNetworkEvent (if implemented by the te).
+ *
+ * @param te TileEntity to notify, should implement INetworkTileEntityEventListener
+ * @param event Arbitrary integer to represent the event, choosing the values is up to you
+ * @param limitRange Limit the notification range to (currently) 20 blocks instead of the
+ * tracking distance if true
+ */
+ public static void initiateTileEntityEvent(TileEntity te, int event, boolean limitRange) {
+ try {
+ if (NetworkManager_initiateTileEntityEvent == null) NetworkManager_initiateTileEntityEvent = Class.forName(getPackage() + ".core.network.NetworkManager").getMethod("initiateTileEntityEvent", TileEntity.class, Integer.TYPE, Boolean.TYPE);
+ if (instance == null) instance = getInstance();
+
+ NetworkManager_initiateTileEntityEvent.invoke(instance, te, event, limitRange);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Immediately send an event for the specified Item to the clients in range.
+ *
+ * The item should implement INetworkItemEventListener to receive the event.
+ *
+ * If this method is being executed on the client (i.e. Singleplayer), it'll just call
+ * INetworkItemEventListener.onNetworkEvent (if implemented by the item).
+ *
+ * @param player EntityPlayer holding the item
+ * @param itemStack ItemStack containing the item
+ * @param event Arbitrary integer to represent the event, choosing the values is up to you
+ * @param limitRange Limit the notification range to (currently) 20 blocks instead of the
+ * tracking distance if true
+ */
+ public static void initiateItemEvent(EntityPlayer player, ItemStack itemStack, int event, boolean limitRange) {
+ try {
+ if (NetworkManager_initiateItemEvent == null) NetworkManager_initiateItemEvent = Class.forName(getPackage() + ".core.network.NetworkManager").getMethod("initiateItemEvent", EntityPlayer.class, ItemStack.class, Integer.TYPE, Boolean.TYPE);
+ if (instance == null) instance = getInstance();
+
+ NetworkManager_initiateItemEvent.invoke(instance, player, itemStack, event, limitRange);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ // client -> server
+
+ /**
+ * Immediately send an event for the specified TileEntity to the server.
+ *
+ * This method doesn't do anything if executed on the server.
+ *
+ * @param te TileEntity to notify, should implement INetworkClientTileEntityEventListener
+ * @param event Arbitrary integer to represent the event, choosing the values is up to you
+ */
+ public static void initiateClientTileEntityEvent(TileEntity te, int event) {
+ try {
+ if (NetworkManager_initiateClientTileEntityEvent == null) NetworkManager_initiateClientTileEntityEvent = Class.forName(getPackage() + ".core.network.NetworkManager").getMethod("initiateClientTileEntityEvent", TileEntity.class, Integer.TYPE);
+ if (instance == null) instance = getInstance();
+
+ NetworkManager_initiateClientTileEntityEvent.invoke(instance, te, event);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Immediately send an event for the specified Item to the clients in range.
+ *
+ * The item should implement INetworkItemEventListener to receive the event.
+ *
+ * This method doesn't do anything if executed on the server.
+ *
+ * @param itemStack ItemStack containing the item
+ * @param event Arbitrary integer to represent the event, choosing the values is up to you
+ */
+ public static void initiateClientItemEvent(ItemStack itemStack, int event) {
+ try {
+ if (NetworkManager_initiateClientItemEvent == null) NetworkManager_initiateClientItemEvent = Class.forName(getPackage() + ".core.network.NetworkManager").getMethod("initiateClientItemEvent", ItemStack.class, Integer.TYPE);
+ if (instance == null) instance = getInstance();
+
+ NetworkManager_initiateClientItemEvent.invoke(instance, itemStack, event);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Get the base IC2 package name, used internally.
+ *
+ * @return IC2 package name, if unable to be determined defaults to ic2
+ */
+ private static String getPackage() {
+ Package pkg = NetworkHelper.class.getPackage();
+
+ if (pkg != null) {
+ String packageName = pkg.getName();
+
+ return packageName.substring(0, packageName.length() - ".api.network".length());
+ }
+
+ return "ic2";
+ }
+
+ /**
+ * Get the NetworkManager instance, used internally.
+ *
+ * @return NetworkManager instance
+ */
+ private static Object getInstance() {
+ try {
+ return Class.forName(getPackage() + ".core.util.SideGateway").getMethod("get").invoke(Class.forName(getPackage() + ".core.IC2").getDeclaredField("network").get(null));
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Object instance;
+ private static Method NetworkManager_updateTileEntityField;
+ private static Method NetworkManager_initiateTileEntityEvent;
+ private static Method NetworkManager_initiateItemEvent;
+ private static Method NetworkManager_initiateClientTileEntityEvent;
+ private static Method NetworkManager_initiateClientItemEvent;
+}
+
diff --git a/src/api/java/ic2/api/network/package-info.java b/src/api/java/ic2/api/network/package-info.java
new file mode 100644
index 0000000..25ea29e
--- /dev/null
+++ b/src/api/java/ic2/api/network/package-info.java
@@ -0,0 +1,4 @@
+@API(apiVersion="1.0", owner="IC2", provides="IC2API")
+package ic2.api.network;
+import cpw.mods.fml.common.API;
+
diff --git a/src/api/java/ic2/api/package-info.java b/src/api/java/ic2/api/package-info.java
new file mode 100644
index 0000000..e6f1522
--- /dev/null
+++ b/src/api/java/ic2/api/package-info.java
@@ -0,0 +1,4 @@
+@API(apiVersion="1.0", owner="IC2", provides="IC2API")
+package ic2.api;
+import cpw.mods.fml.common.API;
+
diff --git a/src/api/java/ic2/api/reactor/IReactor.java b/src/api/java/ic2/api/reactor/IReactor.java
new file mode 100644
index 0000000..55191cc
--- /dev/null
+++ b/src/api/java/ic2/api/reactor/IReactor.java
@@ -0,0 +1,158 @@
+package ic2.api.reactor;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.ChunkCoordinates;
+import net.minecraft.world.World;
+
+/**
+ * Interface implemented by the tile entity of nuclear reactors.
+ */
+public interface IReactor {
+ /**
+ * Get the reactor's position in the world.
+ *
+ * @return Position of the reactor
+ */
+ public ChunkCoordinates getPosition();
+
+ /**
+ * Get the reactor's corresponding world.
+ *
+ * @return The reactor's world
+ */
+ public World getWorld();
+
+ /**
+ * Get the reactor's heat.
+ *
+ * @return The reactor's heat
+ */
+ public int getHeat();
+
+ /**
+ * Set the reactor's heat.
+ *
+ * @param heat reactor heat
+ */
+ public void setHeat(int heat);
+
+ /**
+ * Increase the reactor's heat.
+ *
+ * Use negative values to decrease.
+ *
+ * @param amount amount of heat to add
+ * @return The reactor's heat after adding the specified amount
+ */
+ public int addHeat(int amount);
+
+ /**
+ * Get the reactor's maximum heat before exploding.
+ *
+ * @return Maximum heat value
+ */
+ public int getMaxHeat();
+
+ /**
+ * Set the reactor's stored maxHeat variable.
+ * Used by plating to increase the reactors MaxHeat capacity.
+ * Needs to be called during each cycle process.
+ */
+ public void setMaxHeat(int newMaxHeat);
+
+ /**
+ * add Heat to a EmitHeat Buffer
+ * for use in Reactor operation.. need to be use
+ * for all Componetents with self-cooling
+ * no more magic heat disappear
+ */
+
+
+ public void addEmitHeat(int heat);
+
+ /**
+ * Get's the reactor's HEM (Heat Effect Modifier)
+ * Basic value is 1.0F.
+ * Reducing the value causes a weakening/reduction of the heat-based sideeffects of reactors
+ * (F.e. water evaporation, melting, damaging entitys, etc)
+ *
+ * @return HEM
+ */
+ public float getHeatEffectModifier();
+
+ /**
+ * Set's the reactor's HEM
+ * Needs to be called during each cycle process.
+ */
+ public void setHeatEffectModifier(float newHEM);
+
+ /**
+ * Get the reactor's energy output.
+ *
+ * @return Energy output, not multiplied by the base EU/t value
+ */
+ public float getReactorEnergyOutput();
+
+ /**
+ * Get the reactor's energy output.
+ *
+ * @return Energy output, multiplied with base EU/t value and MainConfig.get().getFloat("balance/energy/generator/nuclear")
+ */
+ public double getReactorEUEnergyOutput();
+
+ /**
+ * Add's the given amount of energy to the Reactor's output.
+ *
+ * @return Energy output after adding the value, not multiplied by the base EU/t value
+ */
+ public float addOutput(float energy);
+
+ /**
+ * Get the item at the specified grid coordinates.
+ *
+ * @param x X position of the item, out of bounds returns null
+ * @param y Y position of the item, out of bounds returns null
+ * @return The item or null if there is no item
+ */
+ public ItemStack getItemAt(int x, int y);
+
+ /**
+ * Set the item at the specified grid coordinates.
+ *
+ * @param x X position of the item, out of bounds is a no-op
+ * @param y Y position of the item, out of bounds is a no-op
+ * @param item The item to set.
+ */
+ public void setItemAt(int x, int y, ItemStack item);
+
+ /**
+ * Explode the reactor.
+ */
+ public void explode();
+
+ /**
+ * Get the reactor's tick rate (game ticks per reactor tick).
+ *
+ * @return Tick rate
+ */
+ public int getTickRate();
+
+ /**
+ * Get whether the reactor is active and supposed to produce energy
+ * @return Whether the reactor is active
+ */
+ public boolean produceEnergy();
+
+ /**
+ * Set Redstone Signal without direct contact
+ *
+ */
+
+ public void setRedstoneSignal(boolean redstone);
+
+ /**
+ * @return true if the reactor is FluidCooled
+ */
+
+ public boolean isFluidCooled();
+}
diff --git a/src/api/java/ic2/api/reactor/IReactorChamber.java b/src/api/java/ic2/api/reactor/IReactorChamber.java
new file mode 100644
index 0000000..4611a6a
--- /dev/null
+++ b/src/api/java/ic2/api/reactor/IReactorChamber.java
@@ -0,0 +1,20 @@
+package ic2.api.reactor;
+
+/**
+ * Interface implemented by the reactor chamber tile entity.
+ */
+public interface IReactorChamber {
+ /**
+ * Get the chamber's reactor.
+ *
+ * @return The reactor
+ */
+ public IReactor getReactor();
+
+ /**
+ * Set Redstone Signal without direct contact
+ *
+ */
+
+ public void setRedstoneSignal(boolean redstone);
+}
diff --git a/src/api/java/ic2/api/reactor/IReactorComponent.java b/src/api/java/ic2/api/reactor/IReactorComponent.java
new file mode 100644
index 0000000..84e011c
--- /dev/null
+++ b/src/api/java/ic2/api/reactor/IReactorComponent.java
@@ -0,0 +1,98 @@
+package ic2.api.reactor;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * Design custom Reactor components by implementing this Interface
+ * Items implementing the interface will not be ejected from Reactors in their clean-up
+ * and can/will be interacted with by other elements, f.e. Uranium Cells.
+ *
+ * All IC2 ReactorComponents implement and use this Interface
+ *
+ */
+public interface IReactorComponent {
+ /**
+ * Called by reactor upon iterating through it's inventory (every cycle).
+ * Perform all necessary calculation/interaction here
+ *
+ * @param reactor Reference to the Reactor
+ * @param yourStack Reference to the specific instance of iterated ItemStack
+ * @param x X-coordinate of the stack in the grid
+ * @param y Y-coordinate of the stack in the grid
+ * @param heatrun every Stack will cycle 2 time (true, false) first run for heat, sec for Eu calculation
+ */
+ public void processChamber(IReactor reactor, ItemStack yourStack, int x, int y, boolean heatrun);
+
+ /**
+ * Can be called by Uranium-Components who attempt to generate energy by pulsing to other components.
+ * Uranium-Uranium interaction (f.e.) uses this method.
+ * @param reactor Reference to the Reactor
+ * @param yourStack Reference to the specific instance of called ItemStack
+ * @param pulsingStack Reference to the specific instance of pulsing ItemStack
+ * @param youX X-coordinate of your stack in the grid
+ * @param youY Y-coordinate of your stack in the grid
+ * @param pulseX X-coordinate of pulsing stack in the grid
+ * @param pulseY Y-coordinate of pulsing stack in the grid
+ * @param heatrun true for only create heat not EU, false for only EU not heat
+ * @return true if this component reacts to the pulse (and pulse is therefore meant to produce heat)
+ */
+ public boolean acceptUraniumPulse(IReactor reactor, ItemStack yourStack, ItemStack pulsingStack, int youX, int youY, int pulseX, int pulseY, boolean heatrun);
+
+ /**
+ * Called by components to determine whether your component can be heated.
+ * @param reactor Reference to the Reactor
+ * @param yourStack Reference to the specific instance of iterated ItemStack
+ * @param x X-coordinate of the stack in the grid
+ * @param y Y-coordinate of the stack in the grid
+ * @return true if your component can take heat
+ */
+ public boolean canStoreHeat(IReactor reactor, ItemStack yourStack, int x, int y);
+
+ /**
+ * Called by heat-switches to determine how much heat to distribute into which direction.
+ * Please return the maximum capacity of your heat-containing component here.
+ * @param reactor Reference to the Reactor
+ * @param yourStack Reference to the specific instance of iterated ItemStack
+ * @param x X-coordinate of the stack in the grid
+ * @param y Y-coordinate of the stack in the grid
+ * @return Maximum heat
+ */
+ public int getMaxHeat(IReactor reactor, ItemStack yourStack, int x, int y);
+
+ /**
+ * Called by heat-switches to determine how much heat to distribute into which direction.
+ * Please return the current amount of heat stored in this component
+ * @param reactor Reference to the Reactor
+ * @param yourStack Reference to the specific instance of iterated ItemStack
+ * @param x X-coordinate of the stack in the grid
+ * @param y Y-coordinate of the stack in the grid
+ * @return Current Heat
+ */
+ public int getCurrentHeat(IReactor reactor, ItemStack yourStack, int x, int y);
+
+ /**
+ * Called by components to distribute heat to your component.
+ * Perform heating-calculations and increase your heat (dmg) level accordingly.
+ * This method will as well be called to REDUCE heat, by providing a negative amount.
+ *
+ * @param reactor Reference to the Reactor
+ * @param yourStack Reference to the specific instance of iterated ItemStack
+ * @param x X-coordinate of the stack in the grid
+ * @param y Y-coordinate of the stack in the grid
+ * @param heat Amount of heat to be added (may be negative to subtract heat)
+ * @return 0 if the 'order' was accepted, return >0 to indicate the 'remaining' heat which couldn't be absorbed (and vice versa for <0)
+ */
+ public int alterHeat(IReactor reactor, ItemStack yourStack, int x, int y, int heat);
+
+ /**
+ * Called upon reactor explosion
+ * Alter the explosion size.
+ * Returning a float 0 < f < 1 will be counted as multiplier.
+ * Anything else will be counted as a flat addition (in case of <0 = reduction).
+ *
+ * @param reactor Reference to the Reactor
+ * @param yourStack Reference to the specific instance of iterated ItemStack
+ * @return your explosion modifier
+ */
+ public float influenceExplosion(IReactor reactor, ItemStack yourStack);
+}
diff --git a/src/api/java/ic2/api/reactor/package-info.java b/src/api/java/ic2/api/reactor/package-info.java
new file mode 100644
index 0000000..5194aee
--- /dev/null
+++ b/src/api/java/ic2/api/reactor/package-info.java
@@ -0,0 +1,4 @@
+@API(apiVersion="1.0", owner="IC2", provides="IC2API")
+package ic2.api.reactor;
+import cpw.mods.fml.common.API;
+
diff --git a/src/api/java/ic2/api/recipe/ICannerBottleRecipeManager.java b/src/api/java/ic2/api/recipe/ICannerBottleRecipeManager.java
new file mode 100644
index 0000000..415b3c3
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/ICannerBottleRecipeManager.java
@@ -0,0 +1,51 @@
+package ic2.api.recipe;
+
+import java.util.Map;
+
+import net.minecraft.item.ItemStack;
+
+public interface ICannerBottleRecipeManager {
+ /**
+ * Adds a recipe to the machine.
+ *
+ * @param container Container to be filled
+ * @param fill Item to fill into the container
+ * @param output Filled container
+ */
+ public void addRecipe(IRecipeInput container, IRecipeInput fill, ItemStack output);
+
+ /**
+ * Gets the recipe output for the given input.
+ *
+ * @param container Container to be filled
+ * @param fill Item to fill into the container
+ * @param adjustInput modify the input according to the recipe's requirements
+ * @param acceptTest allow either container or fill to be null to see if either of them is part of a recipe
+ * @return Recipe output, or null if none
+ */
+ public RecipeOutput getOutputFor(ItemStack container, ItemStack fill, boolean adjustInput, boolean acceptTest);
+
+ /**
+ * Gets a list of recipes.
+ *
+ * You're a mad evil scientist if you ever modify this.
+ *
+ * @return List of recipes
+ */
+ public Map<Input, RecipeOutput> getRecipes();
+
+
+ public static class Input {
+ public Input(IRecipeInput container1, IRecipeInput fill1) {
+ this.container = container1;
+ this.fill = fill1;
+ }
+
+ public boolean matches(ItemStack container1, ItemStack fill1) {
+ return this.container.matches(container1) && this.fill.matches(fill1);
+ }
+
+ public final IRecipeInput container;
+ public final IRecipeInput fill;
+ }
+}
diff --git a/src/api/java/ic2/api/recipe/ICannerEnrichRecipeManager.java b/src/api/java/ic2/api/recipe/ICannerEnrichRecipeManager.java
new file mode 100644
index 0000000..5eba1b6
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/ICannerEnrichRecipeManager.java
@@ -0,0 +1,54 @@
+package ic2.api.recipe;
+
+import java.util.Map;
+
+import net.minecraft.item.ItemStack;
+
+import net.minecraftforge.fluids.FluidStack;
+
+public interface ICannerEnrichRecipeManager {
+ /**
+ * Adds a recipe to the machine.
+ *
+ * @param input Fluid input
+ * @param additive Item to enrich the fluid with
+ * @param output Output fluid
+ */
+ public void addRecipe(FluidStack input, IRecipeInput additive, FluidStack output);
+
+ /**
+ * Gets the recipe output for the given input.
+ *
+ * @param input Fluid input
+ * @param additive Item to enrich the fluid with
+ * @param adjustInput modify the input according to the recipe's requirements
+ * @param acceptTest allow input or additive to be null to see if either of them is part of a recipe
+ * @return Recipe output, or null if none, output fluid in nbt
+ */
+ public RecipeOutput getOutputFor(FluidStack input, ItemStack additive, boolean adjustInput, boolean acceptTest);
+
+ /**
+ * Gets a list of recipes.
+ *
+ * You're a mad evil scientist if you ever modify this.
+ *
+ * @return List of recipes
+ */
+ public Map<Input, FluidStack> getRecipes();
+
+
+ public static class Input {
+ public Input(FluidStack fluid1, IRecipeInput additive1) {
+ this.fluid = fluid1;
+ this.additive = additive1;
+ }
+
+ public boolean matches(FluidStack fluid1, ItemStack additive1) {
+ return (this.fluid == null || this.fluid.isFluidEqual(fluid1)) &&
+ this.additive.matches(additive1);
+ }
+
+ public final FluidStack fluid;
+ public final IRecipeInput additive;
+ }
+}
diff --git a/src/api/java/ic2/api/recipe/ICraftingRecipeManager.java b/src/api/java/ic2/api/recipe/ICraftingRecipeManager.java
new file mode 100644
index 0000000..756eaf6
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/ICraftingRecipeManager.java
@@ -0,0 +1,26 @@
+package ic2.api.recipe;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * Recipe manager interface for crafting recipes.
+ *
+ * @author Richard
+ */
+public interface ICraftingRecipeManager {
+ /**
+ * Adds a shaped crafting recipe.
+ *
+ * @param output Recipe output
+ * @param input Recipe input format
+ */
+ public void addRecipe(ItemStack output, Object... input);
+
+ /**
+ * Adds a shapeless crafting recipe.
+ *
+ * @param output Recipe output
+ * @param input Recipe input
+ */
+ public void addShapelessRecipe(ItemStack output, Object... input);
+}
diff --git a/src/api/java/ic2/api/recipe/IFluidHeatManager.java b/src/api/java/ic2/api/recipe/IFluidHeatManager.java
new file mode 100644
index 0000000..7ddecdc
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/IFluidHeatManager.java
@@ -0,0 +1,32 @@
+package ic2.api.recipe;
+
+import java.util.Map;
+
+import net.minecraftforge.fluids.Fluid;
+
+
+public interface IFluidHeatManager extends ILiquidAcceptManager {
+ /**
+ * Add a new fluid to the Fluid Heat Generator.
+ *
+ * @param fluidName the fluid to burn
+ * @param amount amount of fluid to consume per tick
+ * @param heat amount of heat generated per tick
+ */
+ void addFluid(String fluidName, int amount, int heat);
+
+ BurnProperty getBurnProperty(Fluid fluid);
+
+ Map<String, BurnProperty> getBurnProperties();
+
+
+ public static class BurnProperty {
+ public BurnProperty(int amount1, int heat1) {
+ this.amount = amount1;
+ this.heat = heat1;
+ }
+
+ public final int amount;
+ public final int heat;
+ }
+}
diff --git a/src/api/java/ic2/api/recipe/ILiquidAcceptManager.java b/src/api/java/ic2/api/recipe/ILiquidAcceptManager.java
new file mode 100644
index 0000000..4f377b9
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/ILiquidAcceptManager.java
@@ -0,0 +1,10 @@
+package ic2.api.recipe;
+
+import java.util.Set;
+
+import net.minecraftforge.fluids.Fluid;
+
+public interface ILiquidAcceptManager {
+ boolean acceptsFluid(Fluid fluid);
+ Set<Fluid> getAcceptedFluids();
+}
diff --git a/src/api/java/ic2/api/recipe/ILiquidHeatExchangerManager.java b/src/api/java/ic2/api/recipe/ILiquidHeatExchangerManager.java
new file mode 100644
index 0000000..68decbb
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/ILiquidHeatExchangerManager.java
@@ -0,0 +1,39 @@
+package ic2.api.recipe;
+
+import java.util.Map;
+
+import net.minecraftforge.fluids.Fluid;
+
+public interface ILiquidHeatExchangerManager extends ILiquidAcceptManager {
+
+ /**
+ * Add a new fluid heatup/cooldown recipe.
+ *
+ * @param fluidName the fluid to heat up/cool down
+ * @param fluidOutput the fluid the above fluid turns into
+ * @param huPerMB the Thermal Energy difference in hU for the conversion of one mB fluid
+ */
+ void addFluid(String fluidName, String fluidOutput, int huPerMB);
+
+ HeatExchangeProperty getHeatExchangeProperty(Fluid fluid);
+
+ Map<String, HeatExchangeProperty> getHeatExchangeProperties();
+
+ /**
+ * This returns an ILiquidAcceptManager that only accepts fluids, that can be heated up / cooled down, but not both.
+ * You can basically use this to check if the liquid resulting from this conversion can be reprocessed into the first one.
+ * @return Returns the SingleDirectionManager.
+ */
+ ILiquidAcceptManager getSingleDirectionLiquidManager();
+
+ public static class HeatExchangeProperty {
+ public HeatExchangeProperty(Fluid outputFluid, int huPerMB) {
+ this.outputFluid = outputFluid;
+ this.huPerMB = huPerMB;
+ }
+
+ public final Fluid outputFluid;
+ public final int huPerMB;
+ }
+
+}
diff --git a/src/api/java/ic2/api/recipe/IListRecipeManager.java b/src/api/java/ic2/api/recipe/IListRecipeManager.java
new file mode 100644
index 0000000..adc2291
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/IListRecipeManager.java
@@ -0,0 +1,41 @@
+package ic2.api.recipe;
+
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * Recipe manager interface for basic lists.
+ *
+ * @author Richard
+ */
+public interface IListRecipeManager extends Iterable<IRecipeInput> {
+ /**
+ * Adds a stack to the list.
+ *
+ * @param stack Stack to add
+ */
+ public void add(IRecipeInput input);
+
+ /**
+ * Checks whether the specified stack is in the list.
+ *
+ * @param stack Stack to check
+ * @return Whether the stack is in the list
+ */
+ public boolean contains(ItemStack stack);
+
+ /**
+ * @return if the List is Empty
+ */
+ public boolean isEmpty();
+
+ /**
+ * Gets the list of stacks.
+ *
+ * You're a mad evil scientist if you ever modify this.
+ *
+ * @return List of stacks
+ */
+ public List<IRecipeInput> getInputs();
+}
diff --git a/src/api/java/ic2/api/recipe/IMachineRecipeManager.java b/src/api/java/ic2/api/recipe/IMachineRecipeManager.java
new file mode 100644
index 0000000..d2d4382
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/IMachineRecipeManager.java
@@ -0,0 +1,44 @@
+package ic2.api.recipe;
+
+import java.util.Map;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+/**
+ * Recipe manager interface for basic machines.
+ *
+ * @author RichardG, Player
+ */
+public interface IMachineRecipeManager { // TODO: merge with IMachineRecipeManagerExt
+ /**
+ * Adds a recipe to the machine.
+ *
+ * @param input Recipe input
+ * @param metadata meta data for additional recipe properties, may be null
+ * @param outputs Recipe outputs, zero or more depending on the machine
+ *
+ * For the thermal centrifuge @param metadata meta data {"minHeat": 1-xxx}
+ * For the ore washing plant @param metadata meta data {"amount": 1-8000}
+ *
+ */
+ public void addRecipe(IRecipeInput input, NBTTagCompound metadata, ItemStack... outputs);
+
+ /**
+ * Gets the recipe output for the given input.
+ *
+ * @param input Recipe input
+ * @param adjustInput modify the input according to the recipe's requirements
+ * @return Recipe output, or null if none
+ */
+ public RecipeOutput getOutputFor(ItemStack input, boolean adjustInput);
+
+ /**
+ * Gets a list of recipes.
+ *
+ * You're a mad evil scientist if you ever modify this.
+ *
+ * @return List of recipes
+ */
+ public Map<IRecipeInput, RecipeOutput> getRecipes(); // TODO: Change to iterable/collection/list
+}
diff --git a/src/api/java/ic2/api/recipe/IMachineRecipeManagerExt.java b/src/api/java/ic2/api/recipe/IMachineRecipeManagerExt.java
new file mode 100644
index 0000000..9c68706
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/IMachineRecipeManagerExt.java
@@ -0,0 +1,22 @@
+package ic2.api.recipe;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+public interface IMachineRecipeManagerExt extends IMachineRecipeManager {
+ /**
+ * Adds a recipe to the machine.
+ *
+ * @note Overwrite is only as reliable as IRecipeInput.getInputs().
+ *
+ * @param input Recipe input
+ * @param metadata Meta data for additional recipe properties, may be null.
+ * @param overwrite Replace an existing recipe, not recommended, may be ignored.
+ * @param outputs Recipe outputs, zero or more depending on the machine.
+ * @return true on success, false otherwise, e.g. on conflicts.
+ *
+ * For the thermal centrifuge @param metadata meta data {"minHeat": 1-xxx}
+ * For the ore washing plant @param metadata meta data {"amount": 1-8000}
+ */
+ public boolean addRecipe(IRecipeInput input, NBTTagCompound metadata, boolean overwrite, ItemStack... outputs);
+}
diff --git a/src/api/java/ic2/api/recipe/IPatternStorage.java b/src/api/java/ic2/api/recipe/IPatternStorage.java
new file mode 100644
index 0000000..c9dbd8d
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/IPatternStorage.java
@@ -0,0 +1,11 @@
+package ic2.api.recipe;
+
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+
+public interface IPatternStorage {
+ boolean addPattern(ItemStack itemstack);
+
+ List<ItemStack> getPatterns();
+}
diff --git a/src/api/java/ic2/api/recipe/IRecipeInput.java b/src/api/java/ic2/api/recipe/IRecipeInput.java
new file mode 100644
index 0000000..0c842d6
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/IRecipeInput.java
@@ -0,0 +1,31 @@
+package ic2.api.recipe;
+
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+
+public interface IRecipeInput {
+ /**
+ * Check if subject matches this recipe input, ignoring the amount.
+ *
+ * @param subject ItemStack to check
+ * @return true if it matches the requirement
+ */
+ boolean matches(ItemStack subject);
+
+ /**
+ * Determine the minimum input stack size.
+ *
+ * @return input amount required
+ */
+ int getAmount();
+
+ /**
+ * List all possible inputs (best effort).
+ *
+ * The stack size is undefined, use getAmount to get the correct one.
+ *
+ * @return list of inputs, may be incomplete
+ */
+ List<ItemStack> getInputs();
+}
diff --git a/src/api/java/ic2/api/recipe/IScrapboxManager.java b/src/api/java/ic2/api/recipe/IScrapboxManager.java
new file mode 100644
index 0000000..41b694d
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/IScrapboxManager.java
@@ -0,0 +1,13 @@
+package ic2.api.recipe;
+
+import java.util.Map;
+
+import net.minecraft.item.ItemStack;
+
+public interface IScrapboxManager {
+ void addDrop(ItemStack drop, float rawChance);
+
+ ItemStack getDrop(ItemStack input, boolean adjustInput);
+
+ Map<ItemStack, Float> getDrops();
+}
diff --git a/src/api/java/ic2/api/recipe/ISemiFluidFuelManager.java b/src/api/java/ic2/api/recipe/ISemiFluidFuelManager.java
new file mode 100644
index 0000000..87c3427
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/ISemiFluidFuelManager.java
@@ -0,0 +1,32 @@
+package ic2.api.recipe;
+
+import java.util.Map;
+
+import net.minecraftforge.fluids.Fluid;
+
+
+public interface ISemiFluidFuelManager extends ILiquidAcceptManager {
+ /**
+ * Add a new fluid to the semi fluid generator.
+ *
+ * @param fluidName the fluid to burn
+ * @param amount amount of fluid to consume per tick
+ * @param power amount of energy generated per tick
+ */
+ void addFluid(String fluidName, int amount, double power);
+
+ BurnProperty getBurnProperty(Fluid fluid);
+
+ Map<String, BurnProperty> getBurnProperties();
+
+
+ public static class BurnProperty {
+ public BurnProperty(int amount1, double power1) {
+ this.amount = amount1;
+ this.power = power1;
+ }
+
+ public final int amount;
+ public final double power;
+ }
+}
diff --git a/src/api/java/ic2/api/recipe/RecipeInputFluidContainer.java b/src/api/java/ic2/api/recipe/RecipeInputFluidContainer.java
new file mode 100644
index 0000000..103d293
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/RecipeInputFluidContainer.java
@@ -0,0 +1,54 @@
+package ic2.api.recipe;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidContainerRegistry;
+import net.minecraftforge.fluids.FluidContainerRegistry.FluidContainerData;
+import net.minecraftforge.fluids.FluidStack;
+
+public class RecipeInputFluidContainer implements IRecipeInput {
+ public RecipeInputFluidContainer(Fluid fluid) {
+ this(fluid, FluidContainerRegistry.BUCKET_VOLUME);
+ }
+
+ public RecipeInputFluidContainer(Fluid fluid, int amount) {
+ this.fluid = fluid;
+ this.amount = amount;
+ }
+
+ @Override
+ public boolean matches(ItemStack subject) {
+ FluidStack fs = FluidContainerRegistry.getFluidForFilledItem(subject);
+ if (fs == null) return false;
+
+ return fs.getFluid() == fluid;
+ }
+
+ @Override
+ public int getAmount() {
+ return amount;
+ }
+
+ @Override
+ public List<ItemStack> getInputs() {
+ List<ItemStack> ret = new ArrayList<ItemStack>();
+
+ for (FluidContainerData data : FluidContainerRegistry.getRegisteredFluidContainerData()) {
+ if (data.fluid.getFluid() == fluid) ret.add(data.filledContainer);
+ }
+
+ return ret;
+ }
+
+ @Override
+ public String toString() {
+ return "RInputFluidContainer<"+amount+"x"+fluid.getName()+">";
+ }
+
+ public final Fluid fluid;
+ public final int amount;
+}
diff --git a/src/api/java/ic2/api/recipe/RecipeInputItemStack.java b/src/api/java/ic2/api/recipe/RecipeInputItemStack.java
new file mode 100644
index 0000000..84f86ea
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/RecipeInputItemStack.java
@@ -0,0 +1,47 @@
+package ic2.api.recipe;
+
+import java.util.Arrays;
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+
+import net.minecraftforge.oredict.OreDictionary;
+
+public class RecipeInputItemStack implements IRecipeInput {
+ public RecipeInputItemStack(ItemStack aInput) {
+ this(aInput, aInput.stackSize);
+ }
+
+ public RecipeInputItemStack(ItemStack aInput, int aAmount) {
+ if (aInput.getItem() == null) throw new IllegalArgumentException("Invalid item stack specfied");
+
+ input = aInput.copy(); // Never forget to copy.
+ amount = aAmount;
+ }
+
+ @Override
+ public boolean matches(ItemStack subject) {
+ return subject.getItem() == input.getItem() &&
+ (subject.getItemDamage() == input.getItemDamage() || input.getItemDamage() == OreDictionary.WILDCARD_VALUE);
+ }
+
+ @Override
+ public int getAmount() {
+ return amount;
+ }
+
+ @Override
+ public List<ItemStack> getInputs() {
+ return Arrays.asList(input);
+ }
+
+ @Override
+ public String toString() {
+ ItemStack stack = input.copy();
+ input.stackSize = amount;
+ return "RInputItemStack<"+stack+">";
+ }
+
+ public final ItemStack input;
+ public final int amount;
+}
diff --git a/src/api/java/ic2/api/recipe/RecipeInputOreDict.java b/src/api/java/ic2/api/recipe/RecipeInputOreDict.java
new file mode 100644
index 0000000..e6e4695
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/RecipeInputOreDict.java
@@ -0,0 +1,104 @@
+package ic2.api.recipe;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+import net.minecraftforge.oredict.OreDictionary;
+
+public class RecipeInputOreDict implements IRecipeInput {
+ public RecipeInputOreDict(String input1) {
+ this(input1, 1);
+ }
+
+ public RecipeInputOreDict(String input1, int amount1) {
+ this(input1, amount1, null);
+ }
+
+ public RecipeInputOreDict(String input1, int amount1, Integer meta) {
+ this.input = input1;
+ this.amount = amount1;
+ this.meta = meta;
+ }
+
+ @Override
+ public boolean matches(ItemStack subject) {
+ List<ItemStack> inputs = getOres();
+ boolean useOreStackMeta = (meta == null);
+ Item subjectItem = subject.getItem();
+ int subjectMeta = subject.getItemDamage();
+
+ for (ItemStack oreStack : inputs) {
+ Item oreItem = oreStack.getItem();
+ if (oreItem == null) continue; // ignore invalid
+
+ int metaRequired = useOreStackMeta ? oreStack.getItemDamage() : meta;
+
+ if (subjectItem == oreItem &&
+ (subjectMeta == metaRequired || metaRequired == OreDictionary.WILDCARD_VALUE)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public int getAmount() {
+ return amount;
+ }
+
+ @Override
+ public List<ItemStack> getInputs() {
+ List<ItemStack> ores = getOres();
+
+ // check if we have to filter the list first
+ boolean hasInvalidEntries = false;
+
+ for (ItemStack stack : ores) {
+ if (stack.getItem() == null) {
+ hasInvalidEntries = true;
+ break;
+ }
+ }
+
+ if (!hasInvalidEntries) return ores;
+
+ List<ItemStack> ret = new ArrayList<ItemStack>(ores.size());
+
+ for (ItemStack stack : ores) {
+ if (stack.getItem() != null) ret.add(stack); // ignore invalid
+ }
+
+ return Collections.unmodifiableList(ret);
+ }
+
+ @Override
+ public String toString() {
+ if (meta == null) {
+ return "RInputOreDict<"+amount+"x"+input+">";
+ } else {
+ return "RInputOreDict<"+amount+"x"+input+"@"+meta+">";
+ }
+ }
+
+ private List<ItemStack> getOres() {
+ if (ores != null) return ores;
+
+ // cache the ore list by making use of the fact that forge always uses the same list,
+ // unless it's EMPTY_LIST, which should never happen.
+ List<ItemStack> ret = OreDictionary.getOres(input);
+
+ if (ret != OreDictionary.EMPTY_LIST) ores = ret;
+
+ return ret;
+ }
+
+ public final String input;
+ public final int amount;
+ public final Integer meta;
+ private List<ItemStack> ores;
+}
diff --git a/src/api/java/ic2/api/recipe/RecipeOutput.java b/src/api/java/ic2/api/recipe/RecipeOutput.java
new file mode 100644
index 0000000..80ad5fb
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/RecipeOutput.java
@@ -0,0 +1,53 @@
+package ic2.api.recipe;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+public final class RecipeOutput {
+ public RecipeOutput(NBTTagCompound metadata1, List<ItemStack> items1) {
+ assert !items1.contains(null);
+
+ this.metadata = metadata1;
+ this.items = items1;
+ }
+
+ public RecipeOutput(NBTTagCompound metadata1, ItemStack... items1) {
+ this(metadata1, Arrays.asList(items1));
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof RecipeOutput) {
+ RecipeOutput ro = (RecipeOutput) obj;
+
+ if (items.size() == ro.items.size() &&
+ (metadata == null && ro.metadata == null || metadata != null && ro.metadata != null && metadata.equals(ro.metadata))) {
+ Iterator<ItemStack> itA = items.iterator();
+ Iterator<ItemStack> itB = ro.items.iterator();
+
+ while (itA.hasNext() && itB.hasNext()) {
+ ItemStack stackA = itA.next();
+ ItemStack stackB = itB.next();
+
+ if (ItemStack.areItemStacksEqual(stackA, stackB)) return false;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "ROutput<"+items+","+metadata+">";
+ }
+
+ public final List<ItemStack> items;
+ public final NBTTagCompound metadata;
+}
diff --git a/src/api/java/ic2/api/recipe/Recipes.java b/src/api/java/ic2/api/recipe/Recipes.java
new file mode 100644
index 0000000..0aa0b75
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/Recipes.java
@@ -0,0 +1,68 @@
+package ic2.api.recipe;
+
+
+/**
+ * General recipe registry.
+ *
+ * @author Richard
+ */
+public class Recipes {
+ public static IMachineRecipeManager macerator;
+ public static IMachineRecipeManager extractor;
+ public static IMachineRecipeManager compressor;
+ public static IMachineRecipeManager centrifuge;
+ public static IMachineRecipeManager blockcutter;
+ public static IMachineRecipeManager blastfurance;
+ public static IMachineRecipeManager recycler;
+ public static IMachineRecipeManager metalformerExtruding;
+ public static IMachineRecipeManager metalformerCutting;
+ public static IMachineRecipeManager metalformerRolling;
+ public static IMachineRecipeManager oreWashing;
+ public static ICannerBottleRecipeManager cannerBottle;
+ public static ICannerEnrichRecipeManager cannerEnrich;
+
+ /**
+ * Reference amplifier values:
+ *
+ * 5000: Scrap
+ * 45000: Scrapbox
+ *
+ * As Parameter for the Amplification Value you have to use the NBTTagCompound
+ *
+ * NBTTagCompound nbt = new NBTTagCompound();
+ * nbt.setInteger("amplification", aValue);
+ * matterAmplifier.addRecipe(yourStack, nbt);
+ */
+ public static IMachineRecipeManager matterAmplifier;
+ /**
+ * Reference scrap box chance values:
+ *
+ * 0.1: Diamond
+ * 0.5: Cake, Gold Helmet, Iron Ore, Gold Ore
+ * 1.0: Wooden tools, Soul Sand, Sign, Leather, Feather, Bone
+ * 1.5: Apple, Bread
+ * 2.0: Netherrack, Rotten Flesh
+ * 3.0: Grass, Gravel
+ * 4.0: Stick
+ * 5.0: Dirt, Wooden Hoe
+ */
+ public static IScrapboxManager scrapboxDrops;
+ public static IListRecipeManager recyclerBlacklist;
+ /**
+ * Do not add anything to this Whitelist. This is for Configuration only.
+ * You may need this if you have an own Recycler in your Mod, just to check if something can be recycled. but don't add anything to this List
+ */
+ public static IListRecipeManager recyclerWhitelist;
+ public static ICraftingRecipeManager advRecipes;
+
+ public static ISemiFluidFuelManager semiFluidGenerator;
+ public static IFluidHeatManager FluidHeatGenerator;
+ /**
+ * Used by the Liquid Heat Exchanger to cool down liquids and determine the amount of hu generated for every mb.
+ */
+ public static ILiquidHeatExchangerManager liquidCooldownManager;
+ /**
+ * Opposite of {@link #liquidCooldownManager}. This is for Liquids that can be heated up again.
+ */
+ public static ILiquidHeatExchangerManager liquidHeatupManager;
+}
diff --git a/src/api/java/ic2/api/recipe/package-info.java b/src/api/java/ic2/api/recipe/package-info.java
new file mode 100644
index 0000000..44e70e7
--- /dev/null
+++ b/src/api/java/ic2/api/recipe/package-info.java
@@ -0,0 +1,4 @@
+@API(apiVersion="1.0", owner="IC2", provides="IC2API")
+package ic2.api.recipe;
+import cpw.mods.fml.common.API;
+
diff --git a/src/api/java/ic2/api/tile/ExplosionWhitelist.java b/src/api/java/ic2/api/tile/ExplosionWhitelist.java
new file mode 100644
index 0000000..fa4a474
--- /dev/null
+++ b/src/api/java/ic2/api/tile/ExplosionWhitelist.java
@@ -0,0 +1,47 @@
+package ic2.api.tile;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import net.minecraft.block.Block;
+
+/**
+ * Blocks on this whitelist will not resist an explosion but won't be destroyed.
+ *
+ * The explosion code by default ignores blocks which absorb more than 1000 explosion power to
+ * prevent abusing personal safes, Trade-O-Mats and other blocks to serve as a cheap and
+ * invulnerable reactor chambers. Said blocks will not shield the explosion and won't get
+ * destroyed.
+ */
+public final class ExplosionWhitelist {
+ /**
+ * Add a block to the whitelist.
+ *
+ * @param block block to add
+ */
+ public static void addWhitelistedBlock(Block block) {
+ whitelist.add(block);
+ }
+
+ /**
+ * Remove a block from the whitelist.
+ *
+ * @param block block to remove
+ */
+ public static void removeWhitelistedBlock(Block block) {
+ whitelist.remove(block);
+ }
+
+ /**
+ * Check if a block is on the whitelist.
+ *
+ * @param block block to check if whitelisted
+ * @return Whether the block is whitelisted
+ */
+ public static boolean isBlockWhitelisted(Block block) {
+ return whitelist.contains(block);
+ }
+
+ private static Set<Block> whitelist = new HashSet<Block>();
+}
+
diff --git a/src/api/java/ic2/api/tile/IEnergyStorage.java b/src/api/java/ic2/api/tile/IEnergyStorage.java
new file mode 100644
index 0000000..68f4270
--- /dev/null
+++ b/src/api/java/ic2/api/tile/IEnergyStorage.java
@@ -0,0 +1,61 @@
+package ic2.api.tile;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Interface implemented by the tile entity of energy storage blocks.
+ */
+public interface IEnergyStorage {
+ /**
+ * Get the amount of energy currently stored in the block.
+ *
+ * @return Energy stored in the block
+ */
+ public int getStored();
+
+ /**
+ * Set the amount of energy currently stored in the block.
+ *
+ * @param energy stored energy
+ */
+ public void setStored(int energy);
+
+ /**
+ * Add the specified amount of energy.
+ *
+ * Use negative values to decrease.
+ *
+ * @param amount of energy to add
+ * @return Energy stored in the block after adding the specified amount
+ */
+ public int addEnergy(int amount);
+
+ /**
+ * Get the maximum amount of energy the block can store.
+ *
+ * @return Maximum energy stored
+ */
+ public int getCapacity();
+
+ /**
+ * Get the block's energy output.
+ *
+ * @return Energy output in EU/t
+ */
+ public int getOutput();
+
+ /**
+ * Get the block's energy output.
+ *
+ * @return Energy output in EU/t
+ */
+ public double getOutputEnergyUnitsPerTick();
+
+ /**
+ * Get whether this block can have its energy used by an adjacent teleporter.
+ *
+ * @param side side the teleporter is draining energy from
+ * @return Whether the block is teleporter compatible
+ */
+ public boolean isTeleporterCompatible(ForgeDirection side);
+}
diff --git a/src/api/java/ic2/api/tile/IWrenchable.java b/src/api/java/ic2/api/tile/IWrenchable.java
new file mode 100644
index 0000000..8fcbea5
--- /dev/null
+++ b/src/api/java/ic2/api/tile/IWrenchable.java
@@ -0,0 +1,61 @@
+package ic2.api.tile;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Allows a tile entity to make use of the wrench's removal and rotation functions.
+ */
+public interface IWrenchable {
+ /**
+ * Determine if the wrench can be used to set the block's facing.
+ * Called before wrenchCanRemove().
+ *
+ * @param entityPlayer player using the wrench, may be null
+ * @param side block's side the wrench was clicked on
+ * @return Whether the wrenching was done and the wrench should be damaged
+ */
+ boolean wrenchCanSetFacing(EntityPlayer entityPlayer, int side);
+
+ /**
+ * Get the block's facing.
+ *
+ * @return Block facing
+ */
+ short getFacing();
+
+ /**
+ * Set the block's facing
+ *
+ * @param facing facing to set the block to
+ */
+ void setFacing(short facing);
+
+ /**
+ * Determine if the wrench can be used to remove the block.
+ * Called if wrenchSetFacing fails.
+ *
+ * @param entityPlayer player using the wrench, may be null
+ * @return Whether the wrenching was done and the wrench should be damaged
+ */
+ boolean wrenchCanRemove(EntityPlayer entityPlayer);
+
+ /**
+ * Determine the probability to drop the block as it is.
+ * The first entry in getDrops will be replaced by blockid:meta if the drop is successful.
+ *
+ * @return Probability from 0 to 1
+ */
+ float getWrenchDropRate();
+
+ /**
+ * Determine the item the block will drop when the wrenching is successful.
+ *
+ * The ItemStack will be copied before creating the EntityItem.
+ *
+ * @param entityPlayer player using the wrench, may be null
+ * @return ItemStack to drop, may be null
+ */
+ ItemStack getWrenchDrop(EntityPlayer entityPlayer);
+}
+
diff --git a/src/api/java/ic2/api/tile/package-info.java b/src/api/java/ic2/api/tile/package-info.java
new file mode 100644
index 0000000..5ebc76e
--- /dev/null
+++ b/src/api/java/ic2/api/tile/package-info.java
@@ -0,0 +1,4 @@
+@API(apiVersion="1.0", owner="IC2", provides="IC2API")
+package ic2.api.tile;
+import cpw.mods.fml.common.API;
+
diff --git a/src/api/java/ic2/api/util/IKeyboard.java b/src/api/java/ic2/api/util/IKeyboard.java
new file mode 100644
index 0000000..ff6eaea
--- /dev/null
+++ b/src/api/java/ic2/api/util/IKeyboard.java
@@ -0,0 +1,14 @@
+package ic2.api.util;
+
+import net.minecraft.entity.player.EntityPlayer;
+
+public interface IKeyboard {
+ boolean isAltKeyDown(EntityPlayer player);
+ boolean isBoostKeyDown(EntityPlayer player);
+ boolean isForwardKeyDown(EntityPlayer player);
+ boolean isJumpKeyDown(EntityPlayer player);
+ boolean isModeSwitchKeyDown(EntityPlayer player);
+ boolean isSideinventoryKeyDown(EntityPlayer player);
+ boolean isHudModeKeyDown(EntityPlayer player);
+ boolean isSneakKeyDown(EntityPlayer player);
+}
diff --git a/src/api/java/ic2/api/util/Keys.java b/src/api/java/ic2/api/util/Keys.java
new file mode 100644
index 0000000..84d1756
--- /dev/null
+++ b/src/api/java/ic2/api/util/Keys.java
@@ -0,0 +1,6 @@
+package ic2.api.util;
+
+
+public class Keys {
+ public static IKeyboard instance;
+} \ No newline at end of file
diff --git a/src/api/java/ic2/api/util/package-info.java b/src/api/java/ic2/api/util/package-info.java
new file mode 100644
index 0000000..4dd2d93
--- /dev/null
+++ b/src/api/java/ic2/api/util/package-info.java
@@ -0,0 +1,4 @@
+@API(apiVersion="1.0", owner="IC2", provides="IC2API")
+package ic2.api.util;
+import cpw.mods.fml.common.API;
+
diff --git a/src/api/java/mekanism/api/Chunk3D.java b/src/api/java/mekanism/api/Chunk3D.java
new file mode 100644
index 0000000..f469d5d
--- /dev/null
+++ b/src/api/java/mekanism/api/Chunk3D.java
@@ -0,0 +1,118 @@
+package mekanism.api;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.world.ChunkCoordIntPair;
+import net.minecraft.world.World;
+import net.minecraft.world.chunk.Chunk;
+
+/**
+ * Chunk3D - an integer-based way to keep track of and perform operations on chunks in a Minecraft-based environment. This also takes
+ * in account the dimension the chunk is in.
+ * @author aidancbrady
+ *
+ */
+public class Chunk3D
+{
+ public int dimensionId;
+
+ public int xCoord;
+ public int zCoord;
+
+ /**
+ * Creates a Chunk3D object from the given x and z coordinates, as well as a dimension.
+ * @param x - chunk x location
+ * @param z - chunk z location
+ * @param dimension - the dimension this Chunk3D is in
+ */
+ public Chunk3D(int x, int z, int dimension)
+ {
+ xCoord = x;
+ zCoord = z;
+
+ dimensionId = dimension;
+ }
+
+ /**
+ * Creates a Chunk3D from an entity based on it's location and dimension.
+ * @param entity - the entity to get the Chunk3D object from
+ */
+ public Chunk3D(Entity entity)
+ {
+ xCoord = ((int)entity.posX) >> 4;
+ zCoord = ((int)entity.posZ) >> 4;
+
+ dimensionId = entity.dimension;
+ }
+
+ /**
+ * Creates a Chunk3D from a Coord4D based on it's coordinates and dimension.
+ * @param coord - the Coord4D object to get this Chunk3D from
+ */
+ public Chunk3D(Coord4D coord)
+ {
+ xCoord = coord.xCoord >> 4;
+ zCoord = coord.zCoord >> 4;
+
+ dimensionId = coord.dimensionId;
+ }
+
+ /**
+ * Whether or not this chunk exists in the given world.
+ * @param world - the world to check in
+ * @return if the chunk exists
+ */
+ public boolean exists(World world)
+ {
+ return world.getChunkProvider().chunkExists(xCoord, zCoord);
+ }
+
+ /**
+ * Gets a Chunk object corresponding to this Chunk3D's coordinates.
+ * @param world - the world to get the Chunk object from
+ * @return the corresponding Chunk object
+ */
+ public Chunk getChunk(World world)
+ {
+ return world.getChunkFromChunkCoords(xCoord, zCoord);
+ }
+
+ /**
+ * Returns this Chunk3D in the Minecraft-based ChunkCoordIntPair format.
+ * @return this Chunk3D as a ChunkCoordIntPair
+ */
+ public ChunkCoordIntPair toPair()
+ {
+ return new ChunkCoordIntPair(xCoord, zCoord);
+ }
+
+ @Override
+ public Coord4D clone()
+ {
+ return new Coord4D(xCoord, zCoord, dimensionId);
+ }
+
+ @Override
+ public String toString()
+ {
+ return "[Chunk3D: " + xCoord + ", " + zCoord + ", dim=" + dimensionId + "]";
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return obj instanceof Chunk3D &&
+ ((Chunk3D)obj).xCoord == xCoord &&
+ ((Chunk3D)obj).zCoord == zCoord &&
+ ((Chunk3D)obj).dimensionId == dimensionId;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int code = 1;
+ code = 31 * code + xCoord;
+ code = 31 * code + zCoord;
+ code = 31 * code + dimensionId;
+ return code;
+ }
+}
diff --git a/src/api/java/mekanism/api/Coord4D.java b/src/api/java/mekanism/api/Coord4D.java
new file mode 100644
index 0000000..1a0f7e4
--- /dev/null
+++ b/src/api/java/mekanism/api/Coord4D.java
@@ -0,0 +1,421 @@
+package mekanism.api;
+
+import cpw.mods.fml.common.network.NetworkRegistry.TargetPoint;
+import io.netty.buffer.ByteBuf;
+import net.minecraft.block.Block;
+import net.minecraft.entity.Entity;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.util.MathHelper;
+import net.minecraft.util.MovingObjectPosition;
+import net.minecraft.world.IBlockAccess;
+import net.minecraft.world.World;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import java.util.ArrayList;
+
+/**
+ * Coord4D - an integer-based way to keep track of and perform operations on blocks in a Minecraft-based environment. This also takes
+ * in account the dimension the coordinate is in.
+ * @author aidancbrady
+ *
+ */
+public class Coord4D
+{
+ public int xCoord;
+ public int yCoord;
+ public int zCoord;
+
+ public int dimensionId;
+
+ /**
+ * Creates a Coord4D WITHOUT a dimensionId. Don't use unless absolutely necessary.
+ * @param x - x coordinate
+ * @param y - y coordinate
+ * @param z - z coordinate
+ */
+ public Coord4D(int x, int y, int z)
+ {
+ xCoord = x;
+ yCoord = y;
+ zCoord = z;
+
+ dimensionId = 0;
+ }
+
+ /**
+ * Creates a Coord4D from an entity's position, rounded down.
+ * @param entity - entity to create the Coord4D from
+ */
+ public Coord4D(Entity entity)
+ {
+ xCoord = (int)entity.posX;
+ yCoord = (int)entity.posY;
+ zCoord = (int)entity.posZ;
+
+ dimensionId = entity.worldObj.provider.dimensionId;
+ }
+
+ /**
+ * Creates a Coord4D from the defined x, y, z, and dimension values.
+ * @param x - x coordinate
+ * @param y - y coordinate
+ * @param z - z coordinate
+ * @param dimension - dimension ID
+ */
+ public Coord4D(int x, int y, int z, int dimension)
+ {
+ xCoord = x;
+ yCoord = y;
+ zCoord = z;
+
+ dimensionId = dimension;
+ }
+
+ public Coord4D(MovingObjectPosition mop)
+ {
+ xCoord = mop.blockX;
+ yCoord = mop.blockY;
+ zCoord = mop.blockZ;
+ }
+
+ /**
+ * Gets the metadata of the block representing this Coord4D.
+ * @param world - world this Coord4D is in
+ * @return the metadata of this Coord4D's block
+ */
+ public int getMetadata(IBlockAccess world)
+ {
+ return world.getBlockMetadata(xCoord, yCoord, zCoord);
+ }
+
+ /**
+ * Gets the TileEntity of the block representing this Coord4D.
+ * @param world - world this Coord4D is in
+ * @return the TileEntity of this Coord4D's block
+ */
+ public TileEntity getTileEntity(IBlockAccess world)
+ {
+ if(world instanceof World && !exists((World)world))
+ {
+ return null;
+ }
+
+ return world.getTileEntity(xCoord, yCoord, zCoord);
+ }
+
+ /**
+ * Gets the Block value of the block representing this Coord4D.
+ * @param world - world this Coord4D is in
+ * @return the Block value of this Coord4D's block
+ */
+ public Block getBlock(IBlockAccess world)
+ {
+ if(world instanceof World && !exists((World)world))
+ {
+ return null;
+ }
+
+ return world.getBlock(xCoord, yCoord, zCoord);
+ }
+
+ /**
+ * Writes this Coord4D's data to an NBTTagCompound.
+ * @param nbtTags - tag compound to write to
+ * @return the tag compound with this Coord4D's data
+ */
+ public NBTTagCompound write(NBTTagCompound nbtTags)
+ {
+ nbtTags.setInteger("x", xCoord);
+ nbtTags.setInteger("y", yCoord);
+ nbtTags.setInteger("z", zCoord);
+ nbtTags.setInteger("dimensionId", dimensionId);
+
+ return nbtTags;
+ }
+
+ /**
+ * Writes this Coord4D's data to an ArrayList for packet transfer.
+ * @param data - the ArrayList to add the data to
+ */
+ public void write(ArrayList data)
+ {
+ data.add(xCoord);
+ data.add(yCoord);
+ data.add(zCoord);
+ data.add(dimensionId);
+ }
+
+ /**
+ * Writes this Coord4D's data to a ByteBuf for packet transfer.
+ * @param dataStream - the ByteBuf to add the data to
+ */
+ public void write(ByteBuf dataStream)
+ {
+ dataStream.writeInt(xCoord);
+ dataStream.writeInt(yCoord);
+ dataStream.writeInt(zCoord);
+ dataStream.writeInt(dimensionId);
+ }
+
+ /**
+ * Translates this Coord4D by the defined x, y, and z values.
+ * @param x - x value to translate
+ * @param y - y value to translate
+ * @param z - z value to translate
+ * @return translated Coord4D
+ */
+ public Coord4D translate(int x, int y, int z)
+ {
+ xCoord += x;
+ yCoord += y;
+ zCoord += z;
+
+ return this;
+ }
+
+ /**
+ * Translates this Coord4D by the defined Coord4D's coordinates, regardless of dimension.
+ * @param coord - coordinates to translate by
+ * @return translated Coord4D
+ */
+ public Coord4D translate(Coord4D coord)
+ {
+ translate(coord.xCoord, coord.yCoord, coord.zCoord);
+
+ return this;
+ }
+
+ /**
+ * Creates and returns a new Coord4D translated to the defined offsets of the side.
+ * @param side - side to translate this Coord4D to
+ * @return translated Coord4D
+ */
+ public Coord4D getFromSide(ForgeDirection side)
+ {
+ return getFromSide(side, 1);
+ }
+
+ /**
+ * Creates and returns a new Coord4D translated to the defined offsets of the side by the defined amount.
+ * @param side - side to translate this Coord4D to
+ * @param amount - how far to translate this Coord4D
+ * @return translated Coord4D
+ */
+ public Coord4D getFromSide(ForgeDirection side, int amount)
+ {
+ return new Coord4D(xCoord+(side.offsetX*amount), yCoord+(side.offsetY*amount), zCoord+(side.offsetZ*amount), dimensionId);
+ }
+
+ public ItemStack getStack(IBlockAccess world)
+ {
+ Block block = getBlock(world);
+
+ if(block == null || block == Blocks.air)
+ {
+ return null;
+ }
+
+ return new ItemStack(block, 1, getMetadata(world));
+ }
+
+ /**
+ * Returns a new Coord4D from a defined TileEntity's xCoord, yCoord and zCoord values.
+ * @param tileEntity - TileEntity at the location that will represent this Coord4D
+ * @return the Coord4D object from the TileEntity
+ */
+ public static Coord4D get(TileEntity tileEntity)
+ {
+ return new Coord4D(tileEntity.xCoord, tileEntity.yCoord, tileEntity.zCoord, tileEntity.getWorldObj().provider.dimensionId);
+ }
+
+ /**
+ * Returns a new Coord4D from a tag compound.
+ * @param tag - tag compound to read from
+ * @return the Coord4D from the tag compound
+ */
+ public static Coord4D read(NBTTagCompound tag)
+ {
+ return new Coord4D(tag.getInteger("x"), tag.getInteger("y"), tag.getInteger("z"), tag.getInteger("id"));
+ }
+
+ /**
+ * Returns a new Coord4D from a ByteBuf.
+ * @param dataStream - data input to read from
+ * @return the Coord4D from the data input
+ */
+ public static Coord4D read(ByteBuf dataStream)
+ {
+ return new Coord4D(dataStream.readInt(), dataStream.readInt(), dataStream.readInt(), dataStream.readInt());
+ }
+
+ /**
+ * Creates and returns a new Coord4D with values representing the difference between the defined Coord4D
+ * @param other - the Coord4D to subtract from this
+ * @return a Coord4D representing the distance between the defined Coord4D
+ */
+ public Coord4D difference(Coord4D other)
+ {
+ return new Coord4D(xCoord-other.xCoord, yCoord-other.yCoord, zCoord-other.zCoord, dimensionId);
+ }
+
+ /**
+ * A method used to find the ForgeDirection represented by the distance of the defined Coord4D. Most likely won't have many
+ * applicable uses.
+ * @param other - Coord4D to find the side difference of
+ * @return ForgeDirection representing the side the defined relative Coord4D is on to this
+ */
+ public ForgeDirection sideDifference(Coord4D other)
+ {
+ Coord4D diff = difference(other);
+
+ for(ForgeDirection side : ForgeDirection.VALID_DIRECTIONS)
+ {
+ if(side.offsetX == diff.xCoord && side.offsetY == diff.yCoord && side.offsetZ == diff.zCoord)
+ {
+ return side;
+ }
+ }
+
+ return ForgeDirection.UNKNOWN;
+ }
+
+ /**
+ * Gets the distance to a defined Coord4D.
+ * @param obj - the Coord4D to find the distance to
+ * @return the distance to the defined Coord4D
+ */
+ public int distanceTo(Coord4D obj)
+ {
+ int subX = xCoord - obj.xCoord;
+ int subY = yCoord - obj.yCoord;
+ int subZ = zCoord - obj.zCoord;
+ return (int)MathHelper.sqrt_double(subX * subX + subY * subY + subZ * subZ);
+ }
+
+ /**
+ * Whether or not the defined side of this Coord4D is visible.
+ * @param side - side to check
+ * @param world - world this Coord4D is in
+ * @return
+ */
+ public boolean sideVisible(ForgeDirection side, IBlockAccess world)
+ {
+ return world.isAirBlock(xCoord+side.offsetX, yCoord+side.offsetY, zCoord+side.offsetZ);
+ }
+
+ /**
+ * Gets a TargetPoint with the defined range from this Coord4D with the appropriate coordinates and dimension ID.
+ * @param range - the range the packet can be sent in of this Coord4D
+ * @return TargetPoint relative to this Coord4D
+ */
+ public TargetPoint getTargetPoint(double range)
+ {
+ return new TargetPoint(dimensionId, xCoord, yCoord, zCoord, range);
+ }
+
+ /**
+ * Steps this Coord4D in the defined side's offset without creating a new value.
+ * @param side - side to step towards
+ * @return this Coord4D
+ */
+ public Coord4D step(ForgeDirection side)
+ {
+ return translate(side.offsetX, side.offsetY, side.offsetZ);
+ }
+
+ /**
+ * Whether or not the chunk this Coord4D is in exists and is loaded.
+ * @param world - world this Coord4D is in
+ * @return the chunk of this Coord4D
+ */
+ public boolean exists(World world)
+ {
+ return world.getChunkProvider() == null || world.getChunkProvider().chunkExists(xCoord >> 4, zCoord >> 4);
+ }
+
+ /**
+ * Gets the chunk this Coord4D is in.
+ * @param world - world this Coord4D is in
+ * @return the chunk of this Coord4D
+ */
+ public Chunk getChunk(World world)
+ {
+ return world.getChunkFromBlockCoords(xCoord, zCoord);
+ }
+
+ /**
+ * Gets the Chunk3D object with chunk coordinates correlating to this Coord4D's location
+ * @return Chunk3D with correlating chunk coordinates.
+ */
+ public Chunk3D getChunk3D()
+ {
+ return new Chunk3D(this);
+ }
+
+ /**
+ * Whether or not the block this Coord4D represents is an air block.
+ * @param world - world this Coord4D is in
+ * @return if this Coord4D is an air block
+ */
+ public boolean isAirBlock(IBlockAccess world)
+ {
+ return world.isAirBlock(xCoord, yCoord, zCoord);
+ }
+
+ /**
+ * Whether or not this block this Coord4D represents is replaceable.
+ * @param world - world this Coord4D is in
+ * @return if this Coord4D is replaceable
+ */
+ public boolean isReplaceable(IBlockAccess world)
+ {
+ return getBlock(world).isReplaceable(world, xCoord, yCoord, zCoord);
+ }
+
+ /**
+ * Gets a bounding box that contains the area this Coord4D would take up in a world.
+ * @return this Coord4D's bounding box
+ */
+ public AxisAlignedBB getBoundingBox()
+ {
+ return AxisAlignedBB.getBoundingBox(xCoord, yCoord, zCoord, xCoord+1, yCoord+1, zCoord+1);
+ }
+
+ @Override
+ public Coord4D clone()
+ {
+ return new Coord4D(xCoord, yCoord, zCoord, dimensionId);
+ }
+
+ @Override
+ public String toString()
+ {
+ return "[Coord4D: " + xCoord + ", " + yCoord + ", " + zCoord + ", dim=" + dimensionId + "]";
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return obj instanceof Coord4D &&
+ ((Coord4D)obj).xCoord == xCoord &&
+ ((Coord4D)obj).yCoord == yCoord &&
+ ((Coord4D)obj).zCoord == zCoord &&
+ ((Coord4D)obj).dimensionId == dimensionId;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int code = 1;
+ code = 31 * code + xCoord;
+ code = 31 * code + yCoord;
+ code = 31 * code + zCoord;
+ code = 31 * code + dimensionId;
+ return code;
+ }
+} \ No newline at end of file
diff --git a/src/api/java/mekanism/api/EnumColor.java b/src/api/java/mekanism/api/EnumColor.java
new file mode 100644
index 0000000..df9ba74
--- /dev/null
+++ b/src/api/java/mekanism/api/EnumColor.java
@@ -0,0 +1,112 @@
+package mekanism.api;
+
+import net.minecraft.util.StatCollector;
+
+/**
+ * Simple color enum for adding colors to in-game GUI strings of text.
+ * @author AidanBrady
+ *
+ */
+public enum EnumColor
+{
+ BLACK("\u00a70", "black", "Black", new int[] {0, 0, 0}, 0),
+ DARK_BLUE("\u00a71", "darkBlue", "Blue", new int[] {0, 0, 170}, 4),
+ DARK_GREEN("\u00a72", "darkGreen", "Green", new int[] {0, 170, 0}, 2),
+ DARK_AQUA("\u00a73", "darkAqua", "Cyan", new int[] {0, 255, 255}, 6),
+ DARK_RED("\u00a74", "darkRed", null, new int[] {170, 0, 0}, -1),
+ PURPLE("\u00a75", "purple", "Purple", new int[] {170, 0, 170}, 5),
+ ORANGE("\u00a76", "orange", "Orange", new int[] {255, 170, 0}, 14),
+ GREY("\u00a77", "grey", "LightGray", new int[] {170, 170, 170}, 7),
+ DARK_GREY("\u00a78", "darkGrey", "Gray", new int[] {85, 85, 85}, 8),
+ INDIGO("\u00a79", "indigo", "LightBlue", new int[] {85, 85, 255}, 12),
+ BRIGHT_GREEN("\u00a7a", "brightGreen", "Lime", new int[] {85, 255, 85}, 10),
+ AQUA("\u00a7b", "aqua", null, new int[] {85, 255, 255}, -1),
+ RED("\u00a7c", "red", "Red", new int[] {255, 0, 0}, 1),
+ PINK("\u00a7d", "pink", "Magenta", new int[] {255, 85, 255}, 13),
+ YELLOW("\u00a7e", "yellow", "Yellow", new int[] {255, 255, 85}, 11),
+ WHITE("\u00a7f", "white", "White", new int[] {255, 255, 255}, 15),
+ //Extras for dye-completeness
+ BROWN("\u00a76", "brown", "Brown", new int[] {150, 75, 0}, 3),
+ BRIGHT_PINK("\u00a7d", "brightPink", "Pink", new int[] {255, 192, 203}, 9);
+
+ public static EnumColor[] DYES = new EnumColor[] {BLACK, RED, DARK_GREEN, BROWN, DARK_BLUE, PURPLE, DARK_AQUA, GREY, DARK_GREY, BRIGHT_PINK, BRIGHT_GREEN, YELLOW, INDIGO, PINK, ORANGE, WHITE};
+
+ /** The color code that will be displayed */
+ public final String code;
+
+ public final int[] rgbCode;
+
+ public final int mcMeta;
+
+ /** A friendly name of the color. */
+ public String unlocalizedName;
+
+ public String dyeName;
+
+ private EnumColor(String s, String n, String dye, int[] rgb, int meta)
+ {
+ code = s;
+ unlocalizedName = n;
+ dyeName = dye;
+ rgbCode = rgb;
+ mcMeta = meta;
+ }
+
+ /**
+ * Gets the localized name of this color by translating the unlocalized name.
+ * @return localized name
+ */
+ public String getLocalizedName()
+ {
+ return StatCollector.translateToLocal("color." + unlocalizedName);
+ }
+
+ public String getDyeName()
+ {
+ return StatCollector.translateToLocal("dye." + unlocalizedName);
+ }
+
+ public String getOreDictName()
+ {
+ return dyeName;
+ }
+
+ /**
+ * Gets the name of this color with it's color prefix code.
+ * @return the color's name and color prefix
+ */
+ public String getName()
+ {
+ return code + getLocalizedName();
+ }
+
+ public String getDyedName()
+ {
+ return code + getDyeName();
+ }
+
+ /**
+ * Gets the 0-1 of this color's RGB value by dividing by 255 (used for OpenGL coloring).
+ * @param index - R:0, G:1, B:2
+ * @return the color value
+ */
+ public float getColor(int index)
+ {
+ return (float)rgbCode[index]/255F;
+ }
+
+ /**
+ * Gets the value of this color mapped to MC in-game item colors present in dyes and wool.
+ * @return mc meta value
+ */
+ public int getMetaValue()
+ {
+ return mcMeta;
+ }
+
+ @Override
+ public String toString()
+ {
+ return code;
+ }
+}
diff --git a/src/api/java/mekanism/api/IClientTicker.java b/src/api/java/mekanism/api/IClientTicker.java
new file mode 100644
index 0000000..2288973
--- /dev/null
+++ b/src/api/java/mekanism/api/IClientTicker.java
@@ -0,0 +1,8 @@
+package mekanism.api;
+
+public interface IClientTicker
+{
+ public void clientTick();
+
+ public boolean needsTicks();
+}
diff --git a/src/api/java/mekanism/api/IConfigurable.java b/src/api/java/mekanism/api/IConfigurable.java
new file mode 100644
index 0000000..512fabf
--- /dev/null
+++ b/src/api/java/mekanism/api/IConfigurable.java
@@ -0,0 +1,27 @@
+package mekanism.api;
+
+import net.minecraft.entity.player.EntityPlayer;
+
+/**
+ * Implement this in your TileEntity class if your block can be modified by a Configurator.
+ * @author aidancbrady
+ *
+ */
+public interface IConfigurable
+{
+ /**
+ * Called when a player shift-right clicks this block with a Configurator.
+ * @param player - the player who clicked the block
+ * @param side - the side the block was clicked on
+ * @return whether or not an action was performed
+ */
+ public boolean onSneakRightClick(EntityPlayer player, int side);
+
+ /**
+ * Called when a player right clicks this block with a Configurator.
+ * @param player - the player who clicked the block
+ * @param side - the side the block was clicked on
+ * @return whether or not an action was performed
+ */
+ public boolean onRightClick(EntityPlayer player, int side);
+}
diff --git a/src/api/java/mekanism/api/IFilterAccess.java b/src/api/java/mekanism/api/IFilterAccess.java
new file mode 100644
index 0000000..abdcf29
--- /dev/null
+++ b/src/api/java/mekanism/api/IFilterAccess.java
@@ -0,0 +1,31 @@
+package mekanism.api;
+
+import net.minecraft.nbt.NBTTagCompound;
+
+/**
+ * Implement this in your TileEntity class if you wish for Mekanism filters to be able to store any of their
+ * information.
+ * @author aidancbrady
+ *
+ */
+public interface IFilterAccess
+{
+ /**
+ * Collects the TileEntity's filter card data into the parameterized NBTTagCompound.
+ * @param nbtTags - the NBTTagCompound of the filter card ItemStack
+ * @return the NBTTagCompound that now contains the TileEntity's filter card data
+ */
+ public NBTTagCompound getFilterData(NBTTagCompound nbtTags);
+
+ /**
+ * Retrieves the TileEntity's data contained in the filter card based on the given NBTTagCompopund.
+ * @param nbtTags - the NBTTagCompound of the filter card ItemStack
+ */
+ public void setFilterData(NBTTagCompound nbtTags);
+
+ /**
+ * A String name of this TileEntity that will be displayed as the type of data on the filter card.
+ * @return the String name of this TileEntity
+ */
+ public String getDataType();
+}
diff --git a/src/api/java/mekanism/api/IHeatTransfer.java b/src/api/java/mekanism/api/IHeatTransfer.java
new file mode 100644
index 0000000..da682e1
--- /dev/null
+++ b/src/api/java/mekanism/api/IHeatTransfer.java
@@ -0,0 +1,28 @@
+package mekanism.api;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+public interface IHeatTransfer
+{
+ /**The value of the zero point of our temperature scale in kelvin*/
+ public static final double AMBIENT_TEMP = 300;
+
+ /**The heat transfer coefficient for air*/
+ public static final double AIR_INVERSE_COEFFICIENT = 10000;
+
+ public double getTemp();
+
+ public double getInverseConductionCoefficient();
+
+ public double getInsulationCoefficient(ForgeDirection side);
+
+ public void transferHeatTo(double heat);
+
+ public double[] simulateHeat();
+
+ public double applyTemperatureChange();
+
+ public boolean canConnectHeat(ForgeDirection side);
+
+ public IHeatTransfer getAdjacent(ForgeDirection side);
+}
diff --git a/src/api/java/mekanism/api/IMekWrench.java b/src/api/java/mekanism/api/IMekWrench.java
new file mode 100644
index 0000000..6ac4b05
--- /dev/null
+++ b/src/api/java/mekanism/api/IMekWrench.java
@@ -0,0 +1,8 @@
+package mekanism.api;
+
+import net.minecraft.entity.player.EntityPlayer;
+
+public interface IMekWrench
+{
+ public boolean canUseWrench(EntityPlayer player, int x, int y, int z);
+}
diff --git a/src/api/java/mekanism/api/ISalinationSolar.java b/src/api/java/mekanism/api/ISalinationSolar.java
new file mode 100644
index 0000000..20e3e1e
--- /dev/null
+++ b/src/api/java/mekanism/api/ISalinationSolar.java
@@ -0,0 +1,11 @@
+package mekanism.api;
+
+/**
+ * Implement this class in a TileEntity if you wish for it to be able to heat up a Salination Plant.
+ * @author aidancbrady
+ *
+ */
+public interface ISalinationSolar
+{
+ public boolean seesSun();
+}
diff --git a/src/api/java/mekanism/api/ItemRetriever.java b/src/api/java/mekanism/api/ItemRetriever.java
new file mode 100644
index 0000000..c2820f0
--- /dev/null
+++ b/src/api/java/mekanism/api/ItemRetriever.java
@@ -0,0 +1,112 @@
+package mekanism.api;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Use this class's 'getItem()' method to retrieve ItemStacks from the 'Mekanism'
+ * class.
+ * @author AidanBrady
+ *
+ */
+public final class ItemRetriever
+{
+ /** The 'MekanismItems' class that items are retrieved from. */
+ private static Class MekanismItems;
+
+ /** The 'MekanismBlocks' class that blocks are retrieved from. */
+ private static Class MekanismBlocks;
+
+ /**
+ * Attempts to retrieve an ItemStack of an item with the declared identifier.
+ *
+ * Mekanism identifiers follow an easy-to-remember pattern. All identifiers
+ * are identical to the String returned by 'getItemName().' None include spaces,
+ * and all start with a capital letter. The name that shows up in-game can be
+ * stripped down to identifier form by removing spaces and all non-alphabetic
+ * characters (,./'=-_). Below is an example:
+ *
+ * ItemStack enrichedAlloy = ItemRetriever.getItem("EnrichedAlloy");
+ *
+ * Note that for items or blocks that have specific metadata you will need to create
+ * a new ItemStack with that specified value, as this will only return an ItemStack
+ * with the meta value '0.'
+ *
+ * Make sure you run this in or after FMLPostInitializationEvent runs, because most
+ * items are registered when FMLInitializationEvent runs. However, some items ARE
+ * registered later in order to hook into other mods. In a rare circumstance you may
+ * have to add "after:Mekanism" in the @Mod 'dependencies' annotation.
+ *
+ * @param identifier - a String to be searched in the 'MekanismItems' class
+ * @return an ItemStack of the declared identifier, otherwise null.
+ */
+ public static ItemStack getItem(String identifier)
+ {
+ try {
+ if(MekanismItems == null)
+ {
+ MekanismItems = Class.forName("mekanism.common.MekanismItems");
+ }
+
+ Object ret = MekanismItems.getField(identifier).get(null);
+
+ if(ret instanceof Item)
+ {
+ return new ItemStack((Item)ret, 1);
+ }
+ else {
+ return null;
+ }
+ } catch(Exception e) {
+ System.err.println("Error retrieving item with identifier '" + identifier + "': " + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Attempts to retrieve an ItemStack of a block with the declared identifier.
+ *
+ * Mekanism identifiers follow an easy-to-remember pattern. All identifiers
+ * are identical to the String returned by 'getItemName().' None include spaces,
+ * and all start with a capital letter. The name that shows up in-game can be
+ * stripped down to identifier form by removing spaces and all non-alphabetic
+ * characters (,./'=-_). Below is an example:
+ *
+ * ItemStack enrichedAlloy = ItemRetriever.getItem("EnrichedAlloy");
+ *
+ * Note that for items or blocks that have specific metadata you will need to create
+ * a new ItemStack with that specified value, as this will only return an ItemStack
+ * with the meta value '0.'
+ *
+ * Make sure you run this in or after FMLPostInitializationEvent runs, because most
+ * items are registered when FMLInitializationEvent runs. However, some items ARE
+ * registered later in order to hook into other mods. In a rare circumstance you may
+ * have to add "after:Mekanism" in the @Mod 'dependencies' annotation.
+ *
+ * @param identifier - a String to be searched in the 'MekanismBlocks' class
+ * @return an ItemStack of the declared identifier, otherwise null.
+ */
+ public static ItemStack getBlock(String identifier)
+ {
+ try {
+ if(MekanismBlocks == null)
+ {
+ MekanismBlocks = Class.forName("mekanism.common.MekanismBlocks");
+ }
+
+ Object ret = MekanismBlocks.getField(identifier).get(null);
+
+ if(ret instanceof Block)
+ {
+ return new ItemStack((Block)ret, 1);
+ }
+ else {
+ return null;
+ }
+ } catch(Exception e) {
+ System.err.println("Error retrieving block with identifier '" + identifier + "': " + e.getMessage());
+ return null;
+ }
+ }
+}
diff --git a/src/api/java/mekanism/api/MekanismAPI.java b/src/api/java/mekanism/api/MekanismAPI.java
new file mode 100644
index 0000000..abdd6da
--- /dev/null
+++ b/src/api/java/mekanism/api/MekanismAPI.java
@@ -0,0 +1,50 @@
+package mekanism.api;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import mekanism.api.util.BlockInfo;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.Item;
+import net.minecraftforge.oredict.OreDictionary;
+import cpw.mods.fml.common.eventhandler.Event;
+
+public class MekanismAPI
+{
+ //Add a BlockInfo value here if you don't want a certain block to be picked up by cardboard boxes
+ private static Set<BlockInfo> cardboardBoxIgnore = new HashSet<BlockInfo>();
+
+ /** Mekanism debug mode */
+ public static boolean debug = false;
+
+ public static boolean isBlockCompatible(Item item, int meta)
+ {
+ for(BlockInfo i : cardboardBoxIgnore)
+ {
+ if(i.block == Block.getBlockFromItem(item) && (i.meta == OreDictionary.WILDCARD_VALUE || i.meta == meta))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static void addBoxBlacklist(Block block, int meta)
+ {
+ cardboardBoxIgnore.add(new BlockInfo(block, meta));
+ }
+
+ public static void removeBoxBlacklist(Block block, int meta)
+ {
+ cardboardBoxIgnore.remove(new BlockInfo(block, meta));
+ }
+
+ public static Set<BlockInfo> getBoxIgnore()
+ {
+ return cardboardBoxIgnore;
+ }
+
+ public static class BoxBlacklistEvent extends Event {}
+}
diff --git a/src/api/java/mekanism/api/MekanismConfig.java b/src/api/java/mekanism/api/MekanismConfig.java
new file mode 100644
index 0000000..2f9ea68
--- /dev/null
+++ b/src/api/java/mekanism/api/MekanismConfig.java
@@ -0,0 +1,137 @@
+package mekanism.api;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import mekanism.api.util.UnitDisplayUtils.EnergyType;
+import mekanism.api.util.UnitDisplayUtils.TempType;
+
+public class MekanismConfig
+{
+ public static class general
+ {
+ public static boolean updateNotifications = true;
+ public static boolean controlCircuitOreDict = true;
+ public static boolean logPackets = false;
+ public static boolean dynamicTankEasterEgg = false;
+ public static boolean voiceServerEnabled = true;
+ public static boolean cardboardSpawners = true;
+ public static boolean enableWorldRegeneration = true;
+ public static boolean creativeOverrideElectricChest = true;
+ public static boolean spawnBabySkeletons = true;
+ public static int obsidianTNTBlastRadius = 12;
+ public static int osmiumPerChunk = 12;
+ public static int copperPerChunk = 16;
+ public static int tinPerChunk = 14;
+ public static int saltPerChunk = 2;
+ public static int obsidianTNTDelay = 100;
+ public static int UPDATE_DELAY = 10;
+ public static int VOICE_PORT = 36123;
+ public static int maxUpgradeMultiplier = 10;
+ public static int userWorldGenVersion = 0;
+ public static double ENERGY_PER_REDSTONE = 10000;
+ public static int ETHENE_BURN_TIME = 40;
+ public static double DISASSEMBLER_USAGE = 10;
+ public static EnergyType activeType = EnergyType.J;
+ public static TempType tempUnit = TempType.K;
+ public static double TO_IC2;
+ public static double TO_TE;
+ public static double FROM_H2;
+ public static double FROM_IC2;
+ public static double FROM_TE;
+ public static int laserRange;
+ public static double laserEnergyNeededPerHardness;
+ public static double minerSilkMultiplier = 6;
+ public static boolean blacklistIC2;
+ public static boolean blacklistRF;
+ public static boolean destroyDisabledBlocks;
+ public static boolean enableAmbientLighting;
+ public static int ambientLightingLevel;
+ public static boolean prefilledPortableTanks;
+ public static double armoredJetpackDamageRatio;
+ public static int armoredJetpackDamageMax;
+ public static boolean aestheticWorldDamage;
+ public static boolean opsBypassRestrictions;
+ public static double solarEvaporationSpeed;
+ public static int maxJetpackGas;
+ public static int maxScubaGas;
+ public static int maxFlamethrowerGas;
+ }
+
+ public static class client
+ {
+ public static boolean enablePlayerSounds = true;
+ public static boolean enableMachineSounds = true;
+ public static boolean fancyUniversalCableRender = true;
+ public static boolean holidays = true;
+ public static float baseSoundVolume = 1F;
+ public static boolean machineEffects = true;
+ public static boolean oldTransmitterRender = false;
+ public static boolean replaceSoundsWhenResuming = true;
+ public static boolean renderCTM = true;
+ }
+
+ public static class machines
+ {
+ private static Map<String, Boolean> config = new HashMap<String, Boolean>();
+
+ public static boolean isEnabled(String type)
+ {
+ return config.get(type) != null && config.get(type);
+ }
+
+ public static void setEntry(String type, boolean enabled)
+ {
+ config.put(type, enabled);
+ }
+ }
+
+ public static class usage
+ {
+ public static double enrichmentChamberUsage;
+ public static double osmiumCompressorUsage;
+ public static double combinerUsage;
+ public static double crusherUsage;
+ public static double factoryUsage;
+ public static double metallurgicInfuserUsage;
+ public static double purificationChamberUsage;
+ public static double energizedSmelterUsage;
+ public static double digitalMinerUsage;
+ public static double electricPumpUsage;
+ public static double rotaryCondensentratorUsage;
+ public static double oxidationChamberUsage;
+ public static double chemicalInfuserUsage;
+ public static double chemicalInjectionChamberUsage;
+ public static double precisionSawmillUsage;
+ public static double chemicalDissolutionChamberUsage;
+ public static double chemicalWasherUsage;
+ public static double chemicalCrystallizerUsage;
+ public static double seismicVibratorUsage;
+ public static double pressurizedReactionBaseUsage;
+ public static double fluidicPlenisherUsage;
+ public static double laserUsage;
+ public static double gasCentrifugeUsage;
+ public static double heavyWaterElectrolysisUsage;
+ }
+
+ public static class generators
+ {
+ public static double advancedSolarGeneration;
+ public static double bioGeneration;
+ public static double heatGeneration;
+ public static double heatGenerationLava;
+ public static double heatGenerationNether;
+ public static double solarGeneration;
+
+ public static double windGenerationMin;
+ public static double windGenerationMax;
+
+ public static int windGenerationMinY;
+ public static int windGenerationMaxY;
+ }
+
+ public static class tools
+ {
+ public static double armorSpawnRate;
+ }
+}
diff --git a/src/api/java/mekanism/api/Pos3D.java b/src/api/java/mekanism/api/Pos3D.java
new file mode 100644
index 0000000..75f83a0
--- /dev/null
+++ b/src/api/java/mekanism/api/Pos3D.java
@@ -0,0 +1,458 @@
+package mekanism.api;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.util.MathHelper;
+import net.minecraft.util.MovingObjectPosition;
+import net.minecraft.util.Vec3;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Pos3D - a way of performing operations on objects in a three dimensional environment.
+ * @author aidancbrady
+ *
+ */
+public class Pos3D
+{
+ public double xPos;
+ public double yPos;
+ public double zPos;
+
+ public Pos3D()
+ {
+ this(0, 0, 0);
+ }
+
+ public Pos3D(Vec3 vec)
+ {
+ xPos = vec.xCoord;
+ yPos = vec.yCoord;
+ zPos = vec.zCoord;
+ }
+
+ public Pos3D(MovingObjectPosition mop)
+ {
+ xPos = mop.blockX;
+ yPos = mop.blockY;
+ zPos = mop.blockZ;
+ }
+
+ public Pos3D(double x, double y, double z)
+ {
+ xPos = x;
+ yPos = y;
+ zPos = z;
+ }
+
+ public Pos3D(Coord4D coord)
+ {
+ xPos = coord.xCoord;
+ yPos = coord.yCoord;
+ zPos = coord.zCoord;
+ }
+
+ /**
+ * Creates a Pos3D with an entity's posX, posY, and posZ values.
+ * @param entity - entity to create the Pos3D from
+ */
+ public Pos3D(Entity entity)
+ {
+ this(entity.posX, entity.posY, entity.posZ);
+ }
+
+ /**
+ * Creates a Pos3D with a TileEntity's xCoord, yCoord and zCoord values.
+ * @param tileEntity - TileEntity to create the Pos3D from
+ */
+ public Pos3D(TileEntity tileEntity)
+ {
+ this(tileEntity.xCoord, tileEntity.yCoord, tileEntity.zCoord);
+ }
+
+ /**
+ * Returns a new Pos3D from a tag compound.
+ * @param tag - tag compound to read from
+ * @return the Pos3D from the tag compound
+ */
+ public static Pos3D read(NBTTagCompound tag)
+ {
+ return new Pos3D(tag.getDouble("x"), tag.getDouble("y"), tag.getDouble("z"));
+ }
+
+ /**
+ * Writes this Pos3D's data to an NBTTagCompound.
+ * @param nbtTags - tag compound to write to
+ * @return the tag compound with this Pos3D's data
+ */
+ public NBTTagCompound write(NBTTagCompound nbtTags)
+ {
+ nbtTags.setDouble("x", xPos);
+ nbtTags.setDouble("y", yPos);
+ nbtTags.setDouble("z", zPos);
+
+ return nbtTags;
+ }
+
+ /**
+ * Creates and returns a Pos3D with values representing the difference between this and the Pos3D in the parameters.
+ * @param pos - Pos3D to subtract
+ * @return difference of the two Pos3Ds
+ */
+ public Pos3D diff(Pos3D pos)
+ {
+ return new Pos3D(xPos-pos.xPos, yPos-pos.yPos, zPos-pos.zPos);
+ }
+
+ /**
+ * Creates a new Pos3D from the motion of an entity.
+ * @param entity
+ * @return Pos3D representing the motion of the given entity
+ */
+ public static Pos3D fromMotion(Entity entity)
+ {
+ return new Pos3D(entity.motionX, entity.motionY, entity.motionZ);
+ }
+
+ /**
+ * Creates a new Coord4D representing this Pos3D in the provided dimension.
+ * @param dimensionId - the dimension this Pos3D is in
+ * @return Coord4D representing this Pos3D
+ */
+ public Coord4D getCoord(int dimensionId)
+ {
+ return new Coord4D((int)xPos, (int)yPos, (int)zPos, dimensionId);
+ }
+
+ /**
+ * Centres a block-derived Pos3D
+ */
+ public Pos3D centre()
+ {
+ return translate(0.5, 0.5, 0.5);
+ }
+
+ /**
+ * Translates this Pos3D by the defined values.
+ * @param x - amount to translate on the x axis
+ * @param y - amount to translate on the y axis
+ * @param z - amount to translate on the z axis
+ * @return the translated Pos3D
+ */
+ public Pos3D translate(double x, double y, double z)
+ {
+ xPos += x;
+ yPos += y;
+ zPos += z;
+
+ return this;
+ }
+
+ /**
+ * Performs the same operation as translate(x, y, z), but with a Pos3D value instead.
+ * @param pos - Pos3D value to translate by
+ * @return translated Pos3D
+ */
+ public Pos3D translate(Pos3D pos)
+ {
+ return translate(pos.xPos, pos.yPos, pos.zPos);
+ }
+
+ /**
+ * Performs the same operation as translate(x, y, z), but by a set amount in a ForgeDirection
+ */
+ public Pos3D translate(ForgeDirection direction, double amount)
+ {
+ return translate(direction.offsetX * amount, direction.offsetY * amount, direction.offsetZ * amount);
+ }
+
+ /**
+ * Performs the same operation as translate(x, y, z), but by a set amount in a ForgeDirection
+ */
+ public Pos3D translateExcludingSide(ForgeDirection direction, double amount)
+ {
+ if(direction.offsetX == 0) xPos += amount;
+ if(direction.offsetY == 0) yPos += amount;
+ if(direction.offsetZ == 0) zPos += amount;
+
+ return this;
+ }
+
+ /**
+ * Returns the distance between this and the defined Pos3D.
+ * @param pos - the Pos3D to find the distance to
+ * @return the distance between this and the defined Pos3D
+ */
+ public double distance(Pos3D pos)
+ {
+ double subX = xPos - pos.xPos;
+ double subY = yPos - pos.yPos;
+ double subZ = zPos - pos.zPos;
+ return MathHelper.sqrt_double(subX * subX + subY * subY + subZ * subZ);
+ }
+
+ /**
+ * Rotates this Pos3D by the defined yaw value.
+ * @param yaw - yaw to rotate by
+ * @return rotated Pos3D
+ */
+ public Pos3D rotateYaw(double yaw)
+ {
+ double yawRadians = Math.toRadians(yaw);
+
+ double x = xPos;
+ double z = zPos;
+
+ if(yaw != 0)
+ {
+ xPos = x * Math.cos(yawRadians) - z * Math.sin(yawRadians);
+ zPos = z * Math.cos(yawRadians) + x * Math.sin(yawRadians);
+ }
+
+ return this;
+ }
+
+ public Pos3D rotatePitch(double pitch)
+ {
+ double pitchRadians = Math.toRadians(pitch);
+
+ double y = yPos;
+ double z = zPos;
+
+ if(pitch != 0)
+ {
+ yPos = y * Math.cos(pitchRadians) - z * Math.sin(pitchRadians);
+ zPos = z * Math.cos(pitchRadians) + y * Math.sin(pitchRadians);
+ }
+
+ return this;
+ }
+
+ public Pos3D rotate(double yaw, double pitch)
+ {
+ return rotate(yaw, pitch, 0);
+ }
+
+ public Pos3D rotate(double yaw, double pitch, double roll)
+ {
+ double yawRadians = Math.toRadians(yaw);
+ double pitchRadians = Math.toRadians(pitch);
+ double rollRadians = Math.toRadians(roll);
+
+ double x = xPos;
+ double y = yPos;
+ double z = zPos;
+
+ xPos = x * Math.cos(yawRadians) * Math.cos(pitchRadians) + z * (Math.cos(yawRadians) * Math.sin(pitchRadians) * Math.sin(rollRadians) - Math.sin(yawRadians) * Math.cos(rollRadians)) + y * (Math.cos(yawRadians) * Math.sin(pitchRadians) * Math.cos(rollRadians) + Math.sin(yawRadians) * Math.sin(rollRadians));
+ zPos = x * Math.sin(yawRadians) * Math.cos(pitchRadians) + z * (Math.sin(yawRadians) * Math.sin(pitchRadians) * Math.sin(rollRadians) + Math.cos(yawRadians) * Math.cos(rollRadians)) + y * (Math.sin(yawRadians) * Math.sin(pitchRadians) * Math.cos(rollRadians) - Math.cos(yawRadians) * Math.sin(rollRadians));
+ yPos = -x * Math.sin(pitchRadians) + z * Math.cos(pitchRadians) * Math.sin(rollRadians) + y * Math.cos(pitchRadians) * Math.cos(rollRadians);
+
+ return this;
+ }
+
+ public Pos3D multiply(Pos3D pos)
+ {
+ xPos *= pos.xPos;
+ yPos *= pos.yPos;
+ zPos *= pos.zPos;
+
+ return this;
+ }
+
+ /**
+ * Scales this Pos3D by the defined x, y, an z values.
+ * @param x - x value to scale by
+ * @param y - y value to scale by
+ * @param z - z value to scale by
+ * @return scaled Pos3D
+ */
+ public Pos3D scale(double x, double y, double z)
+ {
+ xPos *= x;
+ yPos *= y;
+ zPos *= z;
+
+ return this;
+ }
+
+ /**
+ * Performs the same operation as scale(x, y, z), but with a value representing all three dimensions.
+ * @param scale - value to scale by
+ * @return scaled Pos3D
+ */
+ public Pos3D scale(double scale)
+ {
+ return scale(scale, scale, scale);
+ }
+
+ public Pos3D rotate(float angle, Pos3D axis)
+ {
+ return translateMatrix(getRotationMatrix(angle, axis), this);
+ }
+
+ public double[] getRotationMatrix(float angle)
+ {
+ double[] matrix = new double[16];
+ Pos3D axis = clone().normalize();
+
+ double x = axis.xPos;
+ double y = axis.yPos;
+ double z = axis.zPos;
+
+ angle *= 0.0174532925D;
+
+ float cos = (float)Math.cos(angle);
+ float ocos = 1.0F - cos;
+ float sin = (float)Math.sin(angle);
+
+ matrix[0] = (x * x * ocos + cos);
+ matrix[1] = (y * x * ocos + z * sin);
+ matrix[2] = (x * z * ocos - y * sin);
+ matrix[4] = (x * y * ocos - z * sin);
+ matrix[5] = (y * y * ocos + cos);
+ matrix[6] = (y * z * ocos + x * sin);
+ matrix[8] = (x * z * ocos + y * sin);
+ matrix[9] = (y * z * ocos - x * sin);
+ matrix[10] = (z * z * ocos + cos);
+ matrix[15] = 1.0F;
+
+ return matrix;
+ }
+
+ public static Pos3D translateMatrix(double[] matrix, Pos3D translation)
+ {
+ double x = translation.xPos * matrix[0] + translation.yPos * matrix[1] + translation.zPos * matrix[2] + matrix[3];
+ double y = translation.xPos * matrix[4] + translation.yPos * matrix[5] + translation.zPos * matrix[6] + matrix[7];
+ double z = translation.xPos * matrix[8] + translation.yPos * matrix[9] + translation.zPos * matrix[10] + matrix[11];
+
+ translation.xPos = x;
+ translation.yPos = y;
+ translation.zPos = z;
+
+ return translation;
+ }
+
+ public static double[] getRotationMatrix(float angle, Pos3D axis)
+ {
+ return axis.getRotationMatrix(angle);
+ }
+
+ public double anglePreNorm(Pos3D pos2)
+ {
+ return Math.acos(dotProduct(pos2));
+ }
+
+ public static double anglePreNorm(Pos3D pos1, Pos3D pos2)
+ {
+ return Math.acos(pos1.clone().dotProduct(pos2));
+ }
+
+ public double dotProduct(Pos3D pos)
+ {
+ return xPos * pos.xPos + yPos * pos.yPos + zPos * pos.zPos;
+ }
+
+ public Pos3D crossProduct(Pos3D compare)
+ {
+ return clone().toCrossProduct(compare);
+ }
+
+ public Pos3D toCrossProduct(Pos3D compare)
+ {
+ double newX = yPos * compare.zPos - zPos * compare.yPos;
+ double newY = zPos * compare.xPos - xPos * compare.zPos;
+ double newZ = xPos * compare.yPos - yPos * compare.xPos;
+
+ xPos = newX;
+ yPos = newY;
+ zPos = newZ;
+
+ return this;
+ }
+
+ public Pos3D xCrossProduct()
+ {
+ return new Pos3D(0.0D, zPos, -yPos);
+ }
+
+ public Pos3D zCrossProduct()
+ {
+ return new Pos3D(-yPos, xPos, 0.0D);
+ }
+
+ public Pos3D getPerpendicular()
+ {
+ if(zPos == 0)
+ {
+ return zCrossProduct();
+ }
+
+ return xCrossProduct();
+ }
+
+ public Pos3D floor()
+ {
+ return new Pos3D(Math.floor(xPos), Math.floor(yPos), Math.floor(zPos));
+ }
+
+ public double getMagnitude()
+ {
+ return Math.sqrt(xPos * xPos + yPos * yPos + zPos * zPos);
+ }
+
+ public Pos3D normalize()
+ {
+ double d = getMagnitude();
+
+ if (d != 0)
+ {
+ this.scale(1 / d);
+ }
+
+ return this;
+ }
+
+ public static AxisAlignedBB getAABB(Pos3D pos1, Pos3D pos2)
+ {
+ return AxisAlignedBB.getBoundingBox(
+ Math.min(pos1.xPos, pos2.xPos),
+ Math.min(pos1.yPos, pos2.yPos),
+ Math.min(pos1.zPos, pos2.zPos),
+ Math.max(pos1.xPos, pos2.xPos),
+ Math.max(pos1.yPos, pos2.yPos),
+ Math.max(pos1.zPos, pos2.zPos)
+ );
+ }
+
+ @Override
+ public Pos3D clone()
+ {
+ return new Pos3D(xPos, yPos, zPos);
+ }
+
+ @Override
+ public String toString()
+ {
+ return "[Pos3D: " + xPos + ", " + yPos + ", " + zPos + "]";
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return obj instanceof Pos3D &&
+ ((Pos3D)obj).xPos == xPos &&
+ ((Pos3D)obj).yPos == yPos &&
+ ((Pos3D)obj).zPos == zPos;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int code = 1;
+ code = 31 * code + new Double(xPos).hashCode();
+ code = 31 * code + new Double(yPos).hashCode();
+ code = 31 * code + new Double(zPos).hashCode();
+ return code;
+ }
+}
diff --git a/src/api/java/mekanism/api/Range4D.java b/src/api/java/mekanism/api/Range4D.java
new file mode 100644
index 0000000..40905a6
--- /dev/null
+++ b/src/api/java/mekanism/api/Range4D.java
@@ -0,0 +1,114 @@
+package mekanism.api;
+
+import net.minecraft.entity.player.EntityPlayer;
+import cpw.mods.fml.common.FMLCommonHandler;
+
+public class Range4D
+{
+ public int dimensionId;
+
+ public int xMin;
+ public int yMin;
+ public int zMin;
+ public int xMax;
+ public int yMax;
+ public int zMax;
+
+ public Range4D(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, int dimension)
+ {
+ xMin = minX;
+ yMin = minY;
+ zMin = minZ;
+ xMax = maxX;
+ yMax = maxY;
+ zMax = maxZ;
+
+ dimensionId = dimension;
+ }
+
+ public Range4D(Chunk3D chunk)
+ {
+ xMin = chunk.xCoord*16;
+ yMin = 0;
+ zMin = chunk.zCoord*16;
+ xMax = xMin+16;
+ yMax = 255;
+ zMax = zMin+16;
+
+ dimensionId = chunk.dimensionId;
+ }
+
+ public Range4D(Coord4D coord)
+ {
+ xMin = coord.xCoord;
+ yMin = coord.yCoord;
+ zMin = coord.zCoord;
+
+ xMax = coord.xCoord+1;
+ yMax = coord.yCoord+1;
+ zMax = coord.zCoord+1;
+
+ dimensionId = coord.dimensionId;
+ }
+
+ public static Range4D getChunkRange(EntityPlayer player)
+ {
+ int radius = FMLCommonHandler.instance().getMinecraftServerInstance().getConfigurationManager().getViewDistance();
+
+ return new Range4D(new Chunk3D(player)).expandChunks(radius);
+ }
+
+ public Range4D expandChunks(int chunks)
+ {
+ xMin -= chunks*16;
+ xMax += chunks*16;
+ zMin -= chunks*16;
+ zMax += chunks*16;
+
+ return this;
+ }
+
+ public boolean intersects(Range4D range)
+ {
+ return (xMax+1 - 1.E-05D > range.xMin) && (range.xMax+1 - 1.E-05D > xMin) && (yMax+1 - 1.E-05D > range.yMin) && (range.yMax+1 - 1.E-05D > yMin) && (zMax+1 - 1.E-05D > range.zMin) && (range.zMax+1 - 1.E-05D > zMin);
+ }
+
+ @Override
+ public Range4D clone()
+ {
+ return new Range4D(xMin, yMin, zMin, xMax, yMax, zMax, dimensionId);
+ }
+
+ @Override
+ public String toString()
+ {
+ return "[Range4D: " + xMin + ", " + yMin + ", " + zMin + ", " + xMax + ", " + yMax + ", " + zMax + ", dim=" + dimensionId + "]";
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return obj instanceof Range4D &&
+ ((Range4D)obj).xMin == xMin &&
+ ((Range4D)obj).yMin == yMin &&
+ ((Range4D)obj).zMin == zMin &&
+ ((Range4D)obj).xMax == xMax &&
+ ((Range4D)obj).yMax == yMax &&
+ ((Range4D)obj).zMax == zMax &&
+ ((Range4D)obj).dimensionId == dimensionId;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int code = 1;
+ code = 31 * code + xMin;
+ code = 31 * code + yMin;
+ code = 31 * code + zMin;
+ code = 31 * code + xMax;
+ code = 31 * code + yMax;
+ code = 31 * code + zMax;
+ code = 31 * code + dimensionId;
+ return code;
+ }
+}
diff --git a/src/api/java/mekanism/api/TabProxy.java b/src/api/java/mekanism/api/TabProxy.java
new file mode 100644
index 0000000..c562c02
--- /dev/null
+++ b/src/api/java/mekanism/api/TabProxy.java
@@ -0,0 +1,42 @@
+package mekanism.api;
+
+import net.minecraft.creativetab.CreativeTabs;
+
+/**
+ * Class used to indirectly reference the Mekanism creative tab.
+ * @author AidanBrady
+ *
+ */
+public final class TabProxy
+{
+ /** The 'Mekanism' class where the tab instance is stored. */
+ public static Class Mekanism;
+
+ /**
+ * Attempts to get the Mekanism creative tab instance from the 'Mekanism' class. Will return
+ * the tab if the mod is loaded, but otherwise will return the defined 'preferred' creative tab. This way
+ * you don't need to worry about NPEs!
+ * @return Mekanism creative tab if can, otherwise preferred tab
+ */
+ public static CreativeTabs tabMekanism(CreativeTabs preferred)
+ {
+ try {
+ if(Mekanism == null)
+ {
+ Mekanism = Class.forName("mekanism.common.Mekanism");
+ }
+
+ Object ret = Mekanism.getField("tabMekanism").get(null);
+
+ if(ret instanceof CreativeTabs)
+ {
+ return (CreativeTabs)ret;
+ }
+
+ return preferred;
+ } catch(Exception e) {
+ System.err.println("Error retrieving Mekanism creative tab.");
+ return preferred;
+ }
+ }
+}
diff --git a/src/api/java/mekanism/api/energy/EnergizedItemManager.java b/src/api/java/mekanism/api/energy/EnergizedItemManager.java
new file mode 100644
index 0000000..165bdf6
--- /dev/null
+++ b/src/api/java/mekanism/api/energy/EnergizedItemManager.java
@@ -0,0 +1,60 @@
+package mekanism.api.energy;
+
+import net.minecraft.item.ItemStack;
+
+public class EnergizedItemManager
+{
+ /**
+ * Discharges an IEnergizedItem with the defined amount of energy.
+ * @param itemStack - ItemStack to discharge
+ * @param amount - amount of energy to discharge from the item, usually the total amount of energy needed in a TileEntity
+ * @return amount of energy discharged
+ */
+ public static double discharge(ItemStack itemStack, double amount)
+ {
+ if(itemStack != null)
+ {
+ if(itemStack.getItem() instanceof IEnergizedItem)
+ {
+ IEnergizedItem energizedItem = (IEnergizedItem)itemStack.getItem();
+
+ if(energizedItem.canSend(itemStack))
+ {
+ double energyToUse = Math.min(energizedItem.getMaxTransfer(itemStack), Math.min(energizedItem.getEnergy(itemStack), amount));
+ energizedItem.setEnergy(itemStack, energizedItem.getEnergy(itemStack) - energyToUse);
+
+ return energyToUse;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Charges an IEnergizedItem with the defined amount of energy.
+ * @param itemStack - ItemStack to charge
+ * @param amount - amount of energy to charge the item with, usually the total amount of energy stored in a TileEntity
+ * @return amount of energy charged
+ */
+ public static double charge(ItemStack itemStack, double amount)
+ {
+ if(itemStack != null)
+ {
+ if(itemStack.getItem() instanceof IEnergizedItem)
+ {
+ IEnergizedItem energizedItem = (IEnergizedItem)itemStack.getItem();
+
+ if(energizedItem.canReceive(itemStack))
+ {
+ double energyToSend = Math.min(energizedItem.getMaxTransfer(itemStack), Math.min(energizedItem.getMaxEnergy(itemStack) - energizedItem.getEnergy(itemStack), amount));
+ energizedItem.setEnergy(itemStack, energizedItem.getEnergy(itemStack) + energyToSend);
+
+ return energyToSend;
+ }
+ }
+ }
+
+ return 0;
+ }
+}
diff --git a/src/api/java/mekanism/api/energy/EnergyStack.java b/src/api/java/mekanism/api/energy/EnergyStack.java
new file mode 100644
index 0000000..3f57621
--- /dev/null
+++ b/src/api/java/mekanism/api/energy/EnergyStack.java
@@ -0,0 +1,14 @@
+package mekanism.api.energy;
+
+/**
+ * Created by ben on 27/03/15.
+ */
+public class EnergyStack
+{
+ public double amount;
+
+ public EnergyStack(double newAmount)
+ {
+ amount = newAmount;
+ }
+}
diff --git a/src/api/java/mekanism/api/energy/ICableOutputter.java b/src/api/java/mekanism/api/energy/ICableOutputter.java
new file mode 100644
index 0000000..015379c
--- /dev/null
+++ b/src/api/java/mekanism/api/energy/ICableOutputter.java
@@ -0,0 +1,18 @@
+package mekanism.api.energy;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this if your TileEntity is capable of outputting energy to cables, overriding Mekanism's default implementation.
+ * @author AidanBrady
+ *
+ */
+public interface ICableOutputter
+{
+ /**
+ * Whether or not this block can output to a cable on a specific side.
+ * @param side - side to check
+ * @return if the block can output
+ */
+ public boolean canOutputTo(ForgeDirection side);
+}
diff --git a/src/api/java/mekanism/api/energy/IEnergizedItem.java b/src/api/java/mekanism/api/energy/IEnergizedItem.java
new file mode 100644
index 0000000..b672545
--- /dev/null
+++ b/src/api/java/mekanism/api/energy/IEnergizedItem.java
@@ -0,0 +1,59 @@
+package mekanism.api.energy;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * Implement this in an item's class if it should be able to store electricity.
+ * @author aidancbrady
+ *
+ */
+public interface IEnergizedItem
+{
+ /**
+ * Gets and returns the amount of energy stored in this item.
+ * @param itemStack - the ItemStack to check
+ * @return energy stored
+ */
+ public double getEnergy(ItemStack itemStack);
+
+ /**
+ * Sets this item's stored energy value to a new amount.
+ * @param itemStack - the ItemStack who's energy value is to be change
+ * @param amount - new amount of energy
+ */
+ public void setEnergy(ItemStack itemStack, double amount);
+
+ /**
+ * Gets and returns this item's maximum amount of energy that can be stored.
+ * @param itemStack - the ItemStack to check
+ * @return maximum energy
+ */
+ public double getMaxEnergy(ItemStack itemStack);
+
+ /**
+ * Gets and returns how much energy this item can transfer to and from charging slots.
+ * @param itemStack - the ItemStack to check
+ * @return transfer amount
+ */
+ public double getMaxTransfer(ItemStack itemStack);
+
+ /**
+ * Gets and returns whether or not this item can receive energy from a charging slot.
+ * @param itemStack - the ItemStack to check
+ * @return if the item can receive energy
+ */
+ public boolean canReceive(ItemStack itemStack);
+
+ /**
+ * Gets and returns whether or not this item can send energy to a charging slot.
+ * @param itemStack - the ItemStack to check
+ * @return if the item can send energy
+ */
+ public boolean canSend(ItemStack itemStack);
+
+ /**
+ * Returns whether or not this item contains metadata-specific subtypes instead of using metadata for damage display.
+ * @return if the item contains metadata-specific subtypes
+ */
+ public boolean isMetadataSpecific(ItemStack itemStack);
+}
diff --git a/src/api/java/mekanism/api/energy/IStrictEnergyAcceptor.java b/src/api/java/mekanism/api/energy/IStrictEnergyAcceptor.java
new file mode 100644
index 0000000..53edc68
--- /dev/null
+++ b/src/api/java/mekanism/api/energy/IStrictEnergyAcceptor.java
@@ -0,0 +1,25 @@
+package mekanism.api.energy;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this if your TileEntity can accept energy at a floating-point double value from Universal Cables.
+ * @author AidanBrady
+ *
+ */
+public interface IStrictEnergyAcceptor extends IStrictEnergyStorage
+{
+ /**
+ * Transfer a certain amount of energy to this acceptor.
+ * @param amount - amount to transfer
+ * @return energy used
+ */
+ public double transferEnergyToAcceptor(ForgeDirection side, double amount);
+
+ /**
+ * Whether or not this tile entity accepts energy from a certain side.
+ * @param side - side to check
+ * @return if tile entity accepts energy
+ */
+ public boolean canReceiveEnergy(ForgeDirection side);
+}
diff --git a/src/api/java/mekanism/api/energy/IStrictEnergyStorage.java b/src/api/java/mekanism/api/energy/IStrictEnergyStorage.java
new file mode 100644
index 0000000..fd560fa
--- /dev/null
+++ b/src/api/java/mekanism/api/energy/IStrictEnergyStorage.java
@@ -0,0 +1,27 @@
+package mekanism.api.energy;
+
+/**
+ * Mekanism-specific energy storage for TileEntities, already implemented in IStrictEnergyAcceptor.
+ * @author aidancbrady
+ *
+ */
+public interface IStrictEnergyStorage
+{
+ /**
+ * Gets the amount of energy this TileEntity is currently storing.
+ * @return stored energy
+ */
+ public double getEnergy();
+
+ /**
+ * Sets the amount of stored energy of this TileEntity to a new amount.
+ * @param energy - new energy value
+ */
+ public void setEnergy(double energy);
+
+ /**
+ * Gets the maximum amount of energy this TileEntity can store.
+ * @return maximum energy
+ */
+ public double getMaxEnergy();
+}
diff --git a/src/api/java/mekanism/api/energy/package-info.java b/src/api/java/mekanism/api/energy/package-info.java
new file mode 100644
index 0000000..8f3d371
--- /dev/null
+++ b/src/api/java/mekanism/api/energy/package-info.java
@@ -0,0 +1,3 @@
+@API(apiVersion = "8.0.0", owner = "Mekanism", provides = "MekanismAPI|energy")
+package mekanism.api.energy;
+import cpw.mods.fml.common.API; \ No newline at end of file
diff --git a/src/api/java/mekanism/api/gas/Gas.java b/src/api/java/mekanism/api/gas/Gas.java
new file mode 100644
index 0000000..f05be52
--- /dev/null
+++ b/src/api/java/mekanism/api/gas/Gas.java
@@ -0,0 +1,221 @@
+package mekanism.api.gas;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.IIcon;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidRegistry;
+
+/**
+ * Gas - a class used to set specific properties of gasses when used or seen in-game.
+ * @author aidancbrady
+ *
+ */
+public class Gas
+{
+ private String name;
+
+ private String unlocalizedName;
+
+ private Fluid fluid;
+
+ private IIcon icon;
+
+ private boolean visible = true;
+
+ private boolean from_fluid = false;
+
+ /**
+ * Creates a new Gas object with a defined name or key value.
+ * @param s - name or key to associate this Gas with
+ */
+ public Gas(String s)
+ {
+ unlocalizedName = name = s;
+ }
+
+ /**
+ * Creates a new Gas object that corresponds to the given Fluid
+ */
+ public Gas(Fluid f)
+ {
+ unlocalizedName = name = f.getName();
+ icon = f.getStillIcon();
+ fluid = f;
+ from_fluid = true;
+ }
+
+ /**
+ * Gets the name (key) of this Gas. This is NOT a translated or localized display name.
+ * @return this Gas's name or key
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Whether or not this is a visible gas.
+ * @return if this gas is visible
+ */
+ public boolean isVisible()
+ {
+ return visible;
+ }
+
+ /**
+ * Sets this gas's "visible" state to a new value. Setting it to 'false' will treat this gas as an internal gas, and it will not be displayed or accessed by other mods.
+ * @param v - new visible state
+ * @return this Gas object
+ */
+ public Gas setVisible(boolean v)
+ {
+ visible = v;
+
+ return this;
+ }
+
+ /**
+ * Gets the unlocalized name of this Gas.
+ * @return this Gas's unlocalized name
+ */
+ public String getUnlocalizedName()
+ {
+ return "gas." + unlocalizedName;
+ }
+
+ /**
+ * Translates this Gas's unlocalized name and returns it as localized.
+ * @return this Gas's localized name
+ */
+ public String getLocalizedName()
+ {
+ return StatCollector.translateToLocal(getUnlocalizedName());
+ }
+
+ /**
+ * Sets the unlocalized name of this Gas.
+ * @param s - unlocalized name to set
+ * @return this Gas object
+ */
+ public Gas setUnlocalizedName(String s)
+ {
+ unlocalizedName = s;
+
+ return this;
+ }
+
+ /**
+ * Gets the IIcon associated with this Gas.
+ * @return associated IIcon
+ */
+ public IIcon getIcon()
+ {
+ if(from_fluid)
+ {
+ return this.getFluid().getIcon();
+ }
+
+ return icon;
+ }
+
+ /**
+ * Sets this gas's icon.
+ * @param i - IIcon to associate with this Gas
+ * @return this Gas object
+ */
+ public Gas setIcon(IIcon i)
+ {
+ icon = i;
+
+ if(hasFluid())
+ {
+ fluid.setIcons(getIcon());
+ }
+
+ from_fluid = false;
+
+ return this;
+ }
+
+ /**
+ * Gets the ID associated with this gas.
+ * @return the associated gas ID
+ */
+ public int getID()
+ {
+ return GasRegistry.getGasID(this);
+ }
+
+ /**
+ * Writes this Gas to a defined tag compound.
+ * @param nbtTags - tag compound to write this Gas to
+ * @return the tag compound this gas was written to
+ */
+ public NBTTagCompound write(NBTTagCompound nbtTags)
+ {
+ nbtTags.setString("gasName", getName());
+
+ return nbtTags;
+ }
+
+ /**
+ * Returns the Gas stored in the defined tag compound.
+ * @param nbtTags - tag compound to get the Gas from
+ * @return Gas stored in the tag compound
+ */
+ public static Gas readFromNBT(NBTTagCompound nbtTags)
+ {
+ if(nbtTags == null || nbtTags.hasNoTags())
+ {
+ return null;
+ }
+
+ return GasRegistry.getGas(nbtTags.getString("gasName"));
+ }
+
+ /**
+ * Whether or not this Gas has an associated fluid.
+ * @return if this gas has a fluid
+ */
+ public boolean hasFluid()
+ {
+ return fluid != null;
+ }
+
+ /**
+ * Gets the fluid associated with this Gas.
+ * @return fluid associated with this gas
+ */
+ public Fluid getFluid()
+ {
+ return fluid;
+ }
+
+ /**
+ * Registers a new fluid out of this Gas or gets one from the FluidRegistry.
+ * @return this Gas object
+ */
+ public Gas registerFluid()
+ {
+ if(fluid == null)
+ {
+ if(FluidRegistry.getFluid(getName()) == null)
+ {
+ fluid = new Fluid(getName()).setGaseous(true);
+ FluidRegistry.registerFluid(fluid);
+ }
+ else {
+ fluid = FluidRegistry.getFluid(getName());
+ }
+ }
+
+ return this;
+ }
+
+ @Override
+ public String toString()
+ {
+ return name;
+ }
+}
diff --git a/src/api/java/mekanism/api/gas/GasNetwork.java b/src/api/java/mekanism/api/gas/GasNetwork.java
new file mode 100644
index 0000000..ea81d12
--- /dev/null
+++ b/src/api/java/mekanism/api/gas/GasNetwork.java
@@ -0,0 +1,358 @@
+package mekanism.api.gas;
+
+import com.google.common.collect.Lists;
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.common.eventhandler.Event;
+import mekanism.api.Coord4D;
+import mekanism.api.transmitters.DynamicNetwork;
+import mekanism.api.transmitters.IGridTransmitter;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import java.util.*;
+
+/**
+ * A DynamicNetwork extension created specifically for the transfer of Gasses. By default this is server-only, but if ticked on
+ * the client side and if it's posted events are handled properly, it has the capability to visually display gasses network-wide.
+ * @author aidancbrady
+ *
+ */
+public class GasNetwork extends DynamicNetwork<IGasHandler, GasNetwork>
+{
+ public int transferDelay = 0;
+
+ public boolean didTransfer;
+ public boolean prevTransfer;
+
+ public float gasScale;
+
+ public Gas refGas;
+
+ public GasStack buffer;
+ public int prevStored;
+
+ public int prevTransferAmount = 0;
+
+ public GasNetwork() {}
+
+ public GasNetwork(Collection<GasNetwork> networks)
+ {
+ for(GasNetwork net : networks)
+ {
+ if(net != null)
+ {
+ if(FMLCommonHandler.instance().getEffectiveSide().isClient())
+ {
+ if(net.refGas != null && net.gasScale > gasScale)
+ {
+ gasScale = net.gasScale;
+ refGas = net.refGas;
+ buffer = net.buffer;
+
+ net.gasScale = 0;
+ net.refGas = null;
+ net.buffer = null;
+ }
+ } else
+ {
+ if(net.buffer != null)
+ {
+ if(buffer == null)
+ {
+ buffer = net.buffer.copy();
+ } else
+ {
+ if(buffer.isGasEqual(net.buffer))
+ {
+ buffer.amount += net.buffer.amount;
+ }
+ else if(net.buffer.amount > buffer.amount)
+ {
+ buffer = net.buffer.copy();
+ }
+
+ }
+
+ net.buffer = null;
+ }
+ }
+
+ adoptTransmittersAndAcceptorsFrom(net);
+ net.deregister();
+ }
+ }
+
+ gasScale = getScale();
+
+ register();
+ }
+
+ @Override
+ public void absorbBuffer(IGridTransmitter<IGasHandler, GasNetwork> transmitter)
+ {
+ Object b = transmitter.getBuffer();
+
+ if(!(b instanceof GasStack) || ((GasStack)b).getGas() == null || ((GasStack)b).amount == 0)
+ {
+ return;
+ }
+
+ GasStack gas = (GasStack)b;
+
+ if(buffer == null || buffer.getGas() == null || buffer.amount == 0)
+ {
+ buffer = gas.copy();
+ gas.amount = 0;
+ return;
+ }
+
+ //TODO better multiple buffer impl
+ if(buffer.isGasEqual(gas))
+ {
+ buffer.amount += gas.amount;
+ }
+
+ gas.amount = 0;
+ }
+
+ @Override
+ public void clampBuffer()
+ {
+ if(buffer != null && buffer.amount > getCapacity())
+ {
+ buffer.amount = capacity;
+ }
+ }
+
+ public int getGasNeeded()
+ {
+ return getCapacity()-(buffer != null ? buffer.amount : 0);
+ }
+
+ public int tickEmit(GasStack stack)
+ {
+ List<IGasHandler> availableAcceptors = Lists.newArrayList();
+
+ availableAcceptors.addAll(getAcceptors(stack.getGas()));
+
+ Collections.shuffle(availableAcceptors);
+
+ int toSend = stack.amount;
+ int prevSending = toSend;
+
+ if(!availableAcceptors.isEmpty())
+ {
+ int divider = availableAcceptors.size();
+ int remaining = toSend % divider;
+ int sending = (toSend-remaining)/divider;
+
+ for(IGasHandler acceptor : availableAcceptors)
+ {
+ int currentSending = sending;
+ EnumSet<ForgeDirection> sides = acceptorDirections.get(Coord4D.get((TileEntity)acceptor));
+
+ if(remaining > 0)
+ {
+ currentSending++;
+ remaining--;
+ }
+
+ for(ForgeDirection side : sides)
+ {
+ int prev = toSend;
+
+ toSend -= acceptor.receiveGas(side, new GasStack(stack.getGas(), currentSending), true);
+
+ if(toSend < prev)
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ int sent = prevSending-toSend;
+
+ if(sent > 0 && FMLCommonHandler.instance().getEffectiveSide().isServer())
+ {
+ didTransfer = true;
+ transferDelay = 2;
+ }
+
+ return sent;
+ }
+
+ public int emit(GasStack stack, boolean doTransfer)
+ {
+ if(buffer != null && buffer.getGas() != stack.getGas())
+ {
+ return 0;
+ }
+
+ int toUse = Math.min(getGasNeeded(), stack.amount);
+
+ if(doTransfer)
+ {
+ if(buffer == null)
+ {
+ buffer = stack.copy();
+ buffer.amount = toUse;
+ }
+ else {
+ buffer.amount += toUse;
+ }
+ }
+
+ return toUse;
+ }
+
+ @Override
+ public void onUpdate()
+ {
+ super.onUpdate();
+
+ if(FMLCommonHandler.instance().getEffectiveSide().isServer())
+ {
+ prevTransferAmount = 0;
+
+ if(transferDelay == 0)
+ {
+ didTransfer = false;
+ }
+ else {
+ transferDelay--;
+ }
+
+ int stored = buffer != null ? buffer.amount : 0;
+
+ if(stored != prevStored)
+ {
+ needsUpdate = true;
+ }
+
+ prevStored = stored;
+
+ if(didTransfer != prevTransfer || needsUpdate)
+ {
+ MinecraftForge.EVENT_BUS.post(new GasTransferEvent(this, buffer, didTransfer));
+ needsUpdate = false;
+ }
+
+ prevTransfer = didTransfer;
+
+ if(buffer != null)
+ {
+ prevTransferAmount = tickEmit(buffer);
+ buffer.amount -= prevTransferAmount;
+
+ if(buffer.amount <= 0)
+ {
+ buffer = null;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void clientTick()
+ {
+ super.clientTick();
+
+ gasScale = Math.max(gasScale, getScale());
+
+ if(didTransfer && gasScale < 1)
+ {
+ gasScale = Math.max(getScale(), Math.min(1, gasScale+0.02F));
+ }
+ else if(!didTransfer && gasScale > 0)
+ {
+ gasScale = Math.max(getScale(), Math.max(0, gasScale-0.02F));
+
+ if(gasScale == 0)
+ {
+ buffer = null;
+ }
+ }
+ }
+
+ @Override
+ public Set<IGasHandler> getAcceptors(Object data)
+ {
+ Gas type = (Gas)data;
+ Set<IGasHandler> toReturn = new HashSet<IGasHandler>();
+
+ if(FMLCommonHandler.instance().getEffectiveSide().isClient())
+ {
+ return toReturn;
+ }
+
+ for(Coord4D coord : possibleAcceptors.keySet())
+ {
+ EnumSet<ForgeDirection> sides = acceptorDirections.get(coord);
+ TileEntity tile = coord.getTileEntity(getWorld());
+
+ if(!(tile instanceof IGasHandler) || sides == null || sides.isEmpty())
+ {
+ continue;
+ }
+
+ IGasHandler acceptor = (IGasHandler)tile;
+
+ for(ForgeDirection side : sides)
+ {
+ if(acceptor != null && acceptor.canReceiveGas(side, type))
+ {
+ toReturn.add(acceptor);
+ break;
+ }
+ }
+ }
+
+ return toReturn;
+ }
+
+ public static class GasTransferEvent extends Event
+ {
+ public final GasNetwork gasNetwork;
+
+ public final GasStack transferType;
+ public final boolean didTransfer;
+
+ public GasTransferEvent(GasNetwork network, GasStack type, boolean did)
+ {
+ gasNetwork = network;
+ transferType = type;
+ didTransfer = did;
+ }
+ }
+
+ public float getScale()
+ {
+ return Math.min(1, (buffer == null || getCapacity() == 0 ? 0 : (float)buffer.amount/getCapacity()));
+ }
+
+ @Override
+ public String toString()
+ {
+ return "[GasNetwork] " + transmitters.size() + " transmitters, " + possibleAcceptors.size() + " acceptors.";
+ }
+
+ @Override
+ public String getNeededInfo()
+ {
+ return Integer.toString(getGasNeeded());
+ }
+
+ @Override
+ public String getStoredInfo()
+ {
+ return buffer != null ? buffer.getGas().getLocalizedName() + " (" + buffer.amount + ")" : "None";
+ }
+
+ @Override
+ public String getFlowInfo()
+ {
+ return Integer.toString(prevTransferAmount) + "/t";
+ }
+}
diff --git a/src/api/java/mekanism/api/gas/GasRegistry.java b/src/api/java/mekanism/api/gas/GasRegistry.java
new file mode 100644
index 0000000..25db966
--- /dev/null
+++ b/src/api/java/mekanism/api/gas/GasRegistry.java
@@ -0,0 +1,113 @@
+package mekanism.api.gas;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraftforge.fluids.Fluid;
+
+public class GasRegistry
+{
+ private static ArrayList<Gas> registeredGasses = new ArrayList<Gas>();
+
+ /**
+ * Register a new gas into GasRegistry.
+ * @param gas - Gas to register
+ * @return the gas that has been registered, pulled right out of GasRegistry
+ */
+ public static Gas register(Gas gas)
+ {
+ if(gas == null)
+ {
+ return null;
+ }
+
+ registeredGasses.add(gas);
+
+ return getGas(gas.getName());
+ }
+
+ /**
+ * Gets the gas associated with the defined ID.
+ * @param id - ID to check
+ * @return gas associated with defined ID
+ */
+ public static Gas getGas(int id)
+ {
+ if(id == -1)
+ {
+ return null;
+ }
+
+ return registeredGasses.get(id);
+ }
+
+ /**
+ * Gets the gas associated with the defined fluid.
+ * @param f - fluid to check
+ * @return the gas associated with the fluid
+ */
+ public static Gas getGas(Fluid f)
+ {
+ for(Gas gas : getRegisteredGasses())
+ {
+ if(gas.hasFluid() && gas.getFluid() == f)
+ {
+ return gas;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Whether or not GasRegistry contains a gas with the specified name
+ * @param name - name to check
+ * @return if GasRegistry contains a gas with the defined name
+ */
+ public static boolean containsGas(String name)
+ {
+ return getGas(name) != null;
+ }
+
+ /**
+ * Gets the list of all gasses registered in GasRegistry.
+ * @return a cloned list of all registered gasses
+ */
+ public static List<Gas> getRegisteredGasses()
+ {
+ return (List<Gas>)registeredGasses.clone();
+ }
+
+ /**
+ * Gets the gas associated with the specified name.
+ * @param name - name of the gas to get
+ * @return gas associated with the name
+ */
+ public static Gas getGas(String name)
+ {
+ for(Gas gas : registeredGasses)
+ {
+ if(gas.getName().toLowerCase().equals(name.toLowerCase()))
+ {
+ return gas;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets the gas ID of a specified gas.
+ * @param gas - gas to get the ID from
+ * @return gas ID
+ */
+ public static int getGasID(Gas gas)
+ {
+ if(gas == null || !containsGas(gas.getName()))
+ {
+ return -1;
+ }
+
+ return registeredGasses.indexOf(gas);
+ }
+}
diff --git a/src/api/java/mekanism/api/gas/GasStack.java b/src/api/java/mekanism/api/gas/GasStack.java
new file mode 100644
index 0000000..05e5ae6
--- /dev/null
+++ b/src/api/java/mekanism/api/gas/GasStack.java
@@ -0,0 +1,132 @@
+package mekanism.api.gas;
+
+import net.minecraft.nbt.NBTTagCompound;
+
+/**
+ * GasStack - a specified amount of a defined Gas with certain properties.
+ * @author aidancbrady
+ *
+ */
+public class GasStack
+{
+ private Gas type;
+
+ public int amount;
+
+ /**
+ * Creates a new GasStack with a defined gas ID and quantity.
+ * @param id - gas ID to associate this GasStack to, will perform a GasRegistry lookup in the constructor
+ * @param quantity - amount of gas to be referenced in this GasStack
+ */
+ public GasStack(int id, int quantity)
+ {
+ type = GasRegistry.getGas(id);
+ amount = quantity;
+ }
+
+ /**
+ * Creates a new GasStack with a defined Gas type and quantity.
+ * @param gas - gas type of the stack
+ * @param quantity - amount of gas to be referenced in this GasStack
+ */
+ public GasStack(Gas gas, int quantity)
+ {
+ type = gas;
+ amount = quantity;
+ }
+
+ private GasStack() {}
+
+ /**
+ * Gets the Gas type of this GasStack.
+ * @return this GasStack's Gas type
+ */
+ public Gas getGas()
+ {
+ return type;
+ }
+
+ public GasStack withAmount(int newAmount)
+ {
+ amount = newAmount;
+
+ return this;
+ }
+
+ /**
+ * Writes this GasStack to a defined tag compound.
+ * @param nbtTags - tag compound to write to
+ * @return tag compound with this GasStack's data
+ */
+ public NBTTagCompound write(NBTTagCompound nbtTags)
+ {
+ type.write(nbtTags);
+ nbtTags.setInteger("amount", amount);
+
+ return nbtTags;
+ }
+
+ /**
+ * Reads this GasStack's data from a defined tag compound.
+ * @param nbtTags - tag compound to read from
+ */
+ public void read(NBTTagCompound nbtTags)
+ {
+ type = Gas.readFromNBT(nbtTags);
+ amount = nbtTags.getInteger("amount");
+ }
+
+ /**
+ * Returns the GasStack stored in the defined tag compound, or null if it doesn't exist.
+ * @param nbtTags - tag compound to read from
+ * @return GasStack stored in the tag compound
+ */
+ public static GasStack readFromNBT(NBTTagCompound nbtTags)
+ {
+ if(nbtTags == null || nbtTags.hasNoTags())
+ {
+ return null;
+ }
+
+ GasStack stack = new GasStack();
+ stack.read(nbtTags);
+
+ if(stack.getGas() == null || stack.amount <= 0)
+ {
+ return null;
+ }
+
+ return stack;
+ }
+
+ /**
+ * Returns a copied form of this GasStack.
+ * @return copied GasStack
+ */
+ public GasStack copy()
+ {
+ return new GasStack(type, amount);
+ }
+
+ /**
+ * Whether or not this GasStack's gas type is equal to the other defined GasStack.
+ * @param stack - GasStack to check
+ * @return if the GasStacks contain the same gas type
+ */
+ public boolean isGasEqual(GasStack stack)
+ {
+ return stack != null && getGas() == stack.getGas();
+ }
+
+ @Override
+ public String toString()
+ {
+ return "[" + type + ", " + amount + "]";
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return type == null ? 0 : type.getID();
+ }
+}
diff --git a/src/api/java/mekanism/api/gas/GasTank.java b/src/api/java/mekanism/api/gas/GasTank.java
new file mode 100644
index 0000000..a5d4c20
--- /dev/null
+++ b/src/api/java/mekanism/api/gas/GasTank.java
@@ -0,0 +1,252 @@
+package mekanism.api.gas;
+
+import net.minecraft.nbt.NBTTagCompound;
+
+/**
+ * An optional way of managing and/or storing gasses. Would be very useful in TileEntity and Entity gas storage.
+ * @author aidancbrady
+ *
+ */
+public class GasTank
+{
+ public GasStack stored;
+
+ private int maxGas;
+
+ private GasTank() {}
+
+ /**
+ * Creates a tank with a defined capacity.
+ * @param max - the maximum amount of gas this GasTank can hold
+ */
+ public GasTank(int max)
+ {
+ maxGas = max;
+ }
+
+ /**
+ * Sets this tank's GasStack value to a new value. Will cap the amount to this GasTank's capacity.
+ * @param stack - value to set this tank's GasStack value to
+ */
+ public void setGas(GasStack stack)
+ {
+ stored = stack;
+
+ if(stored != null)
+ {
+ stored.amount = Math.min(getMaxGas(), stored.amount);
+ }
+ }
+
+ /**
+ * Draws a specified amount of gas out of this tank.
+ * @param amount - amount to draw
+ * @param doDraw - if the gas should actually be removed from this tank
+ * @return gas taken from this GasTank as a GasStack value
+ */
+ public GasStack draw(int amount, boolean doDraw)
+ {
+ if(stored == null || amount <= 0)
+ {
+ return null;
+ }
+
+ GasStack ret = new GasStack(getGas().getGas(), Math.min(getStored(), amount));
+
+ if(ret.amount > 0)
+ {
+ if(doDraw)
+ {
+ stored.amount -= ret.amount;
+
+ if(stored.amount <= 0)
+ {
+ stored = null;
+ }
+ }
+
+ return ret;
+ }
+
+ return null;
+ }
+
+ /**
+ * Adds a specified amount of gas to this tank.
+ * @param amount - the GasStack for this tank to receive
+ * @param doReceive - if the gas should actually be added to this tank
+ * @return the amount of gas accepted by this tank
+ */
+ public int receive(GasStack amount, boolean doReceive)
+ {
+ if(amount == null || (stored != null && !(stored.amount != getMaxGas() && stored.isGasEqual(amount))))
+ {
+ return 0;
+ }
+
+ int toFill = Math.min(getMaxGas()-getStored(), amount.amount);
+
+ if(doReceive)
+ {
+ if(stored == null)
+ {
+ stored = amount.copy().withAmount(getStored()+toFill);
+ }
+ else {
+ stored.amount = Math.min(getMaxGas(), getStored()+amount.amount);
+ }
+ }
+
+ return toFill;
+ }
+
+ /**
+ * If this GasTank can receive the specified type of gas. Will return false if this tank does not need anymore gas.
+ * @param gas - gas to check
+ * @return if this GasTank can accept the defined gas
+ */
+ public boolean canReceive(Gas gas)
+ {
+ if(getNeeded() == 0 || stored != null && (gas != null && gas != stored.getGas()))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * If this GasTank can receive the specified type of gas. Will return TRUE if this tank does not need anymore gas.
+ * @param gas - gas to check
+ * @return if this GasTank can accept the defined gas
+ */
+ public boolean canReceiveType(Gas gas)
+ {
+ if(stored != null && (gas != null && gas != stored.getGas()))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * If this GasTank can be drawn of the specified type of gas. Will return false if this tank does not contain any gas.
+ * @param gas - gas to check
+ * @return if this GasTank can be drawn of the defined gas
+ */
+ public boolean canDraw(Gas gas)
+ {
+ if(stored == null || (gas != null && gas != stored.getGas()))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Gets the amount of gas needed by this GasTank.
+ * @return
+ */
+ public int getNeeded()
+ {
+ return getMaxGas()-getStored();
+ }
+
+ /**
+ * Gets the maximum amount of gas this tank can hold.
+ * @return - max gas
+ */
+ public int getMaxGas()
+ {
+ return maxGas;
+ }
+
+ /**
+ * Sets the maximum amount of gas this tank can hold
+ */
+ public void setMaxGas(int capacity)
+ {
+ maxGas = capacity;
+ }
+
+ /**
+ * Gets the GasStack held by this GasTank.
+ * @return - GasStakc held by this tank
+ */
+ public GasStack getGas()
+ {
+ return stored;
+ }
+
+ /**
+ * Gets the type of gas currently stored in this GasTank.
+ * @return gas type contained
+ */
+ public Gas getGasType()
+ {
+ return stored != null ? stored.getGas() : null;
+ }
+
+ /**
+ * Gets the amount of gas stored by this GasTank.
+ * @return amount of gas stored
+ */
+ public int getStored()
+ {
+ return stored != null ? stored.amount : 0;
+ }
+
+ /**
+ * Writes this tank to a defined tag compound.
+ * @param nbtTags - tag compound to write to
+ * @return tag compound with this tank's data
+ */
+ public NBTTagCompound write(NBTTagCompound nbtTags)
+ {
+ if(stored != null && stored.getGas() != null)
+ {
+ nbtTags.setTag("stored", stored.write(new NBTTagCompound()));
+ }
+
+ nbtTags.setInteger("maxGas", maxGas);
+
+ return nbtTags;
+ }
+
+ /**
+ * Reads this tank's data from a defined tag compound.
+ * @param nbtTags - tag compound to read from
+ */
+ public void read(NBTTagCompound nbtTags)
+ {
+ if(nbtTags.hasKey("stored"))
+ {
+ stored = GasStack.readFromNBT(nbtTags.getCompoundTag("stored"));
+ }
+
+ if(nbtTags.hasKey("maxGas") && nbtTags.getInteger("maxGas") != 0)
+ {
+ maxGas = nbtTags.getInteger("maxGas");
+ }
+ }
+
+ /**
+ * Returns the tank stored in the defined tag compound, or null if it doesn't exist.
+ * @param nbtTags - tag compound to read from
+ * @return tank stored in the tag compound
+ */
+ public static GasTank readFromNBT(NBTTagCompound nbtTags)
+ {
+ if(nbtTags == null || nbtTags.hasNoTags())
+ {
+ return null;
+ }
+
+ GasTank tank = new GasTank();
+ tank.read(nbtTags);
+
+ return tank;
+ }
+}
diff --git a/src/api/java/mekanism/api/gas/GasTransmission.java b/src/api/java/mekanism/api/gas/GasTransmission.java
new file mode 100644
index 0000000..c043eb8
--- /dev/null
+++ b/src/api/java/mekanism/api/gas/GasTransmission.java
@@ -0,0 +1,181 @@
+package mekanism.api.gas;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import mekanism.api.Coord4D;
+import mekanism.api.transmitters.ITransmitterTile;
+import mekanism.api.transmitters.TransmissionType;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * A handy class containing several utilities for efficient gas transfer.
+ * @author AidanBrady
+ *
+ */
+public final class GasTransmission
+{
+ /**
+ * Gets all the acceptors around a tile entity.
+ * @param tileEntity - center tile entity
+ * @return array of IGasAcceptors
+ */
+ public static IGasHandler[] getConnectedAcceptors(TileEntity tileEntity)
+ {
+ IGasHandler[] acceptors = new IGasHandler[] {null, null, null, null, null, null};
+
+ for(ForgeDirection orientation : ForgeDirection.VALID_DIRECTIONS)
+ {
+ TileEntity acceptor = Coord4D.get(tileEntity).getFromSide(orientation).getTileEntity(tileEntity.getWorldObj());
+
+ if(acceptor instanceof IGasHandler)
+ {
+ acceptors[orientation.ordinal()] = (IGasHandler)acceptor;
+ }
+ }
+
+ return acceptors;
+ }
+
+ /**
+ * Gets all the tube connections around a tile entity.
+ * @param tileEntity - center tile entity
+ * @return array of ITubeConnections
+ */
+ public static ITubeConnection[] getConnections(TileEntity tileEntity)
+ {
+ ITubeConnection[] connections = new ITubeConnection[] {null, null, null, null, null, null};
+
+ for(ForgeDirection orientation : ForgeDirection.VALID_DIRECTIONS)
+ {
+ TileEntity connection = Coord4D.get(tileEntity).getFromSide(orientation).getTileEntity(tileEntity.getWorldObj());
+
+ if(canConnect(connection, orientation))
+ {
+ connections[orientation.ordinal()] = (ITubeConnection)connection;
+ }
+ }
+
+ return connections;
+ }
+
+ /**
+ * Whether or not a TileEntity can connect to a specified tile on a specified side.
+ * @param tileEntity - TileEntity to attempt connection to
+ * @param side - side to attempt connection on
+ * @return if this tile and side are connectable
+ */
+ public static boolean canConnect(TileEntity tileEntity, ForgeDirection side)
+ {
+ if(tileEntity instanceof ITubeConnection && (!(tileEntity instanceof ITransmitterTile) || TransmissionType.checkTransmissionType(((ITransmitterTile)tileEntity).getTransmitter(), TransmissionType.GAS)))
+ {
+ if(((ITubeConnection)tileEntity).canTubeConnect(side.getOpposite()))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Removes a specified amount of gas from an IGasItem.
+ * @param itemStack - ItemStack of the IGasItem
+ * @param type - type of gas to remove from the IGasItem, null if it doesn't matter
+ * @param amount - amount of gas to remove from the ItemStack
+ * @return the GasStack removed by the IGasItem
+ */
+ public static GasStack removeGas(ItemStack itemStack, Gas type, int amount)
+ {
+ if(itemStack != null && itemStack.getItem() instanceof IGasItem)
+ {
+ IGasItem item = (IGasItem)itemStack.getItem();
+
+ if(type != null && item.getGas(itemStack) != null && item.getGas(itemStack).getGas() != type || !item.canProvideGas(itemStack, type))
+ {
+ return null;
+ }
+
+ return item.removeGas(itemStack, amount);
+ }
+
+ return null;
+ }
+
+ /**
+ * Adds a specified amount of gas to an IGasItem.
+ * @param itemStack - ItemStack of the IGasItem
+ * @param stack - stack to add to the IGasItem
+ * @return amount of gas accepted by the IGasItem
+ */
+ public static int addGas(ItemStack itemStack, GasStack stack)
+ {
+ if(itemStack != null && itemStack.getItem() instanceof IGasItem && ((IGasItem)itemStack.getItem()).canReceiveGas(itemStack, stack.getGas()))
+ {
+ return ((IGasItem)itemStack.getItem()).addGas(itemStack, stack.copy());
+ }
+
+ return 0;
+ }
+
+ /**
+ * Emits gas from a central block by splitting the received stack among the sides given.
+ * @param sides - the list of sides to output from
+ * @param stack - the stack to output
+ * @param from - the TileEntity to output from
+ * @return the amount of gas emitted
+ */
+ public static int emit(List<ForgeDirection> sides, GasStack stack, TileEntity from)
+ {
+ if(stack == null)
+ {
+ return 0;
+ }
+
+ List<IGasHandler> availableAcceptors = new ArrayList<IGasHandler>();
+ IGasHandler[] possibleAcceptors = getConnectedAcceptors(from);
+
+ for(int i = 0; i < possibleAcceptors.length; i++)
+ {
+ IGasHandler handler = possibleAcceptors[i];
+
+ if(handler != null && handler.canReceiveGas(ForgeDirection.getOrientation(i).getOpposite(), stack.getGas()))
+ {
+ availableAcceptors.add(handler);
+ }
+ }
+
+ Collections.shuffle(availableAcceptors);
+
+ int toSend = stack.amount;
+ int prevSending = toSend;
+
+ if(!availableAcceptors.isEmpty())
+ {
+ int divider = availableAcceptors.size();
+ int remaining = toSend % divider;
+ int sending = (toSend-remaining)/divider;
+
+ for(IGasHandler acceptor : availableAcceptors)
+ {
+ int currentSending = sending;
+
+ if(remaining > 0)
+ {
+ currentSending++;
+ remaining--;
+ }
+
+ ForgeDirection dir = ForgeDirection.getOrientation(Arrays.asList(possibleAcceptors).indexOf(acceptor)).getOpposite();
+ toSend -= acceptor.receiveGas(dir, new GasStack(stack.getGas(), currentSending), true);
+ }
+ }
+
+ return prevSending-toSend;
+ }
+}
diff --git a/src/api/java/mekanism/api/gas/IGasHandler.java b/src/api/java/mekanism/api/gas/IGasHandler.java
new file mode 100644
index 0000000..e069523
--- /dev/null
+++ b/src/api/java/mekanism/api/gas/IGasHandler.java
@@ -0,0 +1,47 @@
+package mekanism.api.gas;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this if your tile entity accepts gas from an external source.
+ * @author AidanBrady
+ *
+ */
+public interface IGasHandler
+{
+ /**
+ * Transfer a certain amount of gas to this block.
+ * @param stack - gas to add
+ * @return gas added
+ */
+ public int receiveGas(ForgeDirection side, GasStack stack, boolean doTransfer);
+
+ @Deprecated
+ public int receiveGas(ForgeDirection side, GasStack stack);
+
+ /**
+ * Draws a certain amount of gas from this block.
+ * @param amount - amount to draw
+ * @return gas drawn
+ */
+ public GasStack drawGas(ForgeDirection side, int amount, boolean doTransfer);
+
+ @Deprecated
+ public GasStack drawGas(ForgeDirection side, int amount);
+
+ /**
+ * Whether or not this block can accept gas from a certain side.
+ * @param side - side to check
+ * @param type - type of gas to check
+ * @return if block accepts gas
+ */
+ public boolean canReceiveGas(ForgeDirection side, Gas type);
+
+ /**
+ * Whether or not this block can be drawn of gas from a certain side.
+ * @param side - side to check
+ * @param type - type of gas to check
+ * @return if block can be drawn of gas
+ */
+ public boolean canDrawGas(ForgeDirection side, Gas type);
+}
diff --git a/src/api/java/mekanism/api/gas/IGasItem.java b/src/api/java/mekanism/api/gas/IGasItem.java
new file mode 100644
index 0000000..9c7d332
--- /dev/null
+++ b/src/api/java/mekanism/api/gas/IGasItem.java
@@ -0,0 +1,81 @@
+package mekanism.api.gas;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * Implement this in your item class if it can store or transfer certain gasses.
+ * @author AidanBrady
+ *
+ */
+public interface IGasItem
+{
+ /**
+ * Gets the rate of transfer this item can handle.
+ * @return
+ */
+ public int getRate(ItemStack itemstack);
+
+ /**
+ * Adds a defined amount of a certain gas to an item.
+ * @param itemstack - the itemstack to add gas to
+ * @param type - the type of gas to add
+ * @param amount - the amount of gas to add
+ * @return the gas that was accepted by the item
+ */
+ public int addGas(ItemStack itemstack, GasStack stack);
+
+ /**
+ * Removes the defined amount of a certain gas from the item.
+ * @param itemstack - the itemstack to remove gas from
+ * @param type - the type of gas to remove
+ * @param amount - the amount of gas to remove
+ * @return the gas that was removed by the item
+ */
+ public GasStack removeGas(ItemStack itemstack, int amount);
+
+ /**
+ * Whether or not this storage tank be given a specific gas.
+ * @param itemstack - the itemstack to check
+ * @param type - the type of gas the tank can possibly receive
+ * @return if the item be charged
+ */
+ public boolean canReceiveGas(ItemStack itemstack, Gas type);
+
+ /**
+ * Whether or not this item can give a gas receiver a certain type of gas.
+ * @param itemstack - the itemstack to check
+ * @param type - the type of gas the tank can provide
+ * @return if the item can provide gas
+ */
+ public boolean canProvideGas(ItemStack itemstack, Gas type);
+
+ /**
+ * Get the gas of a declared type.
+ * @param type - type of gas
+ * @param data - ItemStack parameter if necessary
+ * @return gas stored
+ */
+ public GasStack getGas(ItemStack itemstack);
+
+ /**
+ * Set the gas of a declared type to a new amount;
+ * @param type - type of gas
+ * @param data - ItemStack parameter if necessary
+ * @param amount - amount to store
+ */
+ public void setGas(ItemStack itemstack, GasStack stack);
+
+ /**
+ * Gets the maximum amount of gas this tile entity can store.
+ * @param type - type of gas
+ * @param data - ItemStack parameter if necessary
+ * @return maximum gas
+ */
+ public int getMaxGas(ItemStack itemstack);
+
+ /**
+ * Returns whether or not this item contains metadata-specific subtypes instead of using metadata for damage display.
+ * @return if the item contains metadata-specific subtypes
+ */
+ public boolean isMetadataSpecific(ItemStack itemstack);
+}
diff --git a/src/api/java/mekanism/api/gas/IGasTransmitter.java b/src/api/java/mekanism/api/gas/IGasTransmitter.java
new file mode 100644
index 0000000..57ec4c1
--- /dev/null
+++ b/src/api/java/mekanism/api/gas/IGasTransmitter.java
@@ -0,0 +1,10 @@
+package mekanism.api.gas;
+
+import mekanism.api.transmitters.IGridTransmitter;
+
+import net.minecraft.tileentity.TileEntity;
+
+public interface IGasTransmitter extends IGridTransmitter<IGasHandler, GasNetwork>
+{
+ public boolean canTransferGasToTube(TileEntity tile);
+}
diff --git a/src/api/java/mekanism/api/gas/ITubeConnection.java b/src/api/java/mekanism/api/gas/ITubeConnection.java
new file mode 100644
index 0000000..b669fed
--- /dev/null
+++ b/src/api/java/mekanism/api/gas/ITubeConnection.java
@@ -0,0 +1,18 @@
+package mekanism.api.gas;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this if your block can connect to Pressurized Tubes.
+ * @author AidanBrady
+ *
+ */
+public interface ITubeConnection
+{
+ /**
+ * Whether or not a tube can connect to a certain orientation.
+ * @param side - orientation to check
+ * @return if a tube can connect
+ */
+ public boolean canTubeConnect(ForgeDirection side);
+}
diff --git a/src/api/java/mekanism/api/gas/OreGas.java b/src/api/java/mekanism/api/gas/OreGas.java
new file mode 100644
index 0000000..316169c
--- /dev/null
+++ b/src/api/java/mekanism/api/gas/OreGas.java
@@ -0,0 +1,38 @@
+package mekanism.api.gas;
+
+import net.minecraft.util.StatCollector;
+
+public class OreGas extends Gas
+{
+ private String oreName;
+ private OreGas cleanGas;
+
+ public OreGas(String s, String name)
+ {
+ super(s);
+
+ oreName = name;
+ }
+
+ public boolean isClean()
+ {
+ return getCleanGas() == null;
+ }
+
+ public OreGas getCleanGas()
+ {
+ return cleanGas;
+ }
+
+ public OreGas setCleanGas(OreGas gas)
+ {
+ cleanGas = gas;
+
+ return this;
+ }
+
+ public String getOreName()
+ {
+ return StatCollector.translateToLocal(oreName);
+ }
+}
diff --git a/src/api/java/mekanism/api/gas/package-info.java b/src/api/java/mekanism/api/gas/package-info.java
new file mode 100644
index 0000000..4418451
--- /dev/null
+++ b/src/api/java/mekanism/api/gas/package-info.java
@@ -0,0 +1,3 @@
+@API(apiVersion = "8.0.0", owner = "Mekanism", provides = "MekanismAPI|gas")
+package mekanism.api.gas;
+import cpw.mods.fml.common.API; \ No newline at end of file
diff --git a/src/api/java/mekanism/api/infuse/InfuseObject.java b/src/api/java/mekanism/api/infuse/InfuseObject.java
new file mode 100644
index 0000000..a86c02f
--- /dev/null
+++ b/src/api/java/mekanism/api/infuse/InfuseObject.java
@@ -0,0 +1,21 @@
+package mekanism.api.infuse;
+
+/**
+ * InfuseObject - an object associated with an ItemStack that can modify a Metallurgic Infuser's internal infuse.
+ * @author AidanBrady
+ *
+ */
+public class InfuseObject
+{
+ /** The type of infuse this item stores */
+ public InfuseType type;
+
+ /** How much infuse this item stores */
+ public int stored;
+
+ public InfuseObject(InfuseType infusion, int i)
+ {
+ type = infusion;
+ stored = i;
+ }
+}
diff --git a/src/api/java/mekanism/api/infuse/InfuseRegistry.java b/src/api/java/mekanism/api/infuse/InfuseRegistry.java
new file mode 100644
index 0000000..308b9c5
--- /dev/null
+++ b/src/api/java/mekanism/api/infuse/InfuseRegistry.java
@@ -0,0 +1,113 @@
+package mekanism.api.infuse;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * Use this class to add a new object that registers as an infuse object.
+ * @author AidanBrady
+ *
+ */
+public class InfuseRegistry
+{
+ /** The (private) map of ItemStacks and their related InfuseObjects. */
+ private static Map<ItemStack, InfuseObject> infuseObjects = new HashMap<ItemStack, InfuseObject>();
+
+ /** The (private) map of infuse names and their corresponding InfuseTypes. */
+ private static Map<String, InfuseType> infuseTypes = new HashMap<String, InfuseType>();
+
+ /**
+ * Registers an InfuseType into the registry. Call this in PreInit!
+ * @param infuse
+ */
+ public static void registerInfuseType(InfuseType infuse)
+ {
+ if(infuseTypes.containsKey(infuse.name))
+ {
+ return;
+ }
+
+ infuseTypes.put(infuse.name, infuse);
+ }
+
+ /**
+ * Gets an InfuseType from it's name, or null if it doesn't exist.
+ * @param name - the name of the InfuseType to get
+ * @return the name's corresponding InfuseType
+ */
+ public static InfuseType get(String name)
+ {
+ if(name.equals("null"))
+ {
+ return null;
+ }
+
+ return infuseTypes.get(name);
+ }
+
+ /**
+ * Whether or not the registry contains a correspondent InfuseType to a name.
+ * @param name - the name to check
+ * @return if the name has a coorespondent InfuseType
+ */
+ public static boolean contains(String name)
+ {
+ return get(name) != null;
+ }
+
+ /**
+ * Registers a block or item that serves as an infuse object. An infuse object will store a certain type and amount of infuse,
+ * and will deliver this amount to the Metallurgic Infuser's buffer of infuse. The item's stack size will be decremented when
+ * it is placed in the Metallurgic Infuser's infuse slot, and the machine can accept the type and amount of infuse stored in the
+ * object.
+ * @param itemStack - stack the infuse object is linked to -- stack size is ignored
+ * @param infuseObject - the infuse object with the type and amount data
+ */
+ public static void registerInfuseObject(ItemStack itemStack, InfuseObject infuseObject)
+ {
+ if(getObject(itemStack) != null)
+ {
+ return;
+ }
+
+ infuseObjects.put(itemStack, infuseObject);
+ }
+
+ /**
+ * Gets the InfuseObject data from an ItemStack.
+ * @param itemStack - the ItemStack to check
+ * @return the ItemStack's InfuseObject
+ */
+ public static InfuseObject getObject(ItemStack itemStack)
+ {
+ for(Map.Entry<ItemStack, InfuseObject> obj : infuseObjects.entrySet())
+ {
+ if(itemStack.isItemEqual(obj.getKey()))
+ {
+ return obj.getValue();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets the private map for InfuseObjects.
+ * @return private InfuseObject map
+ */
+ public static final Map<ItemStack, InfuseObject> getObjectMap()
+ {
+ return infuseObjects;
+ }
+
+ /**
+ * Gets the private map for InfuseTypes.
+ * @return private InfuseType map
+ */
+ public static final Map<String, InfuseType> getInfuseMap()
+ {
+ return infuseTypes;
+ }
+}
diff --git a/src/api/java/mekanism/api/infuse/InfuseType.java b/src/api/java/mekanism/api/infuse/InfuseType.java
new file mode 100644
index 0000000..1f5215c
--- /dev/null
+++ b/src/api/java/mekanism/api/infuse/InfuseType.java
@@ -0,0 +1,47 @@
+package mekanism.api.infuse;
+
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.StatCollector;
+
+/**
+ * The types of infuse currently available in Mekanism.
+ * @author AidanBrady
+ *
+ */
+public final class InfuseType
+{
+ /** The name of this infusion */
+ public String name;
+
+ /** The location of this infuse's GUI texture */
+ public ResourceLocation texture;
+
+ /** The infuse's GUI texture X offset. */
+ public int texX;
+
+ /** The infuse's GUI texture Y offset. */
+ public int texY;
+
+ /** The unlocalized name of this type. */
+ public String unlocalizedName;
+
+ public InfuseType(String s, ResourceLocation location, int x, int y)
+ {
+ name = s;
+ texture = location;
+ texX = x;
+ texY = y;
+ }
+
+ public InfuseType setUnlocalizedName(String name)
+ {
+ unlocalizedName = name;
+
+ return this;
+ }
+
+ public String getLocalizedName()
+ {
+ return StatCollector.translateToLocal(unlocalizedName);
+ }
+}
diff --git a/src/api/java/mekanism/api/infuse/package-info.java b/src/api/java/mekanism/api/infuse/package-info.java
new file mode 100644
index 0000000..8718996
--- /dev/null
+++ b/src/api/java/mekanism/api/infuse/package-info.java
@@ -0,0 +1,3 @@
+@API(apiVersion = "8.0.0", owner = "Mekanism", provides = "MekanismAPI|infuse")
+package mekanism.api.infuse;
+import cpw.mods.fml.common.API; \ No newline at end of file
diff --git a/src/api/java/mekanism/api/lasers/ILaserReceptor.java b/src/api/java/mekanism/api/lasers/ILaserReceptor.java
new file mode 100644
index 0000000..641f3ca
--- /dev/null
+++ b/src/api/java/mekanism/api/lasers/ILaserReceptor.java
@@ -0,0 +1,10 @@
+package mekanism.api.lasers;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+public interface ILaserReceptor
+{
+ public void receiveLaserEnergy(double energy, ForgeDirection side);
+
+ public boolean canLasersDig();
+}
diff --git a/src/api/java/mekanism/api/lasers/package-info.java b/src/api/java/mekanism/api/lasers/package-info.java
new file mode 100644
index 0000000..b456937
--- /dev/null
+++ b/src/api/java/mekanism/api/lasers/package-info.java
@@ -0,0 +1,3 @@
+@API(apiVersion = "8.0.0", owner = "Mekanism", provides = "MekanismAPI|laser")
+package mekanism.api.lasers;
+import cpw.mods.fml.common.API; \ No newline at end of file
diff --git a/src/api/java/mekanism/api/package-info.java b/src/api/java/mekanism/api/package-info.java
new file mode 100644
index 0000000..2b9fcab
--- /dev/null
+++ b/src/api/java/mekanism/api/package-info.java
@@ -0,0 +1,3 @@
+@API(apiVersion = "8.0.0", owner = "Mekanism", provides = "MekanismAPI|core")
+package mekanism.api;
+import cpw.mods.fml.common.API; \ No newline at end of file
diff --git a/src/api/java/mekanism/api/reactor/IFusionReactor.java b/src/api/java/mekanism/api/reactor/IFusionReactor.java
new file mode 100644
index 0000000..9d9d96d
--- /dev/null
+++ b/src/api/java/mekanism/api/reactor/IFusionReactor.java
@@ -0,0 +1,66 @@
+package mekanism.api.reactor;
+
+import mekanism.api.IHeatTransfer;
+import mekanism.api.gas.GasTank;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.FluidTank;
+
+public interface IFusionReactor extends IHeatTransfer
+{
+ public void addTemperatureFromEnergyInput(double energyAdded);
+
+ public void simulate();
+
+ public FluidTank getWaterTank();
+
+ public FluidTank getSteamTank();
+
+ public GasTank getDeuteriumTank();
+
+ public GasTank getTritiumTank();
+
+ public GasTank getFuelTank();
+
+ public double getBufferedEnergy();
+
+ public void setBufferedEnergy(double energy);
+
+ public double getPlasmaTemp();
+
+ public void setPlasmaTemp(double temp);
+
+ public double getCaseTemp();
+
+ public void setCaseTemp(double temp);
+
+ public double getBufferSize();
+
+ public void formMultiblock();
+
+ public boolean isFormed();
+
+ public void setInjectionRate(int rate);
+
+ public int getInjectionRate();
+
+ public boolean isBurning();
+
+ public void setBurning(boolean burn);
+
+ public int getMinInjectionRate(boolean active);
+
+ public double getMaxPlasmaTemperature(boolean active);
+
+ public double getMaxCasingTemperature(boolean active);
+
+ public double getIgnitionTemperature(boolean active);
+
+ public double getPassiveGeneration(boolean active, boolean current);
+
+ public int getSteamPerTick(boolean current);
+
+ public void updateTemperatures();
+
+ public ItemStack[] getInventory();
+}
diff --git a/src/api/java/mekanism/api/reactor/INeutronCapture.java b/src/api/java/mekanism/api/reactor/INeutronCapture.java
new file mode 100644
index 0000000..6171a7f
--- /dev/null
+++ b/src/api/java/mekanism/api/reactor/INeutronCapture.java
@@ -0,0 +1,6 @@
+package mekanism.api.reactor;
+
+public interface INeutronCapture extends IReactorBlock
+{
+ public int absorbNeutrons(int neutrons);
+}
diff --git a/src/api/java/mekanism/api/reactor/IReactorBlock.java b/src/api/java/mekanism/api/reactor/IReactorBlock.java
new file mode 100644
index 0000000..b3b8518
--- /dev/null
+++ b/src/api/java/mekanism/api/reactor/IReactorBlock.java
@@ -0,0 +1,12 @@
+package mekanism.api.reactor;
+
+
+public interface IReactorBlock
+{
+ public boolean isFrame();
+
+ public void setReactor(IFusionReactor reactor);
+
+ public IFusionReactor getReactor();
+
+}
diff --git a/src/api/java/mekanism/api/reactor/package-info.java b/src/api/java/mekanism/api/reactor/package-info.java
new file mode 100644
index 0000000..6a00fc6
--- /dev/null
+++ b/src/api/java/mekanism/api/reactor/package-info.java
@@ -0,0 +1,3 @@
+@API(apiVersion = "8.0.0", owner = "Mekanism", provides = "MekanismAPI|reactor")
+package mekanism.api.reactor;
+import cpw.mods.fml.common.API; \ No newline at end of file
diff --git a/src/api/java/mekanism/api/recipe/RecipeHelper.java b/src/api/java/mekanism/api/recipe/RecipeHelper.java
new file mode 100644
index 0000000..1040aa2
--- /dev/null
+++ b/src/api/java/mekanism/api/recipe/RecipeHelper.java
@@ -0,0 +1,319 @@
+package mekanism.api.recipe;
+
+import java.lang.reflect.Method;
+
+import mekanism.api.gas.GasStack;
+import mekanism.api.infuse.InfuseType;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.FluidStack;
+
+/**
+ * SET TO BE REMOVED NEXT MINOR MEKANISM VERSION, PLEASE USE IMC INSTEAD.
+ * Use this handy class to add recipes to Mekanism machinery.
+ * @author AidanBrady
+ *
+ */
+@Deprecated
+public final class RecipeHelper
+{
+ /**
+ * Add an Enrichment Chamber recipe.
+ * @param input - input ItemStack
+ * @param output - output ItemStack
+ */
+ public static void addEnrichmentChamberRecipe(ItemStack input, ItemStack output)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addEnrichmentChamberRecipe", ItemStack.class, ItemStack.class);
+ m.invoke(null, input, output);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add an Osmium Compressor recipe.
+ * @param input - input ItemStack
+ * @param output - output ItemStack
+ */
+ public static void addOsmiumCompressorRecipe(ItemStack input, ItemStack output)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addOsmiumCompressorRecipe", ItemStack.class, ItemStack.class);
+ m.invoke(null, input, output);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Combiner recipe.
+ * @param input - input ItemStack
+ * @param output - output ItemStack
+ */
+ public static void addCombinerRecipe(ItemStack input, ItemStack output)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addCombinerRecipe", ItemStack.class, ItemStack.class);
+ m.invoke(null, input, output);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Crusher recipe.
+ * @param input - input ItemStack
+ * @param output - output ItemStack
+ */
+ public static void addCrusherRecipe(ItemStack input, ItemStack output)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addCrusherRecipe", ItemStack.class, ItemStack.class);
+ m.invoke(null, input, output);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Purification Chamber recipe.
+ * @param input - input ItemStack
+ * @param output - output ItemStack
+ */
+ public static void addPurificationChamberRecipe(ItemStack input, ItemStack output)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addPurificationChamberRecipe", ItemStack.class, ItemStack.class);
+ m.invoke(null, input, output);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Chemical Oxidizer recipe.
+ * @param input - input ItemStack
+ * @param output - output GasStack
+ */
+ public static void addChemicalOxidizerRecipe(ItemStack input, GasStack output)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addChemicalOxidizerRecipe", ItemStack.class, GasStack.class);
+ m.invoke(null, input, output);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Chemical Infuser recipe.
+ * @param leftInput - left input GasStack
+ * @param rightInput - right input GasStack
+ * @param output - output GasStack
+ */
+ public static void addChemicalInfuserRecipe(GasStack leftInput, GasStack rightInput, GasStack output)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addChemicalInfuserRecipe", GasStack.class, GasStack.class, GasStack.class);
+ m.invoke(null, leftInput, rightInput, output);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Precision Sawmill recipe.
+ * @param input - input ItemStack
+ * @param primaryOutput - guaranteed output
+ * @param secondaryOutput - possible extra output
+ * @param chance - probability of obtaining extra output
+ */
+ public static void addPrecisionSawmillRecipe(ItemStack input, ItemStack primaryOutput, ItemStack secondaryOutput, double chance)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addPrecisionSawmillRecipe", ItemStack.class, ItemStack.class, ItemStack.class, Double.TYPE);
+ m.invoke(null, input, primaryOutput, secondaryOutput, chance);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Precision Sawmill recipe with no chance output
+ * @param input - input ItemStack
+ * @param primaryOutput - guaranteed output
+ */
+ public static void addPrecisionSawmillRecipe(ItemStack input, ItemStack primaryOutput)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addPrecisionSawmillRecipe", ItemStack.class, ItemStack.class);
+ m.invoke(null, input, primaryOutput);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Chemical Injection Chamber recipe.
+ * @param input - input AdvancedInput
+ * @param output - output ItemStack
+ */
+ public static void addChemicalInjectionChamberRecipe(ItemStack input, String gasName, ItemStack output)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addChemicalInjectionChamberRecipe", ItemStack.class, String.class, ItemStack.class);
+ m.invoke(null, input, gasName, output);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add an Electrolytic Separator recipe.
+ * @param input - input FluidStack
+ * @param energy - required energy
+ * @param leftOutput - left output GasStack
+ * @param rightOutput - right output GasStack
+ */
+ public static void addElectrolyticSeparatorRecipe(FluidStack input, double energy, GasStack leftOutput, GasStack rightOutput)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addElectrolyticSeparatorRecipe", FluidStack.class, Double.TYPE, GasStack.class, GasStack.class);
+ m.invoke(null, input, energy, leftOutput, rightOutput);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Chemical Dissolution Chamber recipe.
+ * @param input - input ItemStack
+ * @param output - output GasStack
+ */
+ public static void addChemicalDissolutionChamberRecipe(ItemStack input, GasStack output)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addChemicalDissolutionChamberRecipe", ItemStack.class, GasStack.class);
+ m.invoke(null, input, output);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Chemical Washer recipe.
+ * @param input - input GasStack
+ * @param output - output GasStack
+ */
+ public static void addChemicalWasherRecipe(GasStack input, GasStack output)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addChemicalWasherRecipe", GasStack.class, GasStack.class);
+ m.invoke(null, input, output);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Chemical Crystallizer recipe.
+ * @param input - input GasStack
+ * @param output - output ItemStack
+ */
+ public static void addChemicalCrystallizerRecipe(GasStack input, ItemStack output)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addChemicalCrystallizerRecipe", GasStack.class, ItemStack.class);
+ m.invoke(null, input, output);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Metallurgic Infuser recipe.
+ * @param infuse - which Infuse to use
+ * @param amount - how much Infuse to use
+ * @param input - input ItemStack
+ * @param output - output ItemStack
+ */
+ public static void addMetallurgicInfuserRecipe(InfuseType infuse, int amount, ItemStack input, ItemStack output)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addMetallurgicInfuserRecipe", InfuseType.class, Integer.TYPE, ItemStack.class, ItemStack.class);
+ m.invoke(null, infuse, amount, input, output);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Pressurized Reaction Chamber recipe.
+ * @param inputSolid - input ItemStack
+ * @param inputFluid - input FluidStack
+ * @param inputGas - input GasStack
+ * @param outputSolid - output ItemStack
+ * @param outputGas - output GasStack
+ * @param extraEnergy - extra energy needed by the recipe
+ * @param ticks - amount of ticks it takes for this recipe to complete
+ */
+ public static void addPRCRecipe(ItemStack inputSolid, FluidStack inputFluid, GasStack inputGas, ItemStack outputSolid, GasStack outputGas, double extraEnergy, int ticks)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addPRCRecipe", ItemStack.class, FluidStack.class, GasStack.class, ItemStack.class, GasStack.class, Double.TYPE, Integer.TYPE);
+ m.invoke(null, inputSolid, inputFluid, inputGas, outputSolid, outputGas, extraEnergy, ticks);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Solar Evaporation Plant recipe.
+ * @param input - input GasStack
+ * @param output - output GasStack
+ */
+ public static void addSolarEvaporationRecipe(FluidStack input, FluidStack output)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addSolarEvaporationRecipe", FluidStack.class, FluidStack.class);
+ m.invoke(null, input, output);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Add a Solar Neutron Activator recipe.
+ * @param input - input GasStack
+ * @param output - output GasStack
+ */
+ public static void addSolarNeutronRecipe(GasStack input, GasStack output)
+ {
+ try {
+ Class recipeClass = Class.forName("mekanism.common.recipe.RecipeHandler");
+ Method m = recipeClass.getMethod("addSolarEvaporationRecipe", GasStack.class, GasStack.class);
+ m.invoke(null, input, output);
+ } catch(Exception e) {
+ System.err.println("Error while adding recipe: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/api/java/mekanism/api/recipe/package-info.java b/src/api/java/mekanism/api/recipe/package-info.java
new file mode 100644
index 0000000..f166da4
--- /dev/null
+++ b/src/api/java/mekanism/api/recipe/package-info.java
@@ -0,0 +1,3 @@
+@API(apiVersion = "8.0.0", owner = "Mekanism", provides = "MekanismAPI|recipe")
+package mekanism.api.recipe;
+import cpw.mods.fml.common.API; \ No newline at end of file
diff --git a/src/api/java/mekanism/api/transmitters/DynamicNetwork.java b/src/api/java/mekanism/api/transmitters/DynamicNetwork.java
new file mode 100644
index 0000000..ee4e8a2
--- /dev/null
+++ b/src/api/java/mekanism/api/transmitters/DynamicNetwork.java
@@ -0,0 +1,456 @@
+package mekanism.api.transmitters;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.common.eventhandler.Event;
+import mekanism.api.Coord4D;
+import mekanism.api.IClientTicker;
+import mekanism.api.Range4D;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.World;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+public abstract class DynamicNetwork<A, N extends DynamicNetwork<A, N>> implements IClientTicker, INetworkDataHandler
+{
+ public LinkedHashSet<IGridTransmitter<A, N>> transmitters = Sets.newLinkedHashSet();
+ public LinkedHashSet<IGridTransmitter<A, N>> transmittersToAdd = Sets.newLinkedHashSet();
+ public LinkedHashSet<IGridTransmitter<A, N>> transmittersAdded = Sets.newLinkedHashSet();
+
+ public HashMap<Coord4D, A> possibleAcceptors = new HashMap<Coord4D, A>();
+ public HashMap<Coord4D, EnumSet<ForgeDirection>> acceptorDirections = new HashMap<Coord4D, EnumSet<ForgeDirection>>();
+ public HashMap<IGridTransmitter<A, N>, EnumSet<ForgeDirection>> changedAcceptors = Maps.newHashMap();
+
+ private Set<DelayQueue> updateQueue = new LinkedHashSet<DelayQueue>();
+
+ protected Range4D packetRange = null;
+
+ protected int capacity = 0;
+ protected double meanCapacity = 0;
+
+ protected boolean needsUpdate = false;
+ protected int updateDelay = 0;
+
+ protected boolean firstUpdate = true;
+ protected World worldObj = null;
+
+ public void addNewTransmitters(Collection<IGridTransmitter<A, N>> newTransmitters)
+ {
+ transmittersToAdd.addAll(newTransmitters);
+ }
+
+ public void commit()
+ {
+ if(!transmittersToAdd.isEmpty())
+ {
+ for(IGridTransmitter<A, N> transmitter : transmittersToAdd)
+ {
+ if(transmitter.isValid())
+ {
+ if(worldObj == null)
+ {
+ worldObj = transmitter.world();
+ }
+
+ for(ForgeDirection side : ForgeDirection.VALID_DIRECTIONS)
+ {
+ updateTransmitterOnSide(transmitter, side);
+ }
+
+ transmitter.setTransmitterNetwork((N)this);
+ absorbBuffer(transmitter);
+ transmitters.add(transmitter);
+ }
+ }
+
+ updateCapacity();
+ clampBuffer();
+ queueClientUpdate(Lists.newArrayList(transmittersToAdd));
+ transmittersToAdd.clear();
+ }
+
+ if(!changedAcceptors.isEmpty())
+ {
+ for(Entry<IGridTransmitter<A, N>, EnumSet<ForgeDirection>> entry : changedAcceptors.entrySet())
+ {
+ IGridTransmitter<A, N> transmitter = entry.getKey();
+
+ if(transmitter.isValid())
+ {
+ EnumSet<ForgeDirection> directionsChanged = entry.getValue();
+
+ for(ForgeDirection side : directionsChanged)
+ {
+ updateTransmitterOnSide(transmitter, side);
+ }
+ }
+ }
+
+ changedAcceptors.clear();
+ }
+ }
+
+ public void updateTransmitterOnSide(IGridTransmitter<A, N> transmitter, ForgeDirection side)
+ {
+ A acceptor = transmitter.getAcceptor(side);
+ Coord4D acceptorCoord = transmitter.coord().getFromSide(side);
+ EnumSet<ForgeDirection> directions = acceptorDirections.get(acceptorCoord);
+
+ if(acceptor != null)
+ {
+ possibleAcceptors.put(acceptorCoord, acceptor);
+
+ if(directions != null)
+ {
+ directions.add(side.getOpposite());
+ }
+ else {
+ acceptorDirections.put(acceptorCoord, EnumSet.of(side.getOpposite()));
+ }
+ }
+ else {
+ if(directions != null)
+ {
+ directions.remove(side.getOpposite());
+
+ if(directions.isEmpty())
+ {
+ possibleAcceptors.remove(acceptorCoord);
+ acceptorDirections.remove(acceptorCoord);
+ }
+ }
+ else {
+ possibleAcceptors.remove(acceptorCoord);
+ acceptorDirections.remove(acceptorCoord);
+ }
+ }
+
+ }
+
+ public abstract void absorbBuffer(IGridTransmitter<A, N> transmitter);
+
+ public abstract void clampBuffer();
+
+ public void invalidate()
+ {
+ //Remove invalid transmitters first for share calculations
+ for(Iterator<IGridTransmitter<A, N>> iter = transmitters.iterator(); iter.hasNext();)
+ {
+ IGridTransmitter<A, N> transmitter = iter.next();
+
+ if(!transmitter.isValid())
+ {
+ iter.remove();
+ continue;
+ }
+ }
+
+ //Clamp the new buffer
+ clampBuffer();
+
+ //Update all shares
+ for(IGridTransmitter<A, N> transmitter : transmitters)
+ {
+ transmitter.updateShare();
+ }
+
+ //Now invalidate the transmitters
+ for(IGridTransmitter<A, N> transmitter : transmitters)
+ {
+ invalidateTransmitter(transmitter);
+ }
+
+ transmitters.clear();
+ deregister();
+ }
+
+ public void invalidateTransmitter(IGridTransmitter<A, N> transmitter)
+ {
+ if(!worldObj.isRemote && transmitter.isValid())
+ {
+ transmitter.takeShare();
+ transmitter.setTransmitterNetwork(null);
+ TransmitterNetworkRegistry.registerOrphanTransmitter(transmitter);
+ }
+ }
+
+ public void acceptorChanged(IGridTransmitter<A, N> transmitter, ForgeDirection side)
+ {
+ EnumSet<ForgeDirection> directions = changedAcceptors.get(transmitter);
+
+ if(directions != null)
+ {
+ directions.add(side);
+ }
+ else {
+ changedAcceptors.put(transmitter, EnumSet.of(side));
+ }
+
+ TransmitterNetworkRegistry.registerChangedNetwork(this);
+ }
+
+ public void adoptTransmittersAndAcceptorsFrom(N net)
+ {
+ for(IGridTransmitter<A, N> transmitter : net.transmitters)
+ {
+ transmitter.setTransmitterNetwork((N)this);
+ transmitters.add(transmitter);
+ transmittersAdded.add(transmitter);
+ }
+
+ possibleAcceptors.putAll(net.possibleAcceptors);
+
+ for(Entry<Coord4D, EnumSet<ForgeDirection>> entry : net.acceptorDirections.entrySet())
+ {
+ Coord4D coord = entry.getKey();
+
+ if(acceptorDirections.containsKey(coord))
+ {
+ acceptorDirections.get(coord).addAll(entry.getValue());
+ }
+ else {
+ acceptorDirections.put(coord, entry.getValue());
+ }
+ }
+
+ }
+
+ public Range4D getPacketRange()
+ {
+ if(packetRange == null)
+ {
+ return genPacketRange();
+ }
+
+ return packetRange;
+ }
+
+ protected Range4D genPacketRange()
+ {
+ if(getSize() == 0)
+ {
+ deregister();
+ return null;
+ }
+
+ IGridTransmitter<A, N> initTransmitter = transmitters.iterator().next();
+ Coord4D initCoord = initTransmitter.coord();
+
+ int minX = initCoord.xCoord;
+ int minY = initCoord.yCoord;
+ int minZ = initCoord.zCoord;
+ int maxX = initCoord.xCoord;
+ int maxY = initCoord.yCoord;
+ int maxZ = initCoord.zCoord;
+
+ for(IGridTransmitter transmitter : transmitters)
+ {
+ Coord4D coord = transmitter.coord();
+
+ if(coord.xCoord < minX) minX = coord.xCoord;
+ if(coord.yCoord < minY) minY = coord.yCoord;
+ if(coord.zCoord < minZ) minZ = coord.zCoord;
+ if(coord.xCoord > maxX) maxX = coord.xCoord;
+ if(coord.yCoord > maxY) maxY = coord.yCoord;
+ if(coord.zCoord > maxZ) maxZ = coord.zCoord;
+ }
+
+ return new Range4D(minX, minY, minZ, maxX, maxY, maxZ, initTransmitter.world().provider.dimensionId);
+ }
+
+ public void register()
+ {
+ if(FMLCommonHandler.instance().getEffectiveSide().isServer())
+ {
+ TransmitterNetworkRegistry.getInstance().registerNetwork(this);
+ }
+ else {
+ MinecraftForge.EVENT_BUS.post(new ClientTickUpdate(this, (byte)1));
+ }
+ }
+
+ public void deregister()
+ {
+ transmitters.clear();
+
+ if(FMLCommonHandler.instance().getEffectiveSide().isServer())
+ {
+ TransmitterNetworkRegistry.getInstance().removeNetwork(this);
+ }
+ else {
+ MinecraftForge.EVENT_BUS.post(new ClientTickUpdate(this, (byte)0));
+ }
+ }
+
+ public int getSize()
+ {
+ return transmitters.size();
+ }
+
+ public int getAcceptorSize()
+ {
+ return possibleAcceptors.size();
+ }
+
+ public synchronized void updateCapacity()
+ {
+ updateMeanCapacity();
+ capacity = (int)meanCapacity * transmitters.size();
+ }
+
+ /**
+ * Override this if things can have variable capacity along the network.
+ * @return An 'average' value of capacity. Calculate it how you will.
+ */
+ protected synchronized void updateMeanCapacity()
+ {
+ if(transmitters.size() > 0)
+ {
+ meanCapacity = transmitters.iterator().next().getCapacity();
+ }
+ else {
+ meanCapacity = 0;
+ }
+ }
+
+ public int getCapacity()
+ {
+ return capacity;
+ }
+
+ public World getWorld()
+ {
+ return worldObj;
+ }
+
+ public abstract Set<A> getAcceptors(Object data);
+
+ public void tick()
+ {
+ onUpdate();
+ }
+
+ public void onUpdate()
+ {
+ if(FMLCommonHandler.instance().getEffectiveSide().isServer())
+ {
+ Iterator<DelayQueue> i = updateQueue.iterator();
+
+ try {
+ while(i.hasNext())
+ {
+ DelayQueue q = i.next();
+
+ if(q.delay > 0)
+ {
+ q.delay--;
+ }
+ else {
+ transmittersAdded.addAll(transmitters);
+ updateDelay = 1;
+ i.remove();
+ }
+ }
+ } catch(Exception e) {}
+
+ if(updateDelay > 0)
+ {
+ updateDelay--;
+
+ if(updateDelay == 0)
+ {
+ MinecraftForge.EVENT_BUS.post(new TransmittersAddedEvent(this, firstUpdate, (Collection)transmittersAdded));
+ firstUpdate = false;
+ transmittersAdded.clear();
+ needsUpdate = true;
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean needsTicks()
+ {
+ return getSize() > 0;
+ }
+
+ @Override
+ public void clientTick() {}
+
+ public void queueClientUpdate(Collection<IGridTransmitter<A, N>> newTransmitters)
+ {
+ transmittersAdded.addAll(newTransmitters);
+ updateDelay = 3;
+ }
+
+ public static class TransmittersAddedEvent extends Event
+ {
+ public DynamicNetwork<?, ?> network;
+ public boolean newNetwork;
+ public Collection<IGridTransmitter> newTransmitters;
+
+ public TransmittersAddedEvent(DynamicNetwork net, boolean newNet, Collection<IGridTransmitter> added)
+ {
+ network = net;
+ newNetwork = newNet;
+ newTransmitters = added;
+ }
+ }
+
+ public static class ClientTickUpdate extends Event
+ {
+ public DynamicNetwork network;
+ public byte operation; /*0 remove, 1 add*/
+
+ public ClientTickUpdate(DynamicNetwork net, byte b)
+ {
+ network = net;
+ operation = b;
+ }
+ }
+
+ public static class NetworkClientRequest extends Event
+ {
+ public TileEntity tileEntity;
+
+ public NetworkClientRequest(TileEntity tile)
+ {
+ tileEntity = tile;
+ }
+ }
+
+ public void addUpdate(EntityPlayer player)
+ {
+ updateQueue.add(new DelayQueue(player));
+ }
+
+ public static class DelayQueue
+ {
+ public EntityPlayer player;
+ public int delay;
+
+ public DelayQueue(EntityPlayer p)
+ {
+ player = p;
+ delay = 5;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return player.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ return o instanceof DelayQueue && ((DelayQueue)o).player.equals(this.player);
+ }
+ }
+}
diff --git a/src/api/java/mekanism/api/transmitters/IBlockableConnection.java b/src/api/java/mekanism/api/transmitters/IBlockableConnection.java
new file mode 100644
index 0000000..f6811fb
--- /dev/null
+++ b/src/api/java/mekanism/api/transmitters/IBlockableConnection.java
@@ -0,0 +1,10 @@
+package mekanism.api.transmitters;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+public interface IBlockableConnection
+{
+ public boolean canConnectMutual(ForgeDirection side);
+
+ public boolean canConnect(ForgeDirection side);
+}
diff --git a/src/api/java/mekanism/api/transmitters/IGridTransmitter.java b/src/api/java/mekanism/api/transmitters/IGridTransmitter.java
new file mode 100644
index 0000000..9d82d48
--- /dev/null
+++ b/src/api/java/mekanism/api/transmitters/IGridTransmitter.java
@@ -0,0 +1,64 @@
+package mekanism.api.transmitters;
+
+import mekanism.api.Coord4D;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import java.util.Collection;
+
+public interface IGridTransmitter<A, N extends DynamicNetwork<A, N>> extends ITransmitter
+{
+ public boolean hasTransmitterNetwork();
+
+ /**
+ * Gets the network currently in use by this transmitter segment.
+ * @return network this transmitter is using
+ */
+ public N getTransmitterNetwork();
+
+ /**
+ * Sets this transmitter segment's network to a new value.
+ * @param network - network to set to
+ */
+ public void setTransmitterNetwork(N network);
+
+ public int getTransmitterNetworkSize();
+
+ public int getTransmitterNetworkAcceptorSize();
+
+ public String getTransmitterNetworkNeeded();
+
+ public String getTransmitterNetworkFlow();
+
+ public String getTransmitterNetworkBuffer();
+
+ public double getTransmitterNetworkCapacity();
+
+ public int getCapacity();
+
+ public World world();
+
+ public Coord4D coord();
+
+ public Coord4D getAdjacentConnectableTransmitterCoord(ForgeDirection side);
+
+ public A getAcceptor(ForgeDirection side);
+
+ public boolean isValid();
+
+ public boolean isOrphan();
+
+ public void setOrphan(boolean orphaned);
+
+ public N createEmptyNetwork();
+
+ public N mergeNetworks(Collection<N> toMerge);
+
+ public N getExternalNetwork(Coord4D from);
+
+ public void takeShare();
+
+ public void updateShare();
+
+ public Object getBuffer();
+}
diff --git a/src/api/java/mekanism/api/transmitters/INetworkDataHandler.java b/src/api/java/mekanism/api/transmitters/INetworkDataHandler.java
new file mode 100644
index 0000000..38cd637
--- /dev/null
+++ b/src/api/java/mekanism/api/transmitters/INetworkDataHandler.java
@@ -0,0 +1,10 @@
+package mekanism.api.transmitters;
+
+public interface INetworkDataHandler
+{
+ public String getNeededInfo();
+
+ public String getStoredInfo();
+
+ public String getFlowInfo();
+}
diff --git a/src/api/java/mekanism/api/transmitters/ITransmitter.java b/src/api/java/mekanism/api/transmitters/ITransmitter.java
new file mode 100644
index 0000000..cddc76a
--- /dev/null
+++ b/src/api/java/mekanism/api/transmitters/ITransmitter.java
@@ -0,0 +1,11 @@
+package mekanism.api.transmitters;
+
+public interface ITransmitter
+{
+ /**
+ * Get the transmitter's transmission type
+ *
+ * @return TransmissionType this transmitter uses
+ */
+ public TransmissionType getTransmissionType();
+}
diff --git a/src/api/java/mekanism/api/transmitters/ITransmitterTile.java b/src/api/java/mekanism/api/transmitters/ITransmitterTile.java
new file mode 100644
index 0000000..3940b71
--- /dev/null
+++ b/src/api/java/mekanism/api/transmitters/ITransmitterTile.java
@@ -0,0 +1,6 @@
+package mekanism.api.transmitters;
+
+public interface ITransmitterTile<A, N extends DynamicNetwork<A, N>>
+{
+ public IGridTransmitter<A, N> getTransmitter();
+}
diff --git a/src/api/java/mekanism/api/transmitters/TransmissionType.java b/src/api/java/mekanism/api/transmitters/TransmissionType.java
new file mode 100644
index 0000000..7b54e9c
--- /dev/null
+++ b/src/api/java/mekanism/api/transmitters/TransmissionType.java
@@ -0,0 +1,80 @@
+package mekanism.api.transmitters;
+
+import mekanism.api.gas.IGasTransmitter;
+
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.StatCollector;
+
+public enum TransmissionType
+{
+ ENERGY("EnergyNetwork", "Energy"),
+ FLUID("FluidNetwork", "Fluids"),
+ GAS("GasNetwork", "Gases"),
+ ITEM("InventoryNetwork", "Items"),
+ HEAT("HeatNetwork", "Heat");
+
+ private String name;
+ private String transmission;
+
+ private TransmissionType(String n, String t)
+ {
+ name = n;
+ transmission = t;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String getTransmission()
+ {
+ return transmission;
+ }
+
+ public String localize()
+ {
+ return StatCollector.translateToLocal("transmission." + getTransmission());
+ }
+
+ public static boolean checkTransmissionType(ITransmitter sideTile, TransmissionType type)
+ {
+ return type.checkTransmissionType(sideTile);
+ }
+
+ public static boolean checkTransmissionType(TileEntity tile1, TransmissionType type)
+ {
+ return checkTransmissionType(tile1, type, null);
+ }
+
+ public static boolean checkTransmissionType(TileEntity tile1, TransmissionType type, TileEntity tile2)
+ {
+ return type.checkTransmissionType(tile1, tile2);
+ }
+
+ public boolean checkTransmissionType(ITransmitter transmitter)
+ {
+ return transmitter.getTransmissionType() == this;
+ }
+
+ public boolean checkTransmissionType(TileEntity sideTile, TileEntity currentTile)
+ {
+ if(sideTile instanceof ITransmitter)
+ {
+ if(((ITransmitter)sideTile).getTransmissionType() == this)
+ {
+ return true;
+ }
+ }
+
+ if(this == GAS && currentTile instanceof IGasTransmitter)
+ {
+ if(((IGasTransmitter)currentTile).canTransferGasToTube(sideTile))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+} \ No newline at end of file
diff --git a/src/api/java/mekanism/api/transmitters/TransmitterNetworkRegistry.java b/src/api/java/mekanism/api/transmitters/TransmitterNetworkRegistry.java
new file mode 100644
index 0000000..0f9c1c5
--- /dev/null
+++ b/src/api/java/mekanism/api/transmitters/TransmitterNetworkRegistry.java
@@ -0,0 +1,289 @@
+package mekanism.api.transmitters;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+import mekanism.api.Coord4D;
+import mekanism.api.MekanismAPI;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.common.eventhandler.SubscribeEvent;
+import cpw.mods.fml.common.gameevent.TickEvent.Phase;
+import cpw.mods.fml.common.gameevent.TickEvent.ServerTickEvent;
+import cpw.mods.fml.relauncher.Side;
+
+public class TransmitterNetworkRegistry
+{
+ private static TransmitterNetworkRegistry INSTANCE = new TransmitterNetworkRegistry();
+ private static boolean loaderRegistered = false;
+
+ private HashSet<DynamicNetwork> networks = Sets.newHashSet();
+ private HashSet<DynamicNetwork> networksToChange = Sets.newHashSet();
+
+ private HashSet<IGridTransmitter> invalidTransmitters = Sets.newHashSet();
+ private HashMap<Coord4D, IGridTransmitter> orphanTransmitters = Maps.newHashMap();
+
+ private Logger logger = LogManager.getLogger("MekanismTransmitters");
+
+ public static void initiate()
+ {
+ if(!loaderRegistered)
+ {
+ loaderRegistered = true;
+
+ FMLCommonHandler.instance().bus().register(INSTANCE);
+ }
+ }
+
+ public static void reset()
+ {
+ getInstance().networks.clear();
+ getInstance().networksToChange.clear();
+ getInstance().invalidTransmitters.clear();
+ getInstance().orphanTransmitters.clear();
+ }
+
+ public static void invalidateTransmitter(IGridTransmitter transmitter)
+ {
+ getInstance().invalidTransmitters.add(transmitter);
+ }
+
+ public static void registerOrphanTransmitter(IGridTransmitter transmitter)
+ {
+ getInstance().orphanTransmitters.put(transmitter.coord(), transmitter);
+ }
+
+ public static void registerChangedNetwork(DynamicNetwork network)
+ {
+ getInstance().networksToChange.add(network);
+ }
+
+ public static TransmitterNetworkRegistry getInstance()
+ {
+ return INSTANCE;
+ }
+
+ public void registerNetwork(DynamicNetwork network)
+ {
+ networks.add(network);
+ }
+
+ public void removeNetwork(DynamicNetwork network)
+ {
+ if(networks.contains(network))
+ {
+ networks.remove(network);
+ }
+ }
+
+ @SubscribeEvent
+ public void onTick(ServerTickEvent event)
+ {
+ if(event.phase == Phase.END && event.side == Side.SERVER)
+ {
+ tickEnd();
+ }
+ }
+
+ public void tickEnd()
+ {
+ removeInvalidTransmitters();
+
+ assignOrphans();
+
+ commitChanges();
+
+ for(DynamicNetwork net : networks)
+ {
+ net.tick();
+ }
+ }
+
+ public void removeInvalidTransmitters()
+ {
+ if(MekanismAPI.debug && !invalidTransmitters.isEmpty())
+ {
+ logger.info("Dealing with " + invalidTransmitters.size() + " invalid Transmitters");
+ }
+
+ for(IGridTransmitter invalid : invalidTransmitters)
+ {
+ if(!(invalid.isOrphan() && invalid.isValid()))
+ {
+ DynamicNetwork n = invalid.getTransmitterNetwork();
+
+ if(n != null)
+ {
+ n.invalidate();
+ }
+ }
+ }
+
+ invalidTransmitters.clear();
+ }
+
+ public void assignOrphans()
+ {
+ if(MekanismAPI.debug && !orphanTransmitters.isEmpty())
+ {
+ logger.info("Dealing with " + orphanTransmitters.size() + " orphan Transmitters");
+ }
+
+ for(IGridTransmitter orphanTransmitter : orphanTransmitters.values())
+ {
+ DynamicNetwork network = getNetworkFromOrphan(orphanTransmitter);
+
+ if(network != null)
+ {
+ networksToChange.add(network);
+ network.register();
+ }
+ }
+
+ orphanTransmitters.clear();
+ }
+
+ public <A, N extends DynamicNetwork<A, N>> DynamicNetwork<A, N> getNetworkFromOrphan(IGridTransmitter<A, N> startOrphan)
+ {
+ if(startOrphan.isValid() && startOrphan.isOrphan())
+ {
+ OrphanPathFinder<A, N> finder = new OrphanPathFinder<A, N>(startOrphan);
+ finder.start();
+ N network;
+
+ switch(finder.networksFound.size())
+ {
+ case 0:
+ if(MekanismAPI.debug)
+ {
+ logger.info("No networks found. Creating new network for " + finder.connectedTransmitters.size() + " transmitters");
+ }
+
+ network = startOrphan.createEmptyNetwork();
+
+ break;
+ case 1:
+ if(MekanismAPI.debug)
+ {
+ logger.info("Adding " + finder.connectedTransmitters.size() + " transmitters to single found network");
+ }
+
+ network = finder.networksFound.iterator().next();
+
+ break;
+ default:
+ if(MekanismAPI.debug)
+ {
+ logger.info("Merging " + finder.networksFound.size() + " networks with " + finder.connectedTransmitters.size() + " new transmitters");
+ }
+
+ network = startOrphan.mergeNetworks(finder.networksFound);
+ }
+
+ network.addNewTransmitters(finder.connectedTransmitters);
+
+ return network;
+ }
+
+ return null;
+ }
+
+ public void commitChanges()
+ {
+ for(DynamicNetwork network : networksToChange)
+ {
+ network.commit();
+ }
+
+ networksToChange.clear();
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Network Registry:\n" + networks;
+ }
+
+ public String[] toStrings()
+ {
+ String[] strings = new String[networks.size()];
+ int i = 0;
+
+ for(DynamicNetwork network : networks)
+ {
+ strings[i++] = network.toString();
+ }
+
+ return strings;
+ }
+
+ public class OrphanPathFinder<A, N extends DynamicNetwork<A, N>>
+ {
+ public IGridTransmitter<A, N> startPoint;
+
+ public HashSet<Coord4D> iterated = Sets.newHashSet();
+
+ public HashSet<IGridTransmitter<A, N>> connectedTransmitters = Sets.newHashSet();
+ public HashSet<N> networksFound = Sets.newHashSet();
+
+ public OrphanPathFinder(IGridTransmitter<A, N> start)
+ {
+ startPoint = start;
+ }
+
+ public void start()
+ {
+ iterate(startPoint.coord(), ForgeDirection.UNKNOWN);
+ }
+
+ public void iterate(Coord4D from, ForgeDirection fromDirection)
+ {
+ if(iterated.contains(from))
+ {
+ return;
+ }
+
+ iterated.add(from);
+
+ if(orphanTransmitters.containsKey(from))
+ {
+ IGridTransmitter<A, N> transmitter = orphanTransmitters.get(from);
+
+ if(transmitter.isValid() && transmitter.isOrphan())
+ {
+ connectedTransmitters.add(transmitter);
+ transmitter.setOrphan(false);
+
+ for(ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS)
+ {
+ if(direction != fromDirection)
+ {
+ Coord4D directionCoord = transmitter.getAdjacentConnectableTransmitterCoord(direction);
+
+ if(!(directionCoord == null || iterated.contains(directionCoord)))
+ {
+ iterate(directionCoord, direction.getOpposite());
+ }
+ }
+ }
+ }
+ }
+ else {
+ addNetworkToIterated(from);
+ }
+ }
+
+ public void addNetworkToIterated(Coord4D from)
+ {
+ N net = startPoint.getExternalNetwork(from);
+ if(net != null) networksFound.add(net);
+ }
+ }
+}
diff --git a/src/api/java/mekanism/api/transmitters/package-info.java b/src/api/java/mekanism/api/transmitters/package-info.java
new file mode 100644
index 0000000..06819e7
--- /dev/null
+++ b/src/api/java/mekanism/api/transmitters/package-info.java
@@ -0,0 +1,3 @@
+@API(apiVersion = "8.0.0", owner = "Mekanism", provides = "MekanismAPI|transmitter")
+package mekanism.api.transmitters;
+import cpw.mods.fml.common.API; \ No newline at end of file
diff --git a/src/api/java/mekanism/api/util/BlockInfo.java b/src/api/java/mekanism/api/util/BlockInfo.java
new file mode 100644
index 0000000..2cb40d7
--- /dev/null
+++ b/src/api/java/mekanism/api/util/BlockInfo.java
@@ -0,0 +1,38 @@
+package mekanism.api.util;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+
+public class BlockInfo
+{
+ public Block block;
+ public int meta;
+
+ public BlockInfo(Block b, int j)
+ {
+ block = b;
+ meta = j;
+ }
+
+ public static BlockInfo get(ItemStack stack)
+ {
+ return new BlockInfo(Block.getBlockFromItem(stack.getItem()), stack.getItemDamage());
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return obj instanceof BlockInfo &&
+ ((BlockInfo)obj).block == block &&
+ ((BlockInfo)obj).meta == meta;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int code = 1;
+ code = 31 * code + block.getUnlocalizedName().hashCode();
+ code = 31 * code + meta;
+ return code;
+ }
+} \ No newline at end of file
diff --git a/src/api/java/mekanism/api/util/ItemInfo.java b/src/api/java/mekanism/api/util/ItemInfo.java
new file mode 100644
index 0000000..f27065b
--- /dev/null
+++ b/src/api/java/mekanism/api/util/ItemInfo.java
@@ -0,0 +1,38 @@
+package mekanism.api.util;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+public class ItemInfo
+{
+ public Item item;
+ public int meta;
+
+ public ItemInfo(Item i, int j)
+ {
+ item = i;
+ meta = j;
+ }
+
+ public static ItemInfo get(ItemStack stack)
+ {
+ return new ItemInfo(stack.getItem(), stack.getItemDamage());
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return obj instanceof ItemInfo &&
+ ((ItemInfo)obj).item == item &&
+ ((ItemInfo)obj).meta == meta;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int code = 1;
+ code = 31 * code + System.identityHashCode(item);
+ code = 7 * code + meta;
+ return code;
+ }
+}
diff --git a/src/api/java/mekanism/api/util/ListUtils.java b/src/api/java/mekanism/api/util/ListUtils.java
new file mode 100644
index 0000000..e20807c
--- /dev/null
+++ b/src/api/java/mekanism/api/util/ListUtils.java
@@ -0,0 +1,282 @@
+package mekanism.api.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+public class ListUtils
+{
+ public static <V> List<V> inverse(List<V> list)
+ {
+ List<V> toReturn = new ArrayList<V>();
+
+ for(int i = list.size() - 1; i >= 0; i--)
+ {
+ toReturn.add(list.get(i));
+ }
+
+ return toReturn;
+ }
+
+ public static <V> List<V> cap(List<V> list, int cap)
+ {
+ List<V> toReturn = new ArrayList<V>();
+
+ if(list.size() <= cap)
+ {
+ toReturn = copy(list);
+ }
+ else {
+ int count = 0;
+
+ for(V obj : list)
+ {
+ count++;
+
+ toReturn.add(obj);
+
+ if(count == cap)
+ {
+ break;
+ }
+ }
+ }
+
+ return toReturn;
+ }
+
+ public static <V> List<V> copy(List<V> list)
+ {
+ List<V> toReturn = new ArrayList<V>();
+
+ for(V obj : list)
+ {
+ toReturn.add(obj);
+ }
+
+ return toReturn;
+ }
+
+ public static <V> List<V> merge(List<V> listOne, List<V> listTwo)
+ {
+ List<V> newList = new ArrayList<V>();
+
+ for(V obj : listOne)
+ {
+ newList.add(obj);
+ }
+
+ for(V obj : listTwo)
+ {
+ newList.add(obj);
+ }
+
+ return newList;
+ }
+
+ public static <V> List<V> capRemains(List<V> list, int cap)
+ {
+ List<V> toReturn = new ArrayList<V>();
+
+ if(list.size() <= cap)
+ {
+ return toReturn;
+ }
+ else {
+ List<V> inverse = inverse(list);
+
+ int iterNeeded = list.size() - cap;
+ int count = 0;
+
+ for(V obj : list)
+ {
+ count++;
+
+ toReturn.add(obj);
+
+ if(count == iterNeeded)
+ {
+ break;
+ }
+ }
+
+ return toReturn;
+ }
+ }
+
+ public static <V> ArrayList<List<V>> split(List<V> list, int divide)
+ {
+ int remain = list.size() % divide;
+ int size = (list.size() - remain) / divide;
+
+ ArrayList<List<V>> toReturn = new ArrayList<List<V>>();
+
+ for(int i = 0; i < divide; i++)
+ {
+ toReturn.add(i, new ArrayList<V>());
+ }
+
+ for(List<V> iterSet : toReturn)
+ {
+ List<V> removed = new ArrayList<V>();
+
+ int toAdd = size;
+
+ if(remain > 0)
+ {
+ remain--;
+ toAdd++;
+ }
+
+ for(V obj : list)
+ {
+ if(toAdd == 0)
+ {
+ break;
+ }
+
+ iterSet.add(obj);
+ removed.add(obj);
+ toAdd--;
+ }
+
+ for(V obj : removed)
+ {
+ list.remove(obj);
+ }
+ }
+
+ return toReturn;
+ }
+
+ public static <V> V getTop(List<V> list)
+ {
+ for(V obj : list)
+ {
+ return obj;
+ }
+
+ return null;
+ }
+
+ public static <V> List<V> asList(Set<V> set)
+ {
+ return (List<V>)Arrays.asList(set.toArray());
+ }
+
+ public static <V> List<V> asList(V... values)
+ {
+ return (List<V>)Arrays.asList(values);
+ }
+
+ public static double[] splitDouble(int size, double num)
+ {
+ double[] split = new double[size];
+
+ for(int i = 0; i < size; i++)
+ {
+ double remain = num%size;
+ double ret = (num-remain)/size;
+ ret += remain;
+
+ split[i] = ret;
+ num -= remain;
+ }
+
+ return split;
+ }
+
+ public static double[] percent(double[] values)
+ {
+ double[] ret = new double[values.length];
+ double total = 0;
+
+ for(double d : values) total += d;
+
+ for(int i = 0; i < values.length; i++)
+ {
+ ret[i] = values[i]/total;
+ }
+
+ return ret;
+ }
+
+ public static int[] calcPercentInt(double[] percent, int val)
+ {
+ int[] ret = new int[percent.length];
+
+ for(int i = 0; i < percent.length; i++)
+ {
+ ret[i] = (int)Math.round(val*percent[i]);
+ }
+
+ int newTotal = 0;
+ for(int i : ret) newTotal += i;
+
+ int diff = val-newTotal;
+
+ if(diff != val)
+ {
+ for(int i = 0; i < ret.length; i++)
+ {
+ int num = ret[i];
+
+ if(diff < 0 && num == 0)
+ {
+ continue;
+ }
+
+ if(diff > 0)
+ {
+ ret[i]++;
+ diff--;
+ }
+ else if(diff < 0)
+ {
+ ret[i]--;
+ diff++;
+ }
+
+ if(diff == 0)
+ {
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ public static int[] splitInt(int size, int num)
+ {
+ int[] split = new int[size];
+
+ for(int i = 0; i < size; i++)
+ {
+ int remain = num%size;
+ int ret = (num-remain)/size;
+ ret += remain;
+
+ split[i] = ret;
+ num -= remain;
+ }
+
+ return split;
+ }
+
+ public static double[] percent(int[] values)
+ {
+ double[] ret = new double[values.length];
+ double total = 0;
+
+ for(double d : values) total += d;
+
+ for(int i = 0; i < values.length; i++)
+ {
+ ret[i] = values[i]/total;
+ }
+
+ return ret;
+ }
+}
diff --git a/src/api/java/mekanism/api/util/StackUtils.java b/src/api/java/mekanism/api/util/StackUtils.java
new file mode 100644
index 0000000..7363c75
--- /dev/null
+++ b/src/api/java/mekanism/api/util/StackUtils.java
@@ -0,0 +1,254 @@
+package mekanism.api.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.oredict.OreDictionary;
+
+public final class StackUtils
+{
+ public static List<ItemStack> split(ItemStack stack)
+ {
+ if(stack == null || stack.stackSize == 0)
+ {
+ return null;
+ }
+
+ List<ItemStack> ret = new ArrayList<ItemStack>();
+
+ if(stack.stackSize == 1)
+ {
+ ret.add(stack);
+ return ret;
+ }
+
+ int remain = stack.stackSize % 2;
+ int split = (int)((float)(stack.stackSize)/2F);
+
+ ret.add(size(stack, split+remain));
+ ret.add(size(stack, split));
+
+ return ret;
+ }
+
+ public static Item getItem(ItemStack stack)
+ {
+ if(stack == null)
+ {
+ return null;
+ }
+
+ return stack.getItem();
+ }
+
+ public static boolean diffIgnoreNull(ItemStack stack1, ItemStack stack2)
+ {
+ if(stack1 == null || stack2 == null)
+ {
+ return false;
+ }
+
+ return stack1.getItem() != stack2.getItem() || stack1.getItemDamage() != stack2.getItemDamage();
+ }
+
+ public static boolean equalsWildcard(ItemStack wild, ItemStack check)
+ {
+ if(wild == null || check == null)
+ {
+ return check == wild;
+ }
+
+ return wild.getItem() == check.getItem() && (wild.getItemDamage() == OreDictionary.WILDCARD_VALUE || wild.getItemDamage() == check.getItemDamage());
+ }
+
+ public static boolean equalsWildcardWithNBT(ItemStack wild, ItemStack check)
+ {
+ return equalsWildcard(wild, check) && (wild.stackTagCompound == null ? check.stackTagCompound == null : (wild.stackTagCompound == check.stackTagCompound || wild.stackTagCompound.equals(check.stackTagCompound)));
+ }
+
+ public static List<ItemStack> even(ItemStack stack1, ItemStack stack2)
+ {
+ ArrayList<ItemStack> ret = new ArrayList<ItemStack>();
+
+ if(getSize(stack1) == getSize(stack2) || Math.abs(getSize(stack1)-getSize(stack2)) == 1)
+ {
+ ret.add(stack1);
+ ret.add(stack2);
+
+ return ret;
+ }
+
+ if(getSize(stack1) > getSize(stack2))
+ {
+ int diff = getSize(stack1)-getSize(stack2);
+
+ List<ItemStack> split = split(size(stack1, diff));
+
+ ret.add(subtract(stack1, split.get(0)));
+ ret.add(add(stack2, split.get(0)));
+ }
+ else if(getSize(stack2) > getSize(stack1))
+ {
+ int diff = getSize(stack2)-getSize(stack1);
+
+ List<ItemStack> split = split(size(stack2, diff));
+
+ ret.add(subtract(stack2, split.get(0)));
+ ret.add(add(stack1, split.get(0)));
+ }
+
+ return ret;
+ }
+
+ public static ItemStack add(ItemStack stack1, ItemStack stack2)
+ {
+ if(stack1 == null)
+ {
+ return stack2;
+ }
+ else if(stack2 == null)
+ {
+ return stack1;
+ }
+
+ return size(stack1, getSize(stack1)+getSize(stack2));
+ }
+
+ public static ItemStack subtract(ItemStack stack1, ItemStack stack2)
+ {
+ if(stack1 == null)
+ {
+ return null;
+ }
+ else if(stack2 == null)
+ {
+ return stack1;
+ }
+
+ return size(stack1, getSize(stack1)-getSize(stack2));
+ }
+
+ public static ItemStack size(ItemStack stack, int size)
+ {
+ if(size <= 0 || stack == null)
+ {
+ return null;
+ }
+
+ ItemStack ret = stack.copy();
+ ret.stackSize = size;
+ return ret;
+ }
+
+ public static ItemStack copy(ItemStack stack)
+ {
+ if(stack == null)
+ {
+ return null;
+ }
+
+ return stack.copy();
+ }
+
+ public static int getSize(ItemStack stack)
+ {
+ return stack != null ? stack.stackSize : 0;
+ }
+
+ public static List<ItemStack> getMergeRejects(ItemStack[] orig, ItemStack[] toAdd)
+ {
+ List<ItemStack> ret = new ArrayList<ItemStack>();
+
+ for(int i = 0; i < toAdd.length; i++)
+ {
+ if(toAdd[i] != null)
+ {
+ ItemStack reject = getMergeReject(orig[i], toAdd[i]);
+
+ if(reject != null)
+ {
+ ret.add(reject);
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ public static void merge(ItemStack[] orig, ItemStack[] toAdd)
+ {
+ for(int i = 0; i < toAdd.length; i++)
+ {
+ if(toAdd[i] != null)
+ {
+ orig[i] = merge(orig[i], toAdd[i]);
+ }
+ }
+ }
+
+ public static ItemStack merge(ItemStack orig, ItemStack toAdd)
+ {
+ if(orig == null)
+ {
+ return toAdd;
+ }
+
+ if(toAdd == null)
+ {
+ return orig;
+ }
+
+ if(!orig.isItemEqual(toAdd) || !ItemStack.areItemStackTagsEqual(orig, toAdd))
+ {
+ return orig;
+ }
+
+ return StackUtils.size(orig, Math.min(orig.getMaxStackSize(), orig.stackSize+toAdd.stackSize));
+ }
+
+ public static ItemStack getMergeReject(ItemStack orig, ItemStack toAdd)
+ {
+ if(orig == null)
+ {
+ return null;
+ }
+
+ if(toAdd == null)
+ {
+ return orig;
+ }
+
+ if(!orig.isItemEqual(toAdd) || !ItemStack.areItemStackTagsEqual(orig, toAdd))
+ {
+ return orig;
+ }
+
+ int newSize = orig.stackSize+toAdd.stackSize;
+
+ if(newSize > orig.getMaxStackSize())
+ {
+ return StackUtils.size(orig, newSize-orig.getMaxStackSize());
+ }
+ else {
+ return StackUtils.size(orig, newSize);
+ }
+ }
+
+ public static boolean contains(ItemStack container, ItemStack contained)
+ {
+ return equalsWildcardWithNBT(contained, container) && container.stackSize >= contained.stackSize;
+ }
+
+ public static int hashItemStack(ItemStack stack)
+ {
+ if(stack == null || stack.getItem() == null)
+ {
+ return -1;
+ }
+
+ String name = stack.getItemDamage() == OreDictionary.WILDCARD_VALUE ? stack.getItem().getUnlocalizedName() : stack.getItem().getUnlocalizedName(stack);
+ return name.hashCode() << 8 | stack.getItemDamage();
+ }
+}
diff --git a/src/api/java/mekanism/api/util/UnitDisplayUtils.java b/src/api/java/mekanism/api/util/UnitDisplayUtils.java
new file mode 100644
index 0000000..9dd3e28
--- /dev/null
+++ b/src/api/java/mekanism/api/util/UnitDisplayUtils.java
@@ -0,0 +1,295 @@
+package mekanism.api.util;
+
+/**
+ * Code taken from UE and modified to fit Mekanism.
+ */
+public class UnitDisplayUtils
+{
+ public static enum ElectricUnit
+ {
+ JOULES("Joule", "J"),
+ REDSTONE_FLUX("Redstone Flux", "RF"),
+ MINECRAFT_JOULES("Minecraft Joule", "MJ"),
+ ELECTRICAL_UNITS("Electrical Unit", "EU");
+
+ public String name;
+ public String symbol;
+
+ private ElectricUnit(String s, String s1)
+ {
+ name = s;
+ symbol = s1;
+ }
+
+ public String getPlural()
+ {
+ return this == REDSTONE_FLUX ? name : name + "s";
+ }
+ }
+
+ public static enum TemperatureUnit
+ {
+ KELVIN("Kelvin", "K", 0, 1),
+ CELSIUS("Celsius", "°C", 273.15, 1),
+ RANKINE("Rankine", "R", 0, 9D/5D),
+ FAHRENHEIT("Fahrenheit", "°F", 459.67, 9D/5D),
+ AMBIENT("Ambient", "+STP", 300, 1);
+
+ public String name;
+ public String symbol;
+ double zeroOffset;
+ double intervalSize;
+
+ private TemperatureUnit(String s, String s1, double offset, double size)
+ {
+ name = s;
+ symbol = s1;
+ zeroOffset = offset;
+ intervalSize = size;
+ }
+
+ public double convertFromK(double T)
+ {
+ return (T * intervalSize) - zeroOffset;
+ }
+
+ public double convertToK(double T)
+ {
+ return (T + zeroOffset) / intervalSize;
+ }
+ }
+
+ /** Metric system of measurement. */
+ public static enum MeasurementUnit
+ {
+ FEMTO("Femto", "f", 0.000000000000001D),
+ PICO("Pico", "p", 0.000000000001D),
+ NANO("Nano", "n", 0.000000001D),
+ MICRO("Micro", "u", 0.000001D),
+ MILLI("Milli", "m", 0.001D),
+ BASE("", "", 1),
+ KILO("Kilo", "k", 1000D),
+ MEGA("Mega", "M", 1000000D),
+ GIGA("Giga", "G", 1000000000D),
+ TERA("Tera", "T", 1000000000000D),
+ PETA("Peta", "P", 1000000000000000D),
+ EXA("Exa", "E", 1000000000000000000D),
+ ZETTA("Zetta", "Z", 1000000000000000000000D),
+ YOTTA("Yotta", "Y", 1000000000000000000000000D);
+
+ /** long name for the unit */
+ public String name;
+
+ /** short unit version of the unit */
+ public String symbol;
+
+ /** Point by which a number is consider to be of this unit */
+ public double value;
+
+ private MeasurementUnit(String s, String s1, double v)
+ {
+ name = s;
+ symbol = s1;
+ value = v;
+ }
+
+ public String getName(boolean getShort)
+ {
+ if(getShort)
+ {
+ return symbol;
+ }
+ else {
+ return name;
+ }
+ }
+
+ public double process(double d)
+ {
+ return d / value;
+ }
+
+ public boolean above(double d)
+ {
+ return d > value;
+ }
+
+ public boolean below(double d)
+ {
+ return d < value;
+ }
+ }
+
+ /**
+ * Displays the unit as text. Does handle negative numbers, and will place a negative sign in
+ * front of the output string showing this. Use string.replace to remove the negative sign if
+ * unwanted
+ */
+ public static String getDisplay(double value, ElectricUnit unit, int decimalPlaces, boolean isShort)
+ {
+ String unitName = unit.name;
+ String prefix = "";
+
+ if(value < 0)
+ {
+ value = Math.abs(value);
+ prefix = "-";
+ }
+
+ if(isShort)
+ {
+ unitName = unit.symbol;
+ }
+ else if(value > 1)
+ {
+ unitName = unit.getPlural();
+ }
+
+ if(value == 0)
+ {
+ return value + " " + unitName;
+ }
+ else {
+ for(int i = 0; i < MeasurementUnit.values().length; i++)
+ {
+ MeasurementUnit lowerMeasure = MeasurementUnit.values()[i];
+
+ if(lowerMeasure.below(value) && lowerMeasure.ordinal() == 0)
+ {
+ return prefix + roundDecimals(lowerMeasure.process(value), decimalPlaces) + " " + lowerMeasure.getName(isShort) + unitName;
+ }
+
+ if(lowerMeasure.ordinal() + 1 >= MeasurementUnit.values().length)
+ {
+ return prefix + roundDecimals(lowerMeasure.process(value), decimalPlaces) + " " + lowerMeasure.getName(isShort) + unitName;
+ }
+
+ MeasurementUnit upperMeasure = MeasurementUnit.values()[i + 1];
+
+ if((lowerMeasure.above(value) && upperMeasure.below(value)) || lowerMeasure.value == value)
+ {
+ return prefix + roundDecimals(lowerMeasure.process(value), decimalPlaces) + " " + lowerMeasure.getName(isShort) + unitName;
+ }
+ }
+ }
+
+ return prefix + roundDecimals(value, decimalPlaces) + " " + unitName;
+ }
+
+ public static String getDisplayShort(double value, ElectricUnit unit)
+ {
+ return getDisplay(value, unit, 2, true);
+ }
+
+ public static String getDisplayShort(double value, ElectricUnit unit, int decimalPlaces)
+ {
+ return getDisplay(value, unit, decimalPlaces, true);
+ }
+
+ public static String getDisplaySimple(double value, ElectricUnit unit, int decimalPlaces)
+ {
+ if(value > 1)
+ {
+ if(decimalPlaces < 1)
+ {
+ return (int)value + " " + unit.getPlural();
+ }
+
+ return roundDecimals(value, decimalPlaces) + " " + unit.getPlural();
+ }
+
+ if(decimalPlaces < 1)
+ {
+ return (int)value + " " + unit.name;
+ }
+
+ return roundDecimals(value, decimalPlaces) + " " + unit.name;
+ }
+
+ public static String getDisplay(double T, TemperatureUnit unit, int decimalPlaces, boolean isShort)
+ {
+ String unitName = unit.name;
+ String prefix = "";
+
+ double value = unit.convertFromK(T);
+
+ if(value < 0)
+ {
+ value = Math.abs(value);
+ prefix = "-";
+ }
+
+ if(isShort)
+ {
+ unitName = unit.symbol;
+ }
+
+ if(value == 0)
+ {
+ return value + (isShort ? "" : " ") + unitName;
+ }
+ else {
+ for(int i = 0; i < MeasurementUnit.values().length; i++)
+ {
+ MeasurementUnit lowerMeasure = MeasurementUnit.values()[i];
+
+ if(lowerMeasure.below(value) && lowerMeasure.ordinal() == 0)
+ {
+ return prefix + roundDecimals(lowerMeasure.process(value), decimalPlaces) + (isShort ? "" : " ") + lowerMeasure.getName(isShort) + unitName;
+ }
+
+ if(lowerMeasure.ordinal() + 1 >= MeasurementUnit.values().length)
+ {
+ return prefix + roundDecimals(lowerMeasure.process(value), decimalPlaces) + (isShort ? "" : " ") + lowerMeasure.getName(isShort) + unitName;
+ }
+
+ MeasurementUnit upperMeasure = MeasurementUnit.values()[i + 1];
+
+ if((lowerMeasure.above(value) && upperMeasure.below(value)) || lowerMeasure.value == value)
+ {
+ return prefix + roundDecimals(lowerMeasure.process(value), decimalPlaces) + (isShort ? "" : " ") + lowerMeasure.getName(isShort) + unitName;
+ }
+ }
+ }
+
+ return prefix + roundDecimals(value, decimalPlaces) + (isShort ? "" : " ") + unitName;
+ }
+
+ public static String getDisplayShort(double value, TemperatureUnit unit)
+ {
+ return getDisplay(value, unit, 2, true);
+ }
+
+ public static String getDisplayShort(double value, TemperatureUnit unit, int decimalPlaces)
+ {
+ return getDisplay(value, unit, decimalPlaces, true);
+ }
+
+ public static double roundDecimals(double d, int decimalPlaces)
+ {
+ int j = (int)(d*Math.pow(10, decimalPlaces));
+ return j/Math.pow(10, decimalPlaces);
+ }
+
+ public static double roundDecimals(double d)
+ {
+ return roundDecimals(d, 2);
+ }
+
+ public static enum EnergyType
+ {
+ J,
+ RF,
+ EU,
+ MJ
+ }
+
+ public static enum TempType
+ {
+ K,
+ C,
+ R,
+ F,
+ STP
+ }
+}
diff --git a/src/api/java/mekanism/api/util/package-info.java b/src/api/java/mekanism/api/util/package-info.java
new file mode 100644
index 0000000..e5253bc
--- /dev/null
+++ b/src/api/java/mekanism/api/util/package-info.java
@@ -0,0 +1,3 @@
+@API(apiVersion = "8.0.0", owner = "Mekanism", provides = "MekanismAPI|util")
+package mekanism.api.util;
+import cpw.mods.fml.common.API; \ No newline at end of file