summaryrefslogtreecommitdiff
path: root/src/api/java/mekanism
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/java/mekanism')
-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
62 files changed, 5785 insertions, 0 deletions
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