aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/objects
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/gregtech/api/objects')
-rw-r--r--src/main/java/gregtech/api/objects/AE2DigitalChestHandler.java26
-rw-r--r--src/main/java/gregtech/api/objects/CollectorUtils.java30
-rw-r--r--src/main/java/gregtech/api/objects/ElementStack.java47
-rw-r--r--src/main/java/gregtech/api/objects/GT_ArrayList.java73
-rw-r--r--src/main/java/gregtech/api/objects/GT_ChunkManager.java204
-rw-r--r--src/main/java/gregtech/api/objects/GT_CopiedBlockTexture.java35
-rw-r--r--src/main/java/gregtech/api/objects/GT_Cover_Default.java81
-rw-r--r--src/main/java/gregtech/api/objects/GT_Cover_None.java237
-rw-r--r--src/main/java/gregtech/api/objects/GT_Fluid.java36
-rw-r--r--src/main/java/gregtech/api/objects/GT_HashSet.java91
-rw-r--r--src/main/java/gregtech/api/objects/GT_ItemStack.java107
-rw-r--r--src/main/java/gregtech/api/objects/GT_ItemStack2.java41
-rw-r--r--src/main/java/gregtech/api/objects/GT_MultiTexture.java26
-rw-r--r--src/main/java/gregtech/api/objects/GT_RenderedTexture.java33
-rw-r--r--src/main/java/gregtech/api/objects/GT_SidedTexture.java48
-rw-r--r--src/main/java/gregtech/api/objects/GT_StdRenderedTexture.java46
-rw-r--r--src/main/java/gregtech/api/objects/GT_UO_Dimension.java54
-rw-r--r--src/main/java/gregtech/api/objects/GT_UO_DimensionList.java98
-rw-r--r--src/main/java/gregtech/api/objects/GT_UO_Fluid.java69
-rw-r--r--src/main/java/gregtech/api/objects/ItemData.java122
-rw-r--r--src/main/java/gregtech/api/objects/MaterialStack.java70
-rw-r--r--src/main/java/gregtech/api/objects/ObjMap.java239
-rw-r--r--src/main/java/gregtech/api/objects/XSTR.java246
-rw-r--r--src/main/java/gregtech/api/objects/blockupdate/BlockUpdateHandler.java117
-rw-r--r--src/main/java/gregtech/api/objects/blockupdate/Cooldown.java27
-rw-r--r--src/main/java/gregtech/api/objects/blockupdate/RandomCooldown.java31
-rw-r--r--src/main/java/gregtech/api/objects/iterators/MergedIterator.java31
-rw-r--r--src/main/java/gregtech/api/objects/overclockdescriber/EUNoOverclockDescriber.java110
-rw-r--r--src/main/java/gregtech/api/objects/overclockdescriber/EUOverclockDescriber.java80
-rw-r--r--src/main/java/gregtech/api/objects/overclockdescriber/FusionOverclockDescriber.java63
-rw-r--r--src/main/java/gregtech/api/objects/overclockdescriber/OverclockDescriber.java106
-rw-r--r--src/main/java/gregtech/api/objects/overclockdescriber/SteamOverclockDescriber.java64
32 files changed, 2688 insertions, 0 deletions
diff --git a/src/main/java/gregtech/api/objects/AE2DigitalChestHandler.java b/src/main/java/gregtech/api/objects/AE2DigitalChestHandler.java
new file mode 100644
index 0000000000..c2e0556de9
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/AE2DigitalChestHandler.java
@@ -0,0 +1,26 @@
+package gregtech.api.objects;
+
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.metatileentity.BaseMetaTileEntity;
+import gregtech.common.tileentities.storage.GT_MetaTileEntity_DigitalChestBase;
+
+public class AE2DigitalChestHandler implements appeng.api.storage.IExternalStorageHandler {
+
+ @Override
+ public boolean canHandle(final TileEntity te, final ForgeDirection d, final appeng.api.storage.StorageChannel chan,
+ final appeng.api.networking.security.BaseActionSource mySrc) {
+ return chan == appeng.api.storage.StorageChannel.ITEMS && te instanceof BaseMetaTileEntity
+ && ((BaseMetaTileEntity) te).getMetaTileEntity() instanceof GT_MetaTileEntity_DigitalChestBase;
+ }
+
+ @Override
+ public appeng.api.storage.IMEInventory<?> getInventory(final TileEntity te, final ForgeDirection d,
+ final appeng.api.storage.StorageChannel chan, final appeng.api.networking.security.BaseActionSource src) {
+ if (chan == appeng.api.storage.StorageChannel.ITEMS) {
+ return ((GT_MetaTileEntity_DigitalChestBase) (((BaseMetaTileEntity) te).getMetaTileEntity()));
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/CollectorUtils.java b/src/main/java/gregtech/api/objects/CollectorUtils.java
new file mode 100644
index 0000000000..7265076683
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/CollectorUtils.java
@@ -0,0 +1,30 @@
+package gregtech.api.objects;
+
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+public class CollectorUtils {
+
+ /**
+ * Returns a merge function, suitable for use in {@link Map#merge(Object, Object, BiFunction) Map.merge()} or
+ * {@link Collectors#toMap(Function, Function, BinaryOperator) toMap()}, which always throws
+ * {@code IllegalStateException}. This can be used to enforce the assumption that the elements being collected are
+ * distinct.
+ *
+ * @param <T> the type of input arguments to the merge function
+ * @return a merge function which always throw {@code IllegalStateException}
+ */
+ public static <T> BinaryOperator<T> throwingMerger() {
+ return (u, v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
+ }
+
+ public static <K, V, E extends Map.Entry<K, V>, M extends Map<K, V>> Collector<E, ?, M> entriesToMap(
+ Supplier<M> mapSupplier) {
+ return Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, CollectorUtils.throwingMerger(), mapSupplier);
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/ElementStack.java b/src/main/java/gregtech/api/objects/ElementStack.java
new file mode 100644
index 0000000000..58fffd475a
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/ElementStack.java
@@ -0,0 +1,47 @@
+package gregtech.api.objects;
+
+import gregtech.api.enums.Element;
+
+public class ElementStack implements Cloneable {
+
+ public int mAmount;
+ public Element mElement;
+
+ public ElementStack(Element aElement, int aAmount) {
+ mElement = aElement == null ? Element._NULL : aElement;
+ mAmount = aAmount;
+ }
+
+ public ElementStack copy(int aAmount) {
+ return new ElementStack(mElement, aAmount);
+ }
+
+ @Override
+ public ElementStack clone() {
+ try {
+ return (ElementStack) super.clone();
+ } catch (Exception e) {
+ return new ElementStack(mElement, mAmount);
+ }
+ }
+
+ @Override
+ public boolean equals(Object aObject) {
+ if (aObject == this) return true;
+ if (aObject == null) return false;
+ if (aObject instanceof Element) return aObject == mElement;
+ if (aObject instanceof ElementStack) return ((ElementStack) aObject).mElement == mElement
+ && (mAmount < 0 || ((ElementStack) aObject).mAmount < 0 || ((ElementStack) aObject).mAmount == mAmount);
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return mElement.toString() + mAmount;
+ }
+
+ @Override
+ public int hashCode() {
+ return mElement.hashCode();
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_ArrayList.java b/src/main/java/gregtech/api/objects/GT_ArrayList.java
new file mode 100644
index 0000000000..9124ef8616
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_ArrayList.java
@@ -0,0 +1,73 @@
+package gregtech.api.objects;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Objects;
+
+import com.google.common.collect.Collections2;
+
+public class GT_ArrayList<E> extends ArrayList<E> {
+
+ private static final long serialVersionUID = 1L;
+ private int size_sS;
+
+ private final boolean mAllowNulls;
+
+ public GT_ArrayList(boolean aAllowNulls, int aCapacity) {
+ super(aCapacity);
+ mAllowNulls = aAllowNulls;
+ }
+
+ @SafeVarargs
+ public GT_ArrayList(boolean aAllowNulls, E... aArray) {
+ super(Arrays.asList(aArray));
+ mAllowNulls = aAllowNulls;
+ if (!mAllowNulls) {
+ size_sS = size();
+ for (int i = 0; i < size_sS; i++) if (get(i) == null) {
+ remove(i--);
+ size_sS = size();
+ }
+ }
+ }
+
+ public GT_ArrayList(boolean aAllowNulls, Collection<? extends E> aList) {
+ super(aList);
+ mAllowNulls = aAllowNulls;
+ if (!mAllowNulls) {
+ size_sS = size();
+ for (int i = 0; i < size_sS; i++) if (get(i) == null) {
+ remove(i--);
+ size_sS = size();
+ }
+ }
+ }
+
+ @Override
+ public E set(int aIndex, E aElement) {
+ if (mAllowNulls || aElement != null) return super.set(aIndex, aElement);
+ return null;
+ }
+
+ @Override
+ public boolean add(E aElement) {
+ if (mAllowNulls || aElement != null) return super.add(aElement);
+ return false;
+ }
+
+ @Override
+ public void add(int aIndex, E aElement) {
+ if (mAllowNulls || aElement != null) super.add(aIndex, aElement);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends E> aList) {
+ return super.addAll(Collections2.filter(aList, Objects::nonNull));
+ }
+
+ @Override
+ public boolean addAll(int aIndex, Collection<? extends E> aList) {
+ return super.addAll(aIndex, Collections2.filter(aList, Objects::nonNull));
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_ChunkManager.java b/src/main/java/gregtech/api/objects/GT_ChunkManager.java
new file mode 100644
index 0000000000..14baaddd3d
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_ChunkManager.java
@@ -0,0 +1,204 @@
+package gregtech.api.objects;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.ChunkCoordIntPair;
+import net.minecraft.world.World;
+import net.minecraftforge.common.ForgeChunkManager;
+import net.minecraftforge.common.ForgeChunkManager.Ticket;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+
+import gregtech.GT_Mod;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.interfaces.IChunkLoader;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Log;
+
+/**
+ * Handles re-initialization of chunks after a server restart.
+ */
+public class GT_ChunkManager
+ implements ForgeChunkManager.OrderedLoadingCallback, ForgeChunkManager.PlayerOrderedLoadingCallback {
+
+ private final Map<TileEntity, Ticket> registeredTickets = new HashMap<>();
+ public static GT_ChunkManager instance = new GT_ChunkManager();
+
+ public static void init() {
+ ForgeChunkManager.setForcedChunkLoadingCallback(GT_Mod.instance, instance);
+ }
+
+ @Override
+ public void ticketsLoaded(List<Ticket> tickets, World world) {}
+
+ /**
+ * Determines if tickets should be kept. Based on if the ticket is a machine or a working-chunk ticket.
+ * Working-chunk tickets are tossed and recreated when the machine reactivates.
+ * Machine tickets are kept only if the config {@code alwaysReloadChunkloaders} is true.
+ * Otherwise, machine chunks are tossed and recreated only when the machine reactivates,
+ * similarly to a Passive Anchor.
+ *
+ * @param tickets The tickets that you will want to select from.
+ * The list is immutable and cannot be manipulated directly. Copy it first.
+ * @param world The world
+ * @param maxTicketCount The maximum number of tickets that will be allowed.
+ * @return list of tickets
+ */
+
+ @Override
+ public List<Ticket> ticketsLoaded(List<Ticket> tickets, World world, int maxTicketCount) {
+ List<Ticket> validTickets = new ArrayList<>();
+ if (GT_Values.alwaysReloadChunkloaders) {
+ for (Ticket ticket : tickets) {
+ int x = ticket.getModData()
+ .getInteger("OwnerX");
+ int y = ticket.getModData()
+ .getInteger("OwnerY");
+ int z = ticket.getModData()
+ .getInteger("OwnerZ");
+ if (y > 0) {
+ TileEntity tile = world.getTileEntity(x, y, z);
+ if (tile instanceof IGregTechTileEntity && ((IGregTechTileEntity) tile).isAllowedToWork()) {
+ ForgeChunkManager.forceChunk(ticket, new ChunkCoordIntPair(x >> 4, z >> 4));
+ if (!registeredTickets.containsKey(tile)) {
+ registeredTickets.put(tile, ticket);
+ if (((IGregTechTileEntity) tile).getMetaTileEntity() instanceof IChunkLoader)
+ ForgeChunkManager.forceChunk(
+ ticket,
+ ((IChunkLoader) ((IGregTechTileEntity) tile).getMetaTileEntity()).getActiveChunk());
+ validTickets.add(ticket);
+ }
+ }
+ }
+ }
+ }
+ return validTickets;
+ }
+
+ /**
+ * Determines if player tickets should be kept. This is where a ticket list per-player would be created and
+ * maintained. When a player joins, an event occurs, their name/UUID/etc is compared against tickets on this list
+ * and those tickets are reactivated.
+ * Since that info would be maintained/dealt with on a per-player startup, the list returned back to Forge is empty.
+ *
+ * @param tickets The tickets that you will want to select from.
+ * The list is immutable and cannot be manipulated directly. Copy it first.
+ * @param world The world
+ * @return the list of string-ticket paris
+ */
+ @Override
+ public ListMultimap<String, Ticket> playerTicketsLoaded(ListMultimap<String, Ticket> tickets, World world) {
+ // Not currently used, so just return an empty list.
+ return ArrayListMultimap.create();
+ }
+
+ /**
+ * Requests a chunk to be loaded for this machine. May pass a {@code null} chunk to load just the machine itself if
+ * {@code alwaysReloadChunkloaders} is enabled in config.
+ *
+ * @param owner owner of the TileEntity
+ * @param chunkXZ chunk coordinates
+ * @param player player
+ * @return if the chunk was loaded successfully
+ */
+ public static boolean requestPlayerChunkLoad(TileEntity owner, ChunkCoordIntPair chunkXZ, String player) {
+ if (!GT_Values.enableChunkloaders) return false;
+ if (!GT_Values.alwaysReloadChunkloaders && chunkXZ == null) return false;
+ if (GT_Values.debugChunkloaders && chunkXZ != null) GT_Log.out
+ .println("GT_ChunkManager: Chunk request: (" + chunkXZ.chunkXPos + ", " + chunkXZ.chunkZPos + ")");
+ if (instance.registeredTickets.containsKey(owner)) {
+ ForgeChunkManager.forceChunk(instance.registeredTickets.get(owner), chunkXZ);
+ } else {
+ Ticket ticket;
+ if (player.equals("")) ticket = ForgeChunkManager
+ .requestTicket(GT_Mod.instance, owner.getWorldObj(), ForgeChunkManager.Type.NORMAL);
+ else ticket = ForgeChunkManager
+ .requestPlayerTicket(GT_Mod.instance, player, owner.getWorldObj(), ForgeChunkManager.Type.NORMAL);
+ if (ticket == null) {
+ if (GT_Values.debugChunkloaders)
+ GT_Log.out.println("GT_ChunkManager: ForgeChunkManager.requestTicket failed");
+ return false;
+ }
+ if (GT_Values.debugChunkloaders) GT_Log.out.println(
+ "GT_ChunkManager: ticket issued for machine at: (" + owner.xCoord
+ + ", "
+ + owner.yCoord
+ + ", "
+ + owner.zCoord
+ + ")");
+ NBTTagCompound tag = ticket.getModData();
+ tag.setInteger("OwnerX", owner.xCoord);
+ tag.setInteger("OwnerY", owner.yCoord);
+ tag.setInteger("OwnerZ", owner.zCoord);
+ ForgeChunkManager.forceChunk(ticket, chunkXZ);
+ if (GT_Values.alwaysReloadChunkloaders)
+ ForgeChunkManager.forceChunk(ticket, new ChunkCoordIntPair(owner.xCoord >> 4, owner.zCoord >> 4));
+ instance.registeredTickets.put(owner, ticket);
+ }
+ return true;
+ }
+
+ @SuppressWarnings("UnusedReturnValue")
+ public static boolean requestChunkLoad(TileEntity owner, ChunkCoordIntPair chunkXZ) {
+ return requestPlayerChunkLoad(owner, chunkXZ, "");
+ }
+
+ public static void releaseChunk(TileEntity owner, ChunkCoordIntPair chunkXZ) {
+ if (!GT_Values.enableChunkloaders) return;
+ Ticket ticket = instance.registeredTickets.get(owner);
+ if (ticket != null) {
+ if (GT_Values.debugChunkloaders) GT_Log.out
+ .println("GT_ChunkManager: Chunk release: (" + chunkXZ.chunkXPos + ", " + chunkXZ.chunkZPos + ")");
+ ForgeChunkManager.unforceChunk(ticket, chunkXZ);
+ }
+ }
+
+ public static void releaseTicket(TileEntity owner) {
+ if (!GT_Values.enableChunkloaders) return;
+ Ticket ticket = instance.registeredTickets.get(owner);
+ if (ticket != null) {
+ if (GT_Values.debugChunkloaders) {
+ GT_Log.out.println(
+ "GT_ChunkManager: ticket released by machine at: (" + owner.xCoord
+ + ", "
+ + owner.yCoord
+ + ", "
+ + owner.zCoord
+ + ")");
+ for (ChunkCoordIntPair chunk : ticket.getChunkList()) GT_Log.out
+ .println("GT_ChunkManager: Chunk release: (" + chunk.chunkXPos + ", " + chunk.chunkZPos + ")");
+ }
+ ForgeChunkManager.releaseTicket(ticket);
+ instance.registeredTickets.remove(owner);
+ }
+ }
+
+ public static void printTickets() {
+ GT_Log.out.println("GT_ChunkManager: Start forced chunks dump:");
+ instance.registeredTickets.forEach((machine, ticket) -> {
+ GT_Log.out.print(
+ "GT_ChunkManager: Chunks forced by the machine at (" + machine.xCoord
+ + ", "
+ + machine.yCoord
+ + ", "
+ + machine.zCoord
+ + ")");
+ if (ticket.isPlayerTicket()) GT_Log.out.print(" Owner: " + ticket.getPlayerName());
+ GT_Log.out.print(" :");
+ for (ChunkCoordIntPair c : ticket.getChunkList()) {
+ GT_Log.out.print("(");
+ GT_Log.out.print(c.chunkXPos);
+ GT_Log.out.print(", ");
+ GT_Log.out.print(c.chunkZPos);
+ GT_Log.out.print("), ");
+ }
+ });
+ GT_Log.out.println("GT_ChunkManager: End forced chunks dump:");
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_CopiedBlockTexture.java b/src/main/java/gregtech/api/objects/GT_CopiedBlockTexture.java
new file mode 100644
index 0000000000..c5307b4803
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_CopiedBlockTexture.java
@@ -0,0 +1,35 @@
+package gregtech.api.objects;
+
+import net.minecraft.block.Block;
+
+import gregtech.api.enums.Dyes;
+import gregtech.api.interfaces.ITexture;
+
+/**
+ * @deprecated Replaced by the {@link gregtech.api.render.TextureFactory} API.
+ */
+@Deprecated
+public class GT_CopiedBlockTexture extends gregtech.common.render.GT_CopiedBlockTexture implements ITexture {
+
+ // Backwards Compat
+ @Deprecated
+ public short[] mRGBa;
+
+ public GT_CopiedBlockTexture(Block aBlock, int ordinalSide, int aMeta, short[] aRGBa, boolean aAllowAlpha) {
+ super(aBlock, ordinalSide, aMeta, aRGBa, aAllowAlpha);
+ GT_CopiedBlockTexture.this.mRGBa = aRGBa;
+ }
+
+ public GT_CopiedBlockTexture(Block aBlock, int ordinalSide, int aMeta, short[] aRGBa) {
+ this(aBlock, ordinalSide, aMeta, aRGBa, true);
+ }
+
+ public GT_CopiedBlockTexture(Block aBlock, int ordinalSide, int aMeta) {
+ this(aBlock, ordinalSide, aMeta, Dyes._NULL.mRGBa);
+ }
+
+ @Override
+ public boolean isOldTexture() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_Cover_Default.java b/src/main/java/gregtech/api/objects/GT_Cover_Default.java
new file mode 100644
index 0000000000..cc5f96eef3
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_Cover_Default.java
@@ -0,0 +1,81 @@
+package gregtech.api.objects;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+
+import gregtech.api.interfaces.tileentity.ICoverable;
+import gregtech.api.util.GT_CoverBehavior;
+import gregtech.api.util.GT_Utility;
+
+public class GT_Cover_Default extends GT_CoverBehavior {
+
+ /**
+ * This is the Dummy, if there is a generic Cover without behavior
+ */
+ public GT_Cover_Default() {
+ super();
+ }
+
+ @Override
+ public boolean isSimpleCover() {
+ return true;
+ }
+
+ @Override
+ public int onCoverScrewdriverclick(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity,
+ EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ aCoverVariable = ((aCoverVariable + 1) & 15);
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ ((aCoverVariable & 1) != 0 ? GT_Utility.trans("128.1", "Redstone ") : "")
+ + ((aCoverVariable & 2) != 0 ? GT_Utility.trans("129.1", "Energy ") : "")
+ + ((aCoverVariable & 4) != 0 ? GT_Utility.trans("130.1", "Fluids ") : "")
+ + ((aCoverVariable & 8) != 0 ? GT_Utility.trans("131.1", "Items ") : ""));
+ return aCoverVariable;
+ }
+
+ @Override
+ public boolean letsRedstoneGoIn(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) {
+ return (aCoverVariable & 1) != 0;
+ }
+
+ @Override
+ public boolean letsRedstoneGoOut(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) {
+ return (aCoverVariable & 1) != 0;
+ }
+
+ @Override
+ public boolean letsEnergyIn(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) {
+ return (aCoverVariable & 2) != 0;
+ }
+
+ @Override
+ public boolean letsEnergyOut(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) {
+ return (aCoverVariable & 2) != 0;
+ }
+
+ @Override
+ public boolean letsFluidIn(ForgeDirection side, int aCoverID, int aCoverVariable, Fluid aFluid,
+ ICoverable aTileEntity) {
+ return (aCoverVariable & 4) != 0;
+ }
+
+ @Override
+ public boolean letsFluidOut(ForgeDirection side, int aCoverID, int aCoverVariable, Fluid aFluid,
+ ICoverable aTileEntity) {
+ return (aCoverVariable & 4) != 0;
+ }
+
+ @Override
+ public boolean letsItemsIn(ForgeDirection side, int aCoverID, int aCoverVariable, int aSlot,
+ ICoverable aTileEntity) {
+ return (aCoverVariable & 8) != 0;
+ }
+
+ @Override
+ public boolean letsItemsOut(ForgeDirection side, int aCoverID, int aCoverVariable, int aSlot,
+ ICoverable aTileEntity) {
+ return (aCoverVariable & 8) != 0;
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_Cover_None.java b/src/main/java/gregtech/api/objects/GT_Cover_None.java
new file mode 100644
index 0000000000..279efe63d8
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_Cover_None.java
@@ -0,0 +1,237 @@
+package gregtech.api.objects;
+
+import static gregtech.api.enums.GT_Values.E;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+
+import gregtech.api.interfaces.tileentity.ICoverable;
+import gregtech.api.util.GT_CoverBehavior;
+import gregtech.api.util.ISerializableObject;
+
+public class GT_Cover_None extends GT_CoverBehavior {
+
+ /**
+ * This is the Dummy, if there is no Cover
+ */
+ public GT_Cover_None() {}
+
+ @Override
+ public float getBlastProofLevel(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) {
+ return 10.0F;
+ }
+
+ @Override
+ public boolean letsRedstoneGoIn(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ public boolean letsRedstoneGoOut(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ public boolean letsEnergyIn(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ public boolean letsEnergyOut(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ public boolean letsFluidIn(ForgeDirection side, int aCoverID, int aCoverVariable, Fluid aFluid,
+ ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ public boolean letsFluidOut(ForgeDirection side, int aCoverID, int aCoverVariable, Fluid aFluid,
+ ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ public boolean letsItemsIn(ForgeDirection side, int aCoverID, int aCoverVariable, int aSlot,
+ ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ public boolean letsItemsOut(ForgeDirection side, int aCoverID, int aCoverVariable, int aSlot,
+ ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ public boolean isGUIClickable(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ public boolean manipulatesSidedRedstoneOutput(ForgeDirection side, int aCoverID, int aCoverVariable,
+ ICoverable aTileEntity) {
+ return false;
+ }
+
+ @Override
+ public boolean onCoverRightclick(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity,
+ EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ return false;
+ }
+
+ @Override
+ public boolean onCoverRemoval(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity,
+ boolean aForced) {
+ return true;
+ }
+
+ @Override
+ public int doCoverThings(ForgeDirection side, byte aInputRedstone, int aCoverID, int aCoverVariable,
+ ICoverable aTileEntity, long aTimer) {
+ return 0;
+ }
+
+ @Override
+ public boolean isSimpleCover() {
+ return true;
+ }
+
+ @Override
+ protected boolean isRedstoneSensitiveImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, long aTimer) {
+ return false;
+ }
+
+ @Override
+ protected ISerializableObject.LegacyCoverData doCoverThingsImpl(ForgeDirection side, byte aInputRedstone,
+ int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, long aTimer) {
+ return aCoverVariable;
+ }
+
+ @Override
+ protected boolean onCoverRightClickImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, float aX,
+ float aY, float aZ) {
+ return false;
+ }
+
+ @Override
+ protected ISerializableObject.LegacyCoverData onCoverScrewdriverClickImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, float aX,
+ float aY, float aZ) {
+ return aCoverVariable;
+ }
+
+ @Override
+ protected boolean onCoverShiftRightClickImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer) {
+ return false;
+ }
+
+ @Override
+ protected boolean onCoverRemovalImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, boolean aForced) {
+ return true;
+ }
+
+ @Override
+ protected String getDescriptionImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) {
+ return E;
+ }
+
+ @Override
+ protected float getBlastProofLevelImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) {
+ return 10.0F;
+ }
+
+ @Override
+ protected boolean letsRedstoneGoInImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ protected boolean letsRedstoneGoOutImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ protected boolean letsEnergyInImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ protected boolean letsEnergyOutImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ protected boolean letsFluidInImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, Fluid aFluid, ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ protected boolean letsFluidOutImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, Fluid aFluid, ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ protected boolean letsItemsInImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, int aSlot, ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ protected boolean letsItemsOutImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, int aSlot, ICoverable aTileEntity) {
+ return true;
+ }
+
+ @Override
+ protected boolean manipulatesSidedRedstoneOutputImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) {
+ return false;
+ }
+
+ @Override
+ protected boolean alwaysLookConnectedImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) {
+ return false;
+ }
+
+ @Override
+ protected byte getRedstoneInputImpl(ForgeDirection side, byte aInputRedstone, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) {
+ return aInputRedstone;
+ }
+
+ @Override
+ protected int getTickRateImpl(ForgeDirection side, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable,
+ ICoverable aTileEntity) {
+ return 0;
+ }
+
+ @Override
+ protected byte getLensColorImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) {
+ return -1;
+ }
+
+ @Override
+ protected ItemStack getDropImpl(ForgeDirection side, int aCoverID,
+ ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) {
+ return null;
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_Fluid.java b/src/main/java/gregtech/api/objects/GT_Fluid.java
new file mode 100644
index 0000000000..10824d6327
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_Fluid.java
@@ -0,0 +1,36 @@
+package gregtech.api.objects;
+
+import static gregtech.api.enums.Mods.GregTech;
+
+import net.minecraftforge.fluids.Fluid;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.fluid.GT_FluidFactory;
+
+/**
+ * @deprecated use {@link GT_FluidFactory#builder}
+ */
+@Deprecated
+public class GT_Fluid extends Fluid implements Runnable {
+
+ public final String mTextureName;
+ private final short[] mRGBa;
+
+ public GT_Fluid(String aName, String aTextureName, short[] aRGBa) {
+ super(aName);
+ mRGBa = aRGBa;
+ mTextureName = aTextureName;
+ GregTech_API.sGTBlockIconload.add(this);
+ }
+
+ @Override
+ public int getColor() {
+ return (Math.max(0, Math.min(255, mRGBa[0])) << 16) | (Math.max(0, Math.min(255, mRGBa[1])) << 8)
+ | Math.max(0, Math.min(255, mRGBa[2]));
+ }
+
+ @Override
+ public void run() {
+ setIcons(GregTech_API.sBlockIcons.registerIcon(GregTech.getResourcePath("fluids", "fluid." + mTextureName)));
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_HashSet.java b/src/main/java/gregtech/api/objects/GT_HashSet.java
new file mode 100644
index 0000000000..d42f194e5d
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_HashSet.java
@@ -0,0 +1,91 @@
+package gregtech.api.objects;
+
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import net.minecraft.item.ItemStack;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.util.GT_Utility;
+
+public class GT_HashSet<E extends GT_ItemStack> extends AbstractSet<E> {
+
+ private static final Object OBJECT = new Object();
+ private final transient HashMap<GT_ItemStack, Object> map;
+
+ public GT_HashSet() {
+ map = new HashMap<>();
+ GregTech_API.sItemStackMappings.add(map);
+ }
+
+ public GT_HashSet(Collection<? extends E> c) {
+ map = new HashMap<>(Math.max((int) (c.size() / .75f) + 1, 16));
+ addAll(c);
+ GregTech_API.sItemStackMappings.add(map);
+ }
+
+ public GT_HashSet(int initialCapacity, float loadFactor) {
+ map = new HashMap<>(initialCapacity, loadFactor);
+ GregTech_API.sItemStackMappings.add(map);
+ }
+
+ public GT_HashSet(int initialCapacity) {
+ map = new HashMap<>(initialCapacity);
+ GregTech_API.sItemStackMappings.add(map);
+ }
+
+ GT_HashSet(int initialCapacity, float loadFactor, boolean dummy) {
+ map = new LinkedHashMap<>(initialCapacity, loadFactor);
+ GregTech_API.sItemStackMappings.add(map);
+ }
+
+ public Map<GT_ItemStack, Object> getMap() {
+ return map;
+ }
+
+ @SuppressWarnings("unchecked") // The downcasting below will throw ClassCastException unless E is GT_ItemStack.
+ @Override
+ public Iterator<E> iterator() {
+ return (Iterator<E>) map.keySet()
+ .iterator();
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return map.containsKey(o);
+ }
+
+ public boolean add(ItemStack aStack) {
+ if (GT_Utility.isStackInvalid(aStack)) return false;
+ return map.put(new GT_ItemStack(aStack), OBJECT) == null;
+ }
+
+ @Override
+ public boolean add(E e) {
+ return map.put(e, OBJECT) == null;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return map.remove(o) == OBJECT;
+ }
+
+ @Override
+ public void clear() {
+ map.clear();
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_ItemStack.java b/src/main/java/gregtech/api/objects/GT_ItemStack.java
new file mode 100644
index 0000000000..492655740d
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_ItemStack.java
@@ -0,0 +1,107 @@
+package gregtech.api.objects;
+
+import net.minecraft.init.Items;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+import gregtech.api.enums.GT_Values;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.item.ItemHolder;
+import it.unimi.dsi.fastutil.Hash;
+
+/**
+ * An optimization of {@link ItemStack} to have a better {@code hashcode} and {@code equals} in order to improve
+ * {@code HashMap} and {@code Set} performance
+ */
+public class GT_ItemStack extends ItemHolder {
+
+ /**
+ * A better {@link Hash.Strategy} for {@link ItemStack}. Implementation originally from {@code GT_ItemStack2}.
+ */
+ public static final Hash.Strategy<ItemStack> ITEMSTACK_HASH_STRATEGY2 = new Hash.Strategy<>() {
+
+ @Override
+ public int hashCode(ItemStack o) {
+ return o.getItem()
+ .hashCode() * 38197 + Items.feather.getDamage(o);
+ }
+
+ @Override
+ public boolean equals(ItemStack a, ItemStack b) {
+ if (a == b) return true;
+ if (a == null || b == null) return false;
+ return a.getItem() == b.getItem() && Items.feather.getDamage(a) == Items.feather.getDamage(b);
+ }
+ };
+
+ public final Item mItem;
+ public final byte mStackSize;
+ public final short mMetaData;
+
+ public GT_ItemStack(Item aItem, long aStackSize, long aMetaData) {
+ super(new ItemStack(aItem, 1, (int) aMetaData));
+ mItem = aItem;
+ mStackSize = (byte) aStackSize;
+ mMetaData = (short) aMetaData;
+ }
+
+ public GT_ItemStack(ItemStack aStack) {
+ this(aStack, false);
+ }
+
+ public GT_ItemStack(ItemStack aStack, boolean wildcard) {
+ this(
+ aStack == null ? null : aStack.getItem(),
+ aStack == null ? 0 : aStack.stackSize,
+ aStack == null ? 0 : wildcard ? GT_Values.W : Items.feather.getDamage(aStack));
+ }
+
+ public GT_ItemStack(int aHashCode) {
+ this(GT_Utility.intToStack(aHashCode));
+ }
+
+ public final ItemStack toStack() {
+ if (mItem == null) return null;
+ return new ItemStack(mItem, 1, mMetaData);
+ }
+
+ public final boolean isStackEqual(ItemStack aStack) {
+ return GT_Utility.areStacksEqual(toStack(), aStack);
+ }
+
+ public final boolean isStackEqual(GT_ItemStack aStack) {
+ return GT_Utility.areStacksEqual(toStack(), aStack.toStack());
+ }
+
+ @Override
+ public boolean equals(Object aStack) {
+ if (aStack == this) return true;
+ if (aStack instanceof GT_ItemStack) {
+ return ((GT_ItemStack) aStack).mItem == mItem && ((GT_ItemStack) aStack).mMetaData == mMetaData;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return GT_Utility.stackToInt(toStack());
+ }
+
+ /**
+ * @see #internalCopyStack(ItemStack, boolean)
+ */
+ public static ItemStack internalCopyStack(ItemStack aStack) {
+ return internalCopyStack(aStack, false);
+ }
+
+ /**
+ * Replicates the copy behavior of {@link #toStack()} but for normal {@link ItemStack}s.
+ *
+ * @param aStack the stack to copy
+ * @param wildcard whether to use wildcard damage value
+ * @return a copy of the stack with stack size 1 and no NBT
+ */
+ public static ItemStack internalCopyStack(ItemStack aStack, boolean wildcard) {
+ return new ItemStack(aStack.getItem(), 1, wildcard ? GT_Values.W : Items.feather.getDamage(aStack));
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_ItemStack2.java b/src/main/java/gregtech/api/objects/GT_ItemStack2.java
new file mode 100644
index 0000000000..aa93876830
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_ItemStack2.java
@@ -0,0 +1,41 @@
+package gregtech.api.objects;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+/**
+ * GT_ItemStack, but with a better hashCode(). Due to this change, it should not be placed in the same hash based data
+ * structure with GT_ItemStack. It also shouldn't be used to construct search query into a hash based data structure
+ * that contains GT_ItemStack.
+ *
+ * @deprecated See {@link GT_ItemStack#ITEMSTACK_HASH_STRATEGY2}
+ */
+@Deprecated
+public class GT_ItemStack2 extends GT_ItemStack {
+
+ public GT_ItemStack2(Item aItem, long aStackSize, long aMetaData) {
+ super(aItem, aStackSize, aMetaData);
+ }
+
+ public GT_ItemStack2(ItemStack aStack) {
+ super(aStack);
+ }
+
+ public GT_ItemStack2(ItemStack aStack, boolean wildcard) {
+ super(aStack, wildcard);
+ }
+
+ @Override
+ public boolean equals(Object aStack) {
+ if (aStack == this) return true;
+ if (aStack instanceof GT_ItemStack) {
+ return ((GT_ItemStack) aStack).mItem == mItem && ((GT_ItemStack) aStack).mMetaData == mMetaData;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mItem.hashCode() * 38197 + mMetaData;
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_MultiTexture.java b/src/main/java/gregtech/api/objects/GT_MultiTexture.java
new file mode 100644
index 0000000000..9748ecb934
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_MultiTexture.java
@@ -0,0 +1,26 @@
+package gregtech.api.objects;
+
+import gregtech.api.interfaces.ITexture;
+
+/**
+ * <p>
+ * Lets Multiple ITextures Render overlay over each other.<
+ * </p>
+ * <p>
+ * I should have done this much earlier...
+ * </p>
+ *
+ * @deprecated Replaced by the {@link gregtech.api.render.TextureFactory} API.
+ */
+@Deprecated
+public class GT_MultiTexture extends gregtech.common.render.GT_MultiTexture implements ITexture {
+
+ public GT_MultiTexture(ITexture... aTextures) {
+ super(aTextures);
+ }
+
+ @Override
+ public boolean isOldTexture() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_RenderedTexture.java b/src/main/java/gregtech/api/objects/GT_RenderedTexture.java
new file mode 100644
index 0000000000..bbb22e7d36
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_RenderedTexture.java
@@ -0,0 +1,33 @@
+package gregtech.api.objects;
+
+import gregtech.api.enums.Dyes;
+import gregtech.api.interfaces.IColorModulationContainer;
+import gregtech.api.interfaces.IIconContainer;
+import gregtech.api.interfaces.ITexture;
+
+@Deprecated
+public class GT_RenderedTexture extends gregtech.common.render.GT_RenderedTexture
+ implements ITexture, IColorModulationContainer {
+
+ @Deprecated
+ public short[] mRGBa;
+
+ public GT_RenderedTexture(IIconContainer aIcon, short[] aRGBa, boolean aAllowAlpha) {
+ super(aIcon, aRGBa, aAllowAlpha, false, true, false);
+ if (aRGBa.length != 4) throw new IllegalArgumentException("RGBa doesn't have 4 Values @ GT_RenderedTexture");
+ mRGBa = aRGBa;
+ }
+
+ public GT_RenderedTexture(IIconContainer aIcon, short[] aRGBa) {
+ this(aIcon, aRGBa, true);
+ }
+
+ public GT_RenderedTexture(IIconContainer aIcon) {
+ this(aIcon, Dyes._NULL.mRGBa);
+ }
+
+ @Override
+ public boolean isOldTexture() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_SidedTexture.java b/src/main/java/gregtech/api/objects/GT_SidedTexture.java
new file mode 100644
index 0000000000..d042ebede9
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_SidedTexture.java
@@ -0,0 +1,48 @@
+package gregtech.api.objects;
+
+import gregtech.api.enums.Dyes;
+import gregtech.api.interfaces.IColorModulationContainer;
+import gregtech.api.interfaces.IIconContainer;
+import gregtech.api.interfaces.ITexture;
+
+/**
+ * @deprecated Replaced by the {@link gregtech.api.render.TextureFactory} API.
+ */
+@Deprecated
+public class GT_SidedTexture extends gregtech.common.render.GT_SidedTexture
+ implements ITexture, IColorModulationContainer {
+
+ @Deprecated
+ public short[] mRGBa;
+
+ public GT_SidedTexture(IIconContainer aIcon0, IIconContainer aIcon1, IIconContainer aIcon2, IIconContainer aIcon3,
+ IIconContainer aIcon4, IIconContainer aIcon5, short[] aRGBa, boolean aAllowAlpha) {
+ super(aIcon0, aIcon1, aIcon2, aIcon3, aIcon4, aIcon5, aRGBa, aAllowAlpha);
+
+ // Backwards Compat
+ GT_SidedTexture.this.mRGBa = aRGBa;
+ }
+
+ public GT_SidedTexture(IIconContainer aIcon0, IIconContainer aIcon1, IIconContainer aIcon2, IIconContainer aIcon3,
+ IIconContainer aIcon4, IIconContainer aIcon5, short[] aRGBa) {
+ this(aIcon0, aIcon1, aIcon2, aIcon3, aIcon4, aIcon5, aRGBa, true);
+ }
+
+ public GT_SidedTexture(IIconContainer aIcon0, IIconContainer aIcon1, IIconContainer aIcon2, IIconContainer aIcon3,
+ IIconContainer aIcon4, IIconContainer aIcon5) {
+ this(aIcon0, aIcon1, aIcon2, aIcon3, aIcon4, aIcon5, Dyes._NULL.mRGBa);
+ }
+
+ public GT_SidedTexture(IIconContainer aBottom, IIconContainer aTop, IIconContainer aSides, short[] aRGBa) {
+ this(aBottom, aTop, aSides, aSides, aSides, aSides, aRGBa);
+ }
+
+ public GT_SidedTexture(IIconContainer aBottom, IIconContainer aTop, IIconContainer aSides) {
+ this(aBottom, aTop, aSides, Dyes._NULL.mRGBa);
+ }
+
+ @Override
+ public boolean isOldTexture() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_StdRenderedTexture.java b/src/main/java/gregtech/api/objects/GT_StdRenderedTexture.java
new file mode 100644
index 0000000000..d4b8d16da6
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_StdRenderedTexture.java
@@ -0,0 +1,46 @@
+package gregtech.api.objects;
+
+import net.minecraft.block.Block;
+import net.minecraft.client.renderer.RenderBlocks;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.Dyes;
+import gregtech.api.interfaces.IIconContainer;
+import gregtech.api.util.LightingHelper;
+
+/**
+ * This ITexture implementation extends the GT_RenderedTexture class to render with bottom side flipped as with dumb
+ * blocks rendering. It is used in Ore blocks rendering so they better blends with dumb block ores from vanilla or other
+ * mods, when seen from bottom.
+ *
+ * @deprecated Replaced by the {@link gregtech.api.render.TextureFactory} API.
+ */
+@Deprecated
+public class GT_StdRenderedTexture extends GT_RenderedTexture {
+
+ @SuppressWarnings("unused")
+ public GT_StdRenderedTexture(IIconContainer aIcon, short[] aRGBa, boolean aAllowAlpha) {
+ super(aIcon, aRGBa, aAllowAlpha);
+ }
+
+ public GT_StdRenderedTexture(IIconContainer aIcon, short[] aRGBa) {
+ super(aIcon, aRGBa, true);
+ }
+
+ @SuppressWarnings("unused")
+ public GT_StdRenderedTexture(IIconContainer aIcon) {
+ super(aIcon, Dyes._NULL.mRGBa);
+ }
+
+ @Override
+ public void renderYNeg(RenderBlocks aRenderer, Block aBlock, int aX, int aY, int aZ) {
+ LightingHelper lighting = new LightingHelper(aRenderer);
+ lighting.setupLightingYNeg(aBlock, aX, aY, aZ)
+ .setupColor(ForgeDirection.DOWN, mRGBa);
+ aRenderer.renderFaceYNeg(aBlock, aX, aY, aZ, mIconContainer.getIcon());
+ if (mIconContainer.getOverlayIcon() != null) {
+ lighting.setupColor(ForgeDirection.DOWN, 0xffffff);
+ aRenderer.renderFaceYNeg(aBlock, aX, aY, aZ, mIconContainer.getOverlayIcon());
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_UO_Dimension.java b/src/main/java/gregtech/api/objects/GT_UO_Dimension.java
new file mode 100644
index 0000000000..af82c35dab
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_UO_Dimension.java
@@ -0,0 +1,54 @@
+package gregtech.api.objects;
+
+import java.util.Random;
+
+import net.minecraftforge.common.config.ConfigCategory;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
+public class GT_UO_Dimension {
+
+ private final BiMap<String, GT_UO_Fluid> fFluids;
+ private int maxChance;
+ public String Dimension = "null";
+
+ public GT_UO_Dimension(ConfigCategory aConfigCategory) { // TODO CONFIGURE
+ fFluids = HashBiMap.create();
+ if (aConfigCategory.containsKey("Dimension")) {
+ aConfigCategory.get("Dimension").comment = "Dimension ID or Class Name";
+ Dimension = aConfigCategory.get("Dimension")
+ .getString();
+ }
+ maxChance = 0;
+ // GT_FML_LOGGER.info("GT UO "+aConfigCategory.getName()+" Dimension:"+Dimension);
+ for (int i = 0; i < aConfigCategory.getChildren()
+ .size(); i++) {
+ GT_UO_Fluid fluid = new GT_UO_Fluid(
+ (ConfigCategory) aConfigCategory.getChildren()
+ .toArray()[i]);
+ fFluids.put(fluid.Registry, fluid);
+ maxChance += fluid.Chance;
+ }
+ }
+
+ public GT_UO_Fluid getRandomFluid(Random aRandom) {
+ int random = aRandom.nextInt(1000);
+ for (BiMap.Entry<String, GT_UO_Fluid> fl : fFluids.entrySet()) {
+ int chance = fl.getValue().Chance * 1000 / maxChance;
+ if (random <= chance) return fl.getValue();
+ // GT_FML_LOGGER.info("GT UO "+fl.getValue().Registry+" Chance:"+chance+" Random:"+random);
+ random -= chance;
+ }
+ return null;
+ }
+
+ public String getUOFluidKey(GT_UO_Fluid uoFluid) {
+ return fFluids.inverse()
+ .get(uoFluid);
+ }
+
+ public GT_UO_Fluid getUOFluid(String key) {
+ return fFluids.get(key);
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_UO_DimensionList.java b/src/main/java/gregtech/api/objects/GT_UO_DimensionList.java
new file mode 100644
index 0000000000..95d4246cb6
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_UO_DimensionList.java
@@ -0,0 +1,98 @@
+package gregtech.api.objects;
+
+import net.minecraftforge.common.DimensionManager;
+import net.minecraftforge.common.config.ConfigCategory;
+import net.minecraftforge.common.config.Configuration;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
+public class GT_UO_DimensionList {
+
+ private Configuration fConfig;
+ private String fCategory;
+ private final BiMap<String, GT_UO_Dimension> fDimensionList;
+
+ public int[] blackList = new int[0];
+
+ public GT_UO_DimensionList() {
+ fDimensionList = HashBiMap.create();
+ }
+
+ public GT_UO_Dimension GetDimension(int aDimension) {
+ if (CheckBlackList(aDimension)) return null;
+ if (fDimensionList.containsKey(Integer.toString(aDimension)))
+ return fDimensionList.get(Integer.toString(aDimension));
+ for (BiMap.Entry<String, GT_UO_Dimension> dl : fDimensionList.entrySet())
+ if (DimensionManager.getProvider(aDimension)
+ .getClass()
+ .getName()
+ .contains(dl.getValue().Dimension)) return dl.getValue();
+ return fDimensionList.get("Default");
+ }
+
+ private boolean CheckBlackList(int aDimensionId) {
+ try {
+ return java.util.Arrays.binarySearch(blackList, aDimensionId) >= 0;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ public void SetConfigValues(String aDimensionName, String aDimension, String aName, String aRegistry,
+ int aMinAmount, int aMaxAmount, int aChance, int aDecreasePerOperationAmount) {
+ String Category = fCategory + "." + aDimensionName;
+ fConfig.get(Category, "Dimension", aDimension)
+ .getString();
+ Category += "." + aName;
+ fConfig.get(Category, "Registry", aRegistry)
+ .getString();
+ fConfig.get(Category, "MinAmount", aMinAmount)
+ .getInt(aMinAmount);
+ fConfig.get(Category, "MaxAmount", aMaxAmount)
+ .getInt(aMaxAmount);
+ fConfig.get(Category, "Chance", aChance)
+ .getInt(aChance);
+ fConfig.get(Category, "DecreasePerOperationAmount", aDecreasePerOperationAmount)
+ .getInt(aDecreasePerOperationAmount);
+ // IT IS IN BUCKETS!!!
+ }
+
+ public void SetDafultValues() {
+ SetConfigValues("Overworld", "0", "gas_natural_gas", "gas_natural_gas", 0, 700, 20, 7);
+ SetConfigValues("Overworld", "0", "liquid_light_oil", "liquid_light_oil", 0, 650, 20, 6);
+ SetConfigValues("Overworld", "0", "liquid_medium_oil", "liquid_medium_oil", 0, 600, 20, 5);
+ SetConfigValues("Overworld", "0", "liquid_heavy_oil", "liquid_heavy_oil", 0, 550, 20, 4);
+ SetConfigValues("Overworld", "0", "oil", "oil", 0, 600, 20, 5);
+ SetConfigValues("Moon", "Moon", "helium-3", "helium-3", 24, 128, 100, 1);
+ }
+
+ public void getConfig(Configuration aConfig, String aCategory) {
+ fCategory = aCategory;
+ fConfig = aConfig;
+ if (!fConfig.hasCategory(fCategory)) SetDafultValues();
+
+ fConfig.setCategoryComment(fCategory, "Config Underground Fluids (Delete this Category for regenerate)");
+ fConfig.setCategoryComment(
+ fCategory + ".Default",
+ "Set Default Generating (Use this Category for Default settings)");
+ fConfig.setCategoryComment(fCategory + ".Overworld", "Set Overworld Generating");
+ fConfig.setCategoryComment(fCategory + ".Moon", "Set Moon Generating");
+
+ blackList = new int[] { -1, 1 };
+ blackList = aConfig.get(fCategory, "DimBlackList", blackList, "Dimension IDs Black List")
+ .getIntList();
+ java.util.Arrays.sort(blackList);
+
+ for (int i = 0; i < fConfig.getCategory(fCategory)
+ .getChildren()
+ .size(); i++) {
+ GT_UO_Dimension Dimension = new GT_UO_Dimension(
+ (ConfigCategory) fConfig.getCategory(fCategory)
+ .getChildren()
+ .toArray()[i]);
+ fDimensionList.put(Dimension.Dimension, Dimension);
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/GT_UO_Fluid.java b/src/main/java/gregtech/api/objects/GT_UO_Fluid.java
new file mode 100644
index 0000000000..7f9898e02e
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GT_UO_Fluid.java
@@ -0,0 +1,69 @@
+package gregtech.api.objects;
+
+import static gregtech.common.GT_UndergroundOil.DIVIDER;
+
+import java.util.Random;
+
+import net.minecraftforge.common.config.ConfigCategory;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidRegistry;
+
+public class GT_UO_Fluid {
+
+ public String Registry = "null";
+ public int MaxAmount = 0;
+ public int MinAmount = 0;
+ public int Chance = 0;
+ public int DecreasePerOperationAmount = 5;
+
+ public GT_UO_Fluid(ConfigCategory aConfigCategory) { // TODO CONFIGURE
+ if (aConfigCategory.containsKey("Registry")) {
+ aConfigCategory.get("Registry").comment = "Fluid registry name";
+ Registry = aConfigCategory.get("Registry")
+ .getString();
+ }
+ if (aConfigCategory.containsKey("MaxAmount")) {
+ aConfigCategory
+ .get("MaxAmount").comment = "Max amount generation (per operation, sets the VeinData) 80000 MAX";
+ MaxAmount = aConfigCategory.get("MaxAmount")
+ .getInt(0);
+ }
+ if (aConfigCategory.containsKey("MinAmount")) {
+ aConfigCategory.get("MinAmount").comment = "Min amount generation (per operation, sets the VeinData) 0 MIN";
+ MinAmount = aConfigCategory.get("MinAmount")
+ .getInt(0);
+ }
+ if (aConfigCategory.containsKey("Chance")) {
+ aConfigCategory
+ .get("Chance").comment = "Chance generating (weighted chance!, there will be a fluid in chunk always!)";
+ Chance = aConfigCategory.get("Chance")
+ .getInt(0);
+ }
+ if (aConfigCategory.containsKey("DecreasePerOperationAmount")) {
+ aConfigCategory.get(
+ "DecreasePerOperationAmount").comment = "Decrease per operation (actual fluid gained works like (Litre)VeinData/5000)";
+ DecreasePerOperationAmount = aConfigCategory.get("DecreasePerOperationAmount")
+ .getInt(5);
+ }
+ // GT_FML_LOGGER.info("GT UO "+aConfigCategory.getName()+" Fluid:"+Registry+" Max:"+MaxAmount+"
+ // Min:"+MinAmount+" Chance:"+Chance);
+ }
+
+ public Fluid getFluid() {
+ try {
+ return FluidRegistry.getFluid(this.Registry);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public int getRandomAmount(Random aRandom) { // generates some random ass number that correlates to extraction
+ // speeds
+ int smax = (int) Math.floor(Math.pow(MaxAmount * 100.d * DIVIDER, 0.2d)); // use scaled max and min values for
+ // the randomness to make high values
+ // more rare.
+ double smin = Math.pow(MinAmount * 100.d * DIVIDER, 0.2d);
+ double samount = Math.max(smin, aRandom.nextInt(smax) + aRandom.nextDouble());
+ return (int) (Math.pow(samount, 5) / 100); // reverses the computation above
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/ItemData.java b/src/main/java/gregtech/api/objects/ItemData.java
new file mode 100644
index 0000000000..779e45ac8b
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/ItemData.java
@@ -0,0 +1,122 @@
+package gregtech.api.objects;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+
+public class ItemData {
+
+ private static final MaterialStack[] EMPTY_MATERIALSTACK_ARRAY = new MaterialStack[0];
+
+ public final List<Object> mExtraData = new GT_ArrayList<>(false, 1);
+ public final OrePrefixes mPrefix;
+ public final MaterialStack mMaterial;
+ public final MaterialStack[] mByProducts;
+ public boolean mBlackListed = false;
+ public ItemStack mUnificationTarget = null;
+
+ public ItemData(OrePrefixes aPrefix, Materials aMaterial, boolean aBlackListed) {
+ mPrefix = aPrefix;
+ mMaterial = aMaterial == null ? null : new MaterialStack(aMaterial, aPrefix.mMaterialAmount);
+ mBlackListed = aBlackListed;
+ mByProducts = aPrefix.mSecondaryMaterial == null || aPrefix.mSecondaryMaterial.mMaterial == null
+ ? EMPTY_MATERIALSTACK_ARRAY
+ : new MaterialStack[] { aPrefix.mSecondaryMaterial.clone() };
+ }
+
+ public ItemData(OrePrefixes aPrefix, Materials aMaterial) {
+ this(aPrefix, aMaterial, false);
+ }
+
+ public ItemData(MaterialStack aMaterial, MaterialStack... aByProducts) {
+ mPrefix = null;
+ mMaterial = aMaterial.mMaterial == null ? null : aMaterial.clone();
+ mBlackListed = true;
+ if (aByProducts == null) {
+ mByProducts = EMPTY_MATERIALSTACK_ARRAY;
+ } else {
+ MaterialStack[] tByProducts = aByProducts.length < 1 ? EMPTY_MATERIALSTACK_ARRAY
+ : new MaterialStack[aByProducts.length];
+ int j = 0;
+ for (MaterialStack aByProduct : aByProducts)
+ if (aByProduct != null && aByProduct.mMaterial != null) tByProducts[j++] = aByProduct.clone();
+ mByProducts = j > 0 ? new MaterialStack[j] : EMPTY_MATERIALSTACK_ARRAY;
+ System.arraycopy(tByProducts, 0, mByProducts, 0, mByProducts.length);
+ }
+ }
+
+ public ItemData(Materials aMaterial, long aAmount, MaterialStack... aByProducts) {
+ this(new MaterialStack(aMaterial, aAmount), aByProducts);
+ }
+
+ public ItemData(Materials aMaterial, long aAmount, Materials aByProduct, long aByProductAmount) {
+ this(new MaterialStack(aMaterial, aAmount), new MaterialStack(aByProduct, aByProductAmount));
+ }
+
+ public ItemData(ItemData... aData) {
+ mPrefix = null;
+ mBlackListed = true;
+
+ ArrayList<MaterialStack> aList = new ArrayList<>(), rList = new ArrayList<>();
+
+ for (ItemData tData : aData) if (tData != null) {
+ if (tData.hasValidMaterialData() && tData.mMaterial.mAmount > 0) aList.add(tData.mMaterial.clone());
+ for (MaterialStack tMaterial : tData.mByProducts) if (tMaterial.mAmount > 0) aList.add(tMaterial.clone());
+ }
+
+ for (MaterialStack aMaterial : aList) {
+ boolean temp = true;
+ for (MaterialStack tMaterial : rList) if (aMaterial.mMaterial == tMaterial.mMaterial) {
+ tMaterial.mAmount += aMaterial.mAmount;
+ temp = false;
+ break;
+ }
+ if (temp) rList.add(aMaterial.clone());
+ }
+
+ rList.sort((a, b) -> Long.compare(b.mAmount, a.mAmount));
+
+ if (rList.isEmpty()) {
+ mMaterial = null;
+ } else {
+ mMaterial = rList.get(0);
+ rList.remove(0);
+ }
+
+ mByProducts = rList.toArray(new MaterialStack[0]);
+ }
+
+ public final boolean hasValidPrefixMaterialData() {
+ return mPrefix != null && mMaterial != null && mMaterial.mMaterial != null;
+ }
+
+ public final boolean hasValidPrefixData() {
+ return mPrefix != null;
+ }
+
+ public final boolean hasValidMaterialData() {
+ return mMaterial != null && mMaterial.mMaterial != null;
+ }
+
+ public final ArrayList<MaterialStack> getAllMaterialStacks() {
+ ArrayList<MaterialStack> rList = new ArrayList<>();
+ if (hasValidMaterialData()) rList.add(mMaterial);
+ rList.addAll(Arrays.asList(mByProducts));
+ return rList;
+ }
+
+ public final MaterialStack getByProduct(int aIndex) {
+ return aIndex >= 0 && aIndex < mByProducts.length ? mByProducts[aIndex] : null;
+ }
+
+ @Override
+ public String toString() {
+ if (mPrefix == null || mMaterial == null || mMaterial.mMaterial == null) return "";
+ return String.valueOf(mPrefix.name() + mMaterial.mMaterial.mName);
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/MaterialStack.java b/src/main/java/gregtech/api/objects/MaterialStack.java
new file mode 100644
index 0000000000..0a433e0d99
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/MaterialStack.java
@@ -0,0 +1,70 @@
+package gregtech.api.objects;
+
+import gregtech.api.enums.Materials;
+import gregtech.api.util.GT_Utility;
+
+public class MaterialStack implements Cloneable {
+
+ public long mAmount;
+ public Materials mMaterial;
+
+ public MaterialStack(Materials aMaterial, long aAmount) {
+ mMaterial = aMaterial == null ? Materials._NULL : aMaterial;
+ mAmount = aAmount;
+ }
+
+ public MaterialStack copy(long aAmount) {
+ return new MaterialStack(mMaterial, aAmount);
+ }
+
+ @Override
+ public MaterialStack clone() {
+ try {
+ return (MaterialStack) super.clone();
+ } catch (Exception e) {
+ return new MaterialStack(mMaterial, mAmount);
+ }
+ }
+
+ @Override
+ public boolean equals(Object aObject) {
+ if (aObject == this) return true;
+ if (aObject == null) return false;
+ if (aObject instanceof Materials) return aObject == mMaterial;
+ if (aObject instanceof MaterialStack) return ((MaterialStack) aObject).mMaterial == mMaterial
+ && (mAmount < 0 || ((MaterialStack) aObject).mAmount < 0 || ((MaterialStack) aObject).mAmount == mAmount);
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toString(false);
+ }
+
+ public String toString(boolean single) {
+ String temp1 = "", temp2 = mMaterial.getToolTip(true), temp3 = "", temp4 = "";
+ if (mAmount > 1) {
+ temp4 = GT_Utility.toSubscript(mAmount);
+ }
+ if ((!single || mAmount > 1) && isMaterialListComplex(this)) {
+ temp1 = "(";
+ temp3 = ")";
+ }
+ return temp1 + temp2 + temp3 + temp4;
+ }
+
+ private boolean isMaterialListComplex(MaterialStack materialStack) {
+ if (materialStack.mMaterial.mMaterialList.size() > 1) {
+ return true;
+ }
+ if (materialStack.mMaterial.mMaterialList.size() == 0) {
+ return false;
+ }
+ return isMaterialListComplex(materialStack.mMaterial.mMaterialList.get(0));
+ }
+
+ @Override
+ public int hashCode() {
+ return mMaterial.hashCode();
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/ObjMap.java b/src/main/java/gregtech/api/objects/ObjMap.java
new file mode 100644
index 0000000000..badc673464
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/ObjMap.java
@@ -0,0 +1,239 @@
+package gregtech.api.objects;
+
+import java.util.Arrays;
+
+/**
+ * Object-2-object map based on IntIntMap4a
+ */
+public class ObjMap<K, V> {
+
+ private static final Object FREE_KEY = new Object();
+ private static final Object REMOVED_KEY = new Object();
+
+ /** Keys and values */
+ private Object[] m_data;
+
+ /** Value for the null key (if inserted into a map) */
+ private Object m_nullValue;
+
+ private boolean m_hasNull;
+
+ /** Fill factor, must be between (0 and 1) */
+ private final float m_fillFactor;
+ /** We will resize a map once it reaches this size */
+ private int m_threshold;
+ /** Current map size */
+ private int m_size;
+ /** Mask to calculate the original position */
+ private int m_mask;
+ /** Mask to wrap the actual array pointer */
+ private int m_mask2;
+
+ public ObjMap(final int size, final float fillFactor) {
+ if (fillFactor <= 0 || fillFactor >= 1) throw new IllegalArgumentException("FillFactor must be in (0, 1)");
+ if (size <= 0) throw new IllegalArgumentException("Size must be positive!");
+ final int capacity = arraySize(size, fillFactor);
+ m_mask = capacity - 1;
+ m_mask2 = capacity * 2 - 1;
+ m_fillFactor = fillFactor;
+
+ m_data = new Object[capacity * 2];
+ Arrays.fill(m_data, FREE_KEY);
+
+ m_threshold = (int) (capacity * fillFactor);
+ }
+
+ @SuppressWarnings("unchecked")
+ public V get(final K key) {
+ if (key == null) return (V) m_nullValue; // we null it on remove, so safe not to check a flag here
+
+ int ptr = (key.hashCode() & m_mask) << 1;
+ Object k = m_data[ptr];
+
+ if (k == FREE_KEY) return null; // end of chain already
+ if (k.equals(key)) // we check FREE and REMOVED prior to this call
+ return (V) m_data[ptr + 1];
+ while (true) {
+ ptr = (ptr + 2) & m_mask2; // that's next index
+ k = m_data[ptr];
+ if (k == FREE_KEY) return null;
+ if (k.equals(key)) return (V) m_data[ptr + 1];
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public V put(final K key, final V value) {
+ if (key == null) return insertNullKey(value);
+
+ int ptr = getStartIndex(key) << 1;
+ Object k = m_data[ptr];
+
+ if (k == FREE_KEY) // end of chain already
+ {
+ m_data[ptr] = key;
+ m_data[ptr + 1] = value;
+ if (m_size >= m_threshold) rehash(m_data.length * 2); // size is set inside
+ else++m_size;
+ return null;
+ } else if (k.equals(key)) // we check FREE and REMOVED prior to this call
+ {
+ final Object ret = m_data[ptr + 1];
+ m_data[ptr + 1] = value;
+ return (V) ret;
+ }
+
+ int firstRemoved = -1;
+ if (k == REMOVED_KEY) firstRemoved = ptr; // we may find a key later
+
+ while (true) {
+ ptr = (ptr + 2) & m_mask2; // that's next index calculation
+ k = m_data[ptr];
+ if (k == FREE_KEY) {
+ if (firstRemoved != -1) ptr = firstRemoved;
+ m_data[ptr] = key;
+ m_data[ptr + 1] = value;
+ if (m_size >= m_threshold) rehash(m_data.length * 2); // size is set inside
+ else++m_size;
+ return null;
+ } else if (k.equals(key)) {
+ final Object ret = m_data[ptr + 1];
+ m_data[ptr + 1] = value;
+ return (V) ret;
+ } else if (k == REMOVED_KEY) {
+ if (firstRemoved == -1) firstRemoved = ptr;
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public V remove(final K key) {
+ if (key == null) return removeNullKey();
+
+ int ptr = getStartIndex(key) << 1;
+ Object k = m_data[ptr];
+ if (k == FREE_KEY) return null; // end of chain already
+ else if (k.equals(key)) // we check FREE and REMOVED prior to this call
+ {
+ --m_size;
+ if (m_data[(ptr + 2) & m_mask2] == FREE_KEY) m_data[ptr] = FREE_KEY;
+ else m_data[ptr] = REMOVED_KEY;
+ final V ret = (V) m_data[ptr + 1];
+ m_data[ptr + 1] = null;
+ return ret;
+ }
+ while (true) {
+ ptr = (ptr + 2) & m_mask2; // that's next index calculation
+ k = m_data[ptr];
+ if (k == FREE_KEY) return null;
+ else if (k.equals(key)) {
+ --m_size;
+ if (m_data[(ptr + 2) & m_mask2] == FREE_KEY) m_data[ptr] = FREE_KEY;
+ else m_data[ptr] = REMOVED_KEY;
+ final V ret = (V) m_data[ptr + 1];
+ m_data[ptr + 1] = null;
+ return ret;
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private V insertNullKey(final V value) {
+ if (m_hasNull) {
+ final Object ret = m_nullValue;
+ m_nullValue = value;
+ return (V) ret;
+ } else {
+ m_nullValue = value;
+ ++m_size;
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private V removeNullKey() {
+ if (m_hasNull) {
+ final Object ret = m_nullValue;
+ m_nullValue = null;
+ m_hasNull = false;
+ --m_size;
+ return (V) ret;
+ } else {
+ return null;
+ }
+ }
+
+ public int size() {
+ return m_size;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void rehash(final int newCapacity) {
+ m_threshold = (int) (newCapacity / 2 * m_fillFactor);
+ m_mask = newCapacity / 2 - 1;
+ m_mask2 = newCapacity - 1;
+
+ final int oldCapacity = m_data.length;
+ final Object[] oldData = m_data;
+
+ m_data = new Object[newCapacity];
+ Arrays.fill(m_data, FREE_KEY);
+
+ m_size = m_hasNull ? 1 : 0;
+
+ for (int i = 0; i < oldCapacity; i += 2) {
+ final Object oldKey = oldData[i];
+ if (oldKey != FREE_KEY && oldKey != REMOVED_KEY) put((K) oldKey, (V) oldData[i + 1]);
+ }
+ }
+
+ public int getStartIndex(final Object key) {
+ // key is not null here
+ return key.hashCode() & m_mask;
+ }
+
+ /* Taken from FastUtil implementation */
+
+ /**
+ * Return the least power of two greater than or equal to the specified value.
+ *
+ * <p>
+ * Note that this function will return 1 when the argument is 0.
+ *
+ * @param x a long integer smaller than or equal to 2<sup>62</sup>.
+ * @return the least power of two greater than or equal to the specified value.
+ */
+ public static long nextPowerOfTwo(long x) {
+ if (x == 0) return 1;
+ x--;
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ return (x | x >> 32) + 1;
+ }
+
+ /**
+ * Returns the least power of two smaller than or equal to 2<sup>30</sup> and larger than or equal to
+ * <code>Math.ceil( expected / f )</code>.
+ *
+ * @param expected the expected number of elements in a hash table.
+ * @param f the load factor.
+ * @return the minimum possible size for a backing array.
+ * @throws IllegalArgumentException if the necessary size is larger than 2<sup>30</sup>.
+ */
+ public static int arraySize(final int expected, final float f) {
+ final long s = Math.max(2, nextPowerOfTwo((long) Math.ceil(expected / f)));
+ if (s > (1 << 30)) throw new IllegalArgumentException(
+ "Too large (" + expected + " expected elements with load factor " + f + ")");
+ return (int) s;
+ }
+
+ // taken from FastUtil
+ private static final int INT_PHI = 0x9E3779B9;
+
+ public static int phiMix(final int x) {
+ final int h = x * INT_PHI;
+ return h ^ (h >> 16);
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/XSTR.java b/src/main/java/gregtech/api/objects/XSTR.java
new file mode 100644
index 0000000000..33823d3ebd
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/XSTR.java
@@ -0,0 +1,246 @@
+package gregtech.api.objects;
+
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicLong;
+
+/*
+ * TODO: Check the validity of the algorithm.
+ * There is a claim that this particular implementation is not faithful to the articles it links, skewing the
+ * distribution.
+ */
+/**
+ * XSTR - Xorshift ThermiteRandom Modified by Bogdan-G 03.06.2016 version 0.0.4
+ * <p>
+ * A subclass of java.util.random that implements the Xorshift random number generator
+ * <p>
+ * - it is 30% faster than the generator from Java's library - it produces random sequences of higher quality than
+ * java.util.Random - this class also provides a clone() function
+ * <p>
+ * Usage: XSRandom rand = new XSRandom(); //Instantiation x = rand.nextInt(); //pull a random number
+ * <p>
+ * To use the class in legacy code, you may also instantiate an XSRandom object and assign it to a java.util.Random
+ * object: java.util.Random rand = new XSRandom();
+ * <p>
+ * for an explanation of the algorithm, see http://demesos.blogspot.com/2011/09/pseudo-random-number-generators.html
+ *
+ * @author Wilfried Elmenreich University of Klagenfurt/Lakeside Labs http://www.elmenreich.tk
+ * <p>
+ * This code is released under the GNU Lesser General Public License Version 3
+ * http://www.gnu.org/licenses/lgpl-3.0.txt
+ */
+public class XSTR extends Random {
+
+ private static final long serialVersionUID = 6208727693524452904L;
+ private long seed;
+ private long last;
+ private static final long GAMMA = 0x9e3779b97f4a7c15L;
+ private static final int PROBE_INCREMENT = 0x9e3779b9;
+ private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL;
+ private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
+ private static final float FLOAT_UNIT = 0x1.0p-24f; // 1.0f / (1 << 24)
+ private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L);
+ public static final XSTR XSTR_INSTANCE = new XSTR() {
+
+ @Override
+ public synchronized void setSeed(long seed) {
+ if (!Thread.currentThread()
+ .getStackTrace()[2].getClassName()
+ .equals(Random.class.getName()))
+ throw new NoSuchMethodError("This is meant to be shared!, leave seed state alone!");
+ }
+ };
+
+ /*
+ * MODIFIED BY: Robotia Modification: Implemented Random class seed generator
+ */
+ /**
+ * Creates a new pseudo random number generator. The seed is initialized to the current time, as if by
+ * <code>setSeed(System.currentTimeMillis());</code>.
+ */
+ public XSTR() {
+ this(seedUniquifier() ^ System.nanoTime());
+ }
+
+ private static long seedUniquifier() {
+ // L'Ecuyer, "Tables of Linear Congruential Generators of
+ // Different Sizes and Good Lattice Structure", 1999
+ for (;;) {
+ long current = seedUniquifier.get();
+ long next = current * 181783497276652981L;
+ if (seedUniquifier.compareAndSet(current, next)) {
+ return next;
+ }
+ }
+ }
+
+ /**
+ * Creates a new pseudo random number generator, starting with the specified seed, using
+ * <code>setSeed(seed);</code>.
+ *
+ * @param seed the initial seed
+ */
+ public XSTR(long seed) {
+ this.seed = seed;
+ }
+
+ @Override
+ public boolean nextBoolean() {
+ return next(1) != 0;
+ }
+
+ @Override
+ public double nextDouble() {
+ return (((long) (next(26)) << 27) + next(27)) * DOUBLE_UNIT;
+ }
+
+ /**
+ * Returns the current state of the seed, can be used to clone the object
+ *
+ * @return the current seed
+ */
+ public synchronized long getSeed() {
+ return seed;
+ }
+
+ /**
+ * Sets the seed for this pseudo random number generator. As described above, two instances of the same random
+ * class, starting with the same seed, produce the same results, if the same methods are called.
+ *
+ * @param seed the new seed
+ */
+ @Override
+ public synchronized void setSeed(long seed) {
+ this.seed = seed;
+ }
+
+ /**
+ * @return Returns an XSRandom object with the same state as the original
+ */
+ @Override
+ public XSTR clone() {
+ return new XSTR(getSeed());
+ }
+
+ /**
+ * Implementation of George Marsaglia's Xorshift random generator that is 30% faster and better quality than the
+ * built-in java.util.random.
+ *
+ * @param nbits number of bits to shift the result for
+ * @return a random integer
+ * @see <a href="https://www.javamex.com/tutorials/random_numbers/xorshift.shtml">the Xorshift article</a>
+ */
+ @Override
+ public int next(int nbits) {
+ long x = seed;
+ x ^= (x << 21);
+ x ^= (x >>> 35);
+ x ^= (x << 4);
+ seed = x;
+ x &= ((1L << nbits) - 1);
+ return (int) x;
+ }
+
+ boolean haveNextNextGaussian = false;
+ double nextNextGaussian = 0;
+
+ @Override
+ public synchronized double nextGaussian() {
+ // See Knuth, ACP, Section 3.4.1 Algorithm C.
+ if (haveNextNextGaussian) {
+ haveNextNextGaussian = false;
+ return nextNextGaussian;
+ } else {
+ double v1, v2, s;
+ do {
+ v1 = 2 * nextDouble() - 1; // between -1 and 1
+ v2 = 2 * nextDouble() - 1; // between -1 and 1
+ s = v1 * v1 + v2 * v2;
+ } while (s >= 1 || s == 0);
+ double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s) / s);
+ nextNextGaussian = v2 * multiplier;
+ haveNextNextGaussian = true;
+ return v1 * multiplier;
+ }
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed {@code int} value between 0 (inclusive) and the specified value
+ * (exclusive), drawn from this random number generator's sequence. The general contract of {@code nextInt} is that
+ * one {@code int} value in the specified range is pseudorandomly generated and returned. All {@code bound} possible
+ * {@code int} values are produced with (approximately) equal probability. The method {@code nextInt(int bound)} is
+ * implemented by class {@code Random} as if by:
+ *
+ * <pre>
+ * {@code
+ * public int nextInt(int bound) {
+ * if (bound <= 0)
+ * throw new IllegalArgumentException("bound must be positive");
+ *
+ * if ((bound & -bound) == bound) // i.e., bound is a power of 2
+ * return (int)((bound * (long)next(31)) >> 31);
+ *
+ * int bits, val;
+ * do {
+ * bits = next(31);
+ * val = bits % bound;
+ * } while (bits - val + (bound-1) < 0);
+ * return val;
+ * }}
+ * </pre>
+ *
+ * <p>
+ * The next method is only approximately an unbiased source of independently chosen bits. If it were a perfect
+ * source of randomly chosen bits, then the algorithm shown would choose {@code int} values from the stated range
+ * with perfect uniformity.
+ * <p>
+ * The algorithm is slightly tricky. It rejects values that would result in an uneven distribution (due to the fact
+ * that 2^31 is not divisible by n). The probability of a value being rejected depends on n. The worst case is
+ * n=2^30+1, for which the probability of a reject is 1/2, and the expected number of iterations before the loop
+ * terminates is 2.
+ * <p>
+ * The algorithm treats the case where n is a power of two specially: it returns the correct number of high-order
+ * bits from the underlying pseudo-random number generator. In the absence of special treatment, the correct number
+ * of <i>low-order</i> bits would be returned. Linear congruential pseudo-random number generators such as the one
+ * implemented by this class are known to have short periods in the sequence of values of their low-order bits.
+ * Thus, this special case greatly increases the length of the sequence of values returned by successive calls to
+ * this method if n is a small power of two.
+ *
+ * @param bound the upper bound (exclusive). Must be positive.
+ * @return the next pseudorandom, uniformly distributed {@code int} value between zero (inclusive) and {@code bound}
+ * (exclusive) from this random number generator's sequence
+ * @throws IllegalArgumentException if bound is not positive
+ * @since 1.2
+ */
+ @Override
+ public int nextInt(int bound) {
+ last = seed ^ (seed << 21);
+ last ^= (last >>> 35);
+ last ^= (last << 4);
+ seed = last;
+ int out = (int) last % bound;
+ return (out < 0) ? -out : out;
+ }
+
+ @Override
+ public int nextInt() {
+ return next(32);
+ }
+
+ @Override
+ public float nextFloat() {
+ return next(24) * FLOAT_UNIT;
+ }
+
+ @Override
+ public long nextLong() {
+ // it's okay that the bottom word remains signed.
+ return ((long) (next(32)) << 32) + next(32);
+ }
+
+ @Override
+ public void nextBytes(byte[] bytes_arr) {
+ for (int iba = 0, lenba = bytes_arr.length; iba < lenba;)
+ for (int rndba = nextInt(), nba = Math.min(lenba - iba, Integer.SIZE / Byte.SIZE); nba--
+ > 0; rndba >>= Byte.SIZE) bytes_arr[iba++] = (byte) rndba;
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/blockupdate/BlockUpdateHandler.java b/src/main/java/gregtech/api/objects/blockupdate/BlockUpdateHandler.java
new file mode 100644
index 0000000000..e8f084ea34
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/blockupdate/BlockUpdateHandler.java
@@ -0,0 +1,117 @@
+package gregtech.api.objects.blockupdate;
+
+import java.util.HashMap;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.EntityClientPlayerMP;
+import net.minecraft.world.ChunkCoordIntPair;
+import net.minecraft.world.World;
+import net.minecraft.world.chunk.Chunk;
+
+import appeng.api.util.WorldCoord;
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.common.eventhandler.SubscribeEvent;
+import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
+import cpw.mods.fml.common.gameevent.TickEvent.Phase;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.api.enums.TickTime;
+
+// this is a middleware for block updates
+// Greg's BaseMetaTileEntity uses World::markBlockForUpdate() to update the texture of a machine
+// World::markBlockForUpdate() triggers block updates of all blocks within the same chunk
+// this class makes sure chunk updates are perfmormed only once even if several blocks requested update
+// (valid obly for requests made using BlockUpdateHandler::enqueueBlockUpdate)
+// and introduces a per chunk cooldown to slow the things a bit
+// cause too frequent updates are not necessary for visual appearance of a block
+
+@SideOnly(Side.CLIENT)
+public class BlockUpdateHandler {
+
+ public static final int MIN_UPDATE_COOLDOWN = TickTime.SECOND / 2;
+ public static final int MAX_UPDATE_COOLDOWN = TickTime.SECOND;
+
+ public final static BlockUpdateHandler Instance = new BlockUpdateHandler();
+
+ private BlockUpdateHandler() {
+
+ blocksToUpdate = new HashMap<ChunkCoordIntPair, WorldCoord>();
+ cooldowns = new HashMap<ChunkCoordIntPair, RandomCooldown>();
+
+ FMLCommonHandler.instance()
+ .bus()
+ .register(this);
+ }
+
+ public void enqueueBlockUpdate(World world, WorldCoord pos) {
+
+ var player = getPlayer();
+
+ if (world != player.worldObj) return;
+
+ ResetDataIfPlayerWorldChanged(player);
+
+ blocksToUpdate.put(getBlockChunkCoords(world, pos), pos);
+ }
+
+ @SubscribeEvent
+ public void OnClientTickEvent(ClientTickEvent event) {
+
+ if (event.phase != Phase.START) return;
+
+ ResetDataIfPlayerWorldChanged(getPlayer());
+
+ var it = blocksToUpdate.entrySet()
+ .iterator();
+
+ while (it.hasNext()) {
+
+ var entry = it.next();
+ ChunkCoordIntPair chunkCoords = entry.getKey();
+ WorldCoord blockCoords = entry.getValue();
+
+ RandomCooldown cooldown = cooldowns.get(chunkCoords);
+
+ if (cooldown == null) {
+ cooldown = new RandomCooldown(MIN_UPDATE_COOLDOWN, MAX_UPDATE_COOLDOWN);
+ cooldowns.put(chunkCoords, cooldown);
+ }
+
+ if (!cooldown.hasPassed(internalTickCounter)) continue;
+
+ currWorld.markBlockForUpdate(blockCoords.x, blockCoords.y, blockCoords.z);
+ cooldown.set(internalTickCounter);
+ it.remove();
+ }
+
+ ++internalTickCounter;
+ }
+
+ private EntityClientPlayerMP getPlayer() {
+ return Minecraft.getMinecraft().thePlayer;
+ }
+
+ private void ResetDataIfPlayerWorldChanged(EntityClientPlayerMP player) {
+
+ if (player == null) return;
+
+ World playerWorld = player.worldObj;
+
+ if (currWorld != playerWorld) {
+ blocksToUpdate.clear();
+ cooldowns.clear();
+ currWorld = playerWorld;
+ }
+ }
+
+ private ChunkCoordIntPair getBlockChunkCoords(World world, WorldCoord pos) {
+
+ Chunk chunk = world.getChunkFromBlockCoords(pos.x, pos.z);
+ return chunk.getChunkCoordIntPair();
+ }
+
+ private HashMap<ChunkCoordIntPair, WorldCoord> blocksToUpdate;
+ private HashMap<ChunkCoordIntPair, RandomCooldown> cooldowns;
+ private World currWorld = null;
+ private long internalTickCounter = 0;
+}
diff --git a/src/main/java/gregtech/api/objects/blockupdate/Cooldown.java b/src/main/java/gregtech/api/objects/blockupdate/Cooldown.java
new file mode 100644
index 0000000000..e00fc1c770
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/blockupdate/Cooldown.java
@@ -0,0 +1,27 @@
+package gregtech.api.objects.blockupdate;
+
+public class Cooldown {
+
+ public Cooldown(int aLengthInTicks) {
+
+ if (aLengthInTicks <= 0) throw new IllegalArgumentException("length should be a positive non-zero number");
+
+ this.lengthInTicks = aLengthInTicks;
+ this.lastTimeStarted = 0;
+ }
+
+ public void set(long currTickTime) {
+ lastTimeStarted = currTickTime;
+ }
+
+ public boolean hasPassed(long currTickTime) {
+ return currTickTime - lastTimeStarted >= lengthInTicks;
+ }
+
+ public long getLastTimeStarted() {
+ return lastTimeStarted;
+ }
+
+ private long lastTimeStarted;
+ protected int lengthInTicks;
+}
diff --git a/src/main/java/gregtech/api/objects/blockupdate/RandomCooldown.java b/src/main/java/gregtech/api/objects/blockupdate/RandomCooldown.java
new file mode 100644
index 0000000000..d275c29744
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/blockupdate/RandomCooldown.java
@@ -0,0 +1,31 @@
+package gregtech.api.objects.blockupdate;
+
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+public class RandomCooldown extends Cooldown {
+
+ public RandomCooldown(int aMinLengthInTicks, int aMaxLengthInTicks) {
+
+ super(aMinLengthInTicks);
+
+ if (aMinLengthInTicks <= 0)
+ throw new IllegalArgumentException("min length should be a positive non-zero number");
+ if (aMaxLengthInTicks <= 0)
+ throw new IllegalArgumentException("max length should be a positive non-zero number");
+ if (aMinLengthInTicks > aMaxLengthInTicks)
+ throw new IllegalArgumentException("min length should be less or equal to max length");
+
+ this.minLengthInTicks = aMinLengthInTicks;
+ this.maxLengthInTicks = aMaxLengthInTicks;
+ }
+
+ @Override
+ public void set(long currTickTime) {
+
+ super.set(currTickTime);
+ lengthInTicks = minLengthInTicks + XSTR_INSTANCE.nextInt(maxLengthInTicks - minLengthInTicks + 1);
+ }
+
+ private int minLengthInTicks;
+ private int maxLengthInTicks;
+}
diff --git a/src/main/java/gregtech/api/objects/iterators/MergedIterator.java b/src/main/java/gregtech/api/objects/iterators/MergedIterator.java
new file mode 100644
index 0000000000..961c98e81a
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/iterators/MergedIterator.java
@@ -0,0 +1,31 @@
+package gregtech.api.objects.iterators;
+
+import java.util.Iterator;
+
+public class MergedIterator<T> implements Iterator<T> {
+
+ private final Iterator<T>[] inners;
+ private int current;
+
+ @SafeVarargs
+ public MergedIterator(Iterator<T>... iterators) {
+ inners = iterators;
+ current = 0;
+ }
+
+ public boolean hasNext() {
+ while (current < inners.length && !inners[current].hasNext()) {
+ current++;
+ }
+
+ return current < inners.length;
+ }
+
+ public T next() {
+ while (current < inners.length && !inners[current].hasNext()) {
+ current++;
+ }
+
+ return inners[current].next();
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/overclockdescriber/EUNoOverclockDescriber.java b/src/main/java/gregtech/api/objects/overclockdescriber/EUNoOverclockDescriber.java
new file mode 100644
index 0000000000..1e29e2d812
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/overclockdescriber/EUNoOverclockDescriber.java
@@ -0,0 +1,110 @@
+package gregtech.api.objects.overclockdescriber;
+
+import static gregtech.api.util.GT_Utility.trans;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.MethodsReturnNonnullByDefault;
+import gregtech.nei.RecipeDisplayInfo;
+
+@ParametersAreNonnullByDefault
+@MethodsReturnNonnullByDefault
+public class EUNoOverclockDescriber extends OverclockDescriber {
+
+ /**
+ * Amperage of the recipemap.
+ */
+ protected final int amperage;
+
+ public EUNoOverclockDescriber(byte tier, int amperage) {
+ super(tier);
+ if (amperage < 1) {
+ throw new IllegalArgumentException("Amperage cannot be lower than 1");
+ }
+ this.amperage = amperage;
+ }
+
+ @Override
+ public GT_OverclockCalculator createCalculator(GT_OverclockCalculator template, GT_Recipe recipe) {
+ return GT_OverclockCalculator.ofNoOverclock(recipe);
+ }
+
+ @Override
+ public String getTierString() {
+ return GT_Utility.getColoredTierNameFromTier(tier);
+ }
+
+ @Override
+ public final void drawEnergyInfo(RecipeDisplayInfo recipeInfo) {
+ if (recipeInfo.calculator.getDuration() > 0 && recipeInfo.calculator.getConsumption() > 0) {
+ recipeInfo.drawText(trans("152", "Total: ") + getTotalPowerString(recipeInfo.calculator));
+ }
+ drawEnergyInfoImpl(recipeInfo);
+ }
+
+ /**
+ * Override this to draw custom info about the energy this object can handle on NEI recipe GUI, minus total
+ * power usage.
+ */
+ protected void drawEnergyInfoImpl(RecipeDisplayInfo recipeInfo) {
+ if (recipeInfo.calculator.getConsumption() <= 0) {
+ return;
+ }
+ recipeInfo.drawText(trans("153", "Usage: ") + getEUtDisplay(recipeInfo.calculator));
+ if (shouldShowAmperage(recipeInfo.calculator)) {
+ recipeInfo.drawText(trans("154", "Voltage: ") + getVoltageString(recipeInfo.calculator));
+ recipeInfo.drawText(trans("155", "Amperage: ") + getAmperageString(recipeInfo.calculator));
+ }
+ }
+
+ protected String getTotalPowerString(GT_OverclockCalculator calculator) {
+ return GT_Utility.formatNumbers(calculator.getConsumption() * calculator.getDuration()) + " EU";
+ }
+
+ /**
+ * @return If amperage should be shown on NEI.
+ */
+ protected boolean shouldShowAmperage(GT_OverclockCalculator calculator) {
+ return amperage != 1;
+ }
+
+ /**
+ * @return Whole EU/t usage, without tier display.
+ */
+ protected String getEUtWithoutTier(GT_OverclockCalculator calculator) {
+ return GT_Utility.formatNumbers(calculator.getConsumption()) + " EU/t";
+ }
+
+ /**
+ * @return Whole EU/t usage, with tier display.
+ */
+ protected String getEUtWithTier(GT_OverclockCalculator calculator) {
+ return getEUtWithoutTier(calculator) + GT_Utility.getTierNameWithParentheses(calculator.getConsumption());
+ }
+
+ /**
+ * @return Whole EU/t usage. Also displays voltage tier if it should be shown.
+ */
+ protected String getEUtDisplay(GT_OverclockCalculator calculator) {
+ return shouldShowAmperage(calculator) ? getEUtWithoutTier(calculator) : getEUtWithTier(calculator);
+ }
+
+ /**
+ * @return EU/t usage, divided by amperage. With tier display.
+ */
+ protected String getVoltageString(GT_OverclockCalculator calculator) {
+ long voltage = computeVoltageForEURate(calculator.getConsumption());
+ return GT_Utility.formatNumbers(voltage) + " EU/t" + GT_Utility.getTierNameWithParentheses(voltage);
+ }
+
+ protected String getAmperageString(GT_OverclockCalculator calculator) {
+ return GT_Utility.formatNumbers(amperage);
+ }
+
+ protected long computeVoltageForEURate(long euPerTick) {
+ return euPerTick / amperage;
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/overclockdescriber/EUOverclockDescriber.java b/src/main/java/gregtech/api/objects/overclockdescriber/EUOverclockDescriber.java
new file mode 100644
index 0000000000..9d53711515
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/overclockdescriber/EUOverclockDescriber.java
@@ -0,0 +1,80 @@
+package gregtech.api.objects.overclockdescriber;
+
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.util.GT_Utility.trans;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+import com.google.common.primitives.Ints;
+
+import gregtech.GT_Mod;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.MethodsReturnNonnullByDefault;
+import gregtech.nei.RecipeDisplayInfo;
+
+@ParametersAreNonnullByDefault
+@MethodsReturnNonnullByDefault
+public class EUOverclockDescriber extends EUNoOverclockDescriber {
+
+ public EUOverclockDescriber(byte tier, int amperage) {
+ super(tier, amperage);
+ }
+
+ @Override
+ public GT_OverclockCalculator createCalculator(GT_OverclockCalculator template, GT_Recipe recipe) {
+ return template.setEUt(Ints.saturatedCast(V[tier] * amperage));
+ }
+
+ @Override
+ protected void drawEnergyInfoImpl(RecipeDisplayInfo recipeInfo) {
+ if (!wasOverclocked(recipeInfo.calculator)) {
+ super.drawEnergyInfoImpl(recipeInfo);
+ return;
+ }
+
+ recipeInfo.drawText(trans("153", "Usage: ") + getEUtDisplay(recipeInfo.calculator));
+ if (shouldShowAmperage(recipeInfo.calculator)) {
+ recipeInfo.drawText(trans("154", "Voltage: ") + getVoltageString(recipeInfo.calculator));
+ }
+ if (GT_Mod.gregtechproxy.mNEIOriginalVoltage) {
+ EUNoOverclockDescriber originalPower = new EUNoOverclockDescriber(tier, amperage);
+ GT_OverclockCalculator originalPowerCalculator = GT_OverclockCalculator.ofNoOverclock(recipeInfo.recipe)
+ .calculate();
+ recipeInfo
+ .drawText(trans("275", "Original usage: ") + originalPower.getEUtDisplay(originalPowerCalculator));
+ }
+ if (shouldShowAmperage(recipeInfo.calculator)) {
+ recipeInfo.drawText(trans("155", "Amperage: ") + getAmperageString(recipeInfo.calculator));
+ }
+ }
+
+ @Override
+ protected String getEUtWithoutTier(GT_OverclockCalculator calculator) {
+ return decorateWithOverclockLabel(super.getEUtWithoutTier(calculator), calculator);
+ }
+
+ @Override
+ protected String getEUtWithTier(GT_OverclockCalculator calculator) {
+ return this.getEUtWithoutTier(calculator) + GT_Utility.getTierNameWithParentheses(calculator.getConsumption());
+ }
+
+ @Override
+ protected String getVoltageString(GT_OverclockCalculator calculator) {
+ long voltage = computeVoltageForEURate(calculator.getConsumption());
+ return decorateWithOverclockLabel(GT_Utility.formatNumbers(voltage) + " EU/t", calculator)
+ + GT_Utility.getTierNameWithParentheses(voltage);
+ }
+
+ protected String decorateWithOverclockLabel(String s, GT_OverclockCalculator calculator) {
+ if (wasOverclocked(calculator)) {
+ s += " (OC)";
+ }
+ return s;
+ }
+
+ protected boolean wasOverclocked(GT_OverclockCalculator calculator) {
+ return calculator.getPerformedOverclocks() > 0;
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/overclockdescriber/FusionOverclockDescriber.java b/src/main/java/gregtech/api/objects/overclockdescriber/FusionOverclockDescriber.java
new file mode 100644
index 0000000000..8f64f3146e
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/overclockdescriber/FusionOverclockDescriber.java
@@ -0,0 +1,63 @@
+package gregtech.api.objects.overclockdescriber;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+import net.minecraft.util.EnumChatFormatting;
+
+import gregtech.api.enums.GT_Values;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.MethodsReturnNonnullByDefault;
+import gregtech.nei.formatter.FusionSpecialValueFormatter;
+
+@ParametersAreNonnullByDefault
+@MethodsReturnNonnullByDefault
+public class FusionOverclockDescriber extends EUOverclockDescriber {
+
+ protected final long capableStartup;
+
+ public FusionOverclockDescriber(byte energyTier, long capableStartup) {
+ super(energyTier, 1);
+ this.capableStartup = capableStartup;
+ }
+
+ @Override
+ public GT_OverclockCalculator createCalculator(GT_OverclockCalculator template, GT_Recipe recipe) {
+ return super.createCalculator(template, recipe)
+ .limitOverclockCount(overclock(recipe.mSpecialValue, recipe.mEUt))
+ .setEUtIncreasePerOC(getEUtIncreasePerOC())
+ .setDurationDecreasePerOC(getDurationDecreasePerOC());
+ }
+
+ protected int getEUtIncreasePerOC() {
+ return 1;
+ }
+
+ protected int getDurationDecreasePerOC() {
+ return 1;
+ }
+
+ @Override
+ public String getTierString() {
+ return GT_Values.TIER_COLORS[tier] + "MK " + getFusionTier() + EnumChatFormatting.RESET;
+ }
+
+ @Override
+ public boolean canHandle(GT_Recipe recipe) {
+ byte tier = GT_Utility.getTier(recipe.mEUt);
+ if (this.tier < tier) {
+ return false;
+ }
+ return this.capableStartup >= recipe.mSpecialValue;
+ }
+
+ protected int overclock(long startEnergy, long voltage) {
+ // Fusion Computer tier - recipe tier
+ return Math.max(getFusionTier() - FusionSpecialValueFormatter.getFusionTier(startEnergy, voltage), 0);
+ }
+
+ protected int getFusionTier() {
+ return this.tier - 5; // Mk1 <-> LuV
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/overclockdescriber/OverclockDescriber.java b/src/main/java/gregtech/api/objects/overclockdescriber/OverclockDescriber.java
new file mode 100644
index 0000000000..0b253c95fa
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/overclockdescriber/OverclockDescriber.java
@@ -0,0 +1,106 @@
+package gregtech.api.objects.overclockdescriber;
+
+import static gregtech.api.util.GT_Utility.trans;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+import gregtech.GT_Mod;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.MethodsReturnNonnullByDefault;
+import gregtech.nei.RecipeDisplayInfo;
+
+/**
+ * Provides an overclock behavior that will run on machines with the ability to draw information about it on NEI.
+ * <p>
+ * Implement {@link gregtech.api.interfaces.tileentity.IOverclockDescriptionProvider} for corresponding machine to use
+ * derivative of this class when looking up NEI recipe catalyst.
+ */
+@ParametersAreNonnullByDefault
+@MethodsReturnNonnullByDefault
+public abstract class OverclockDescriber {
+
+ /**
+ * Tier of the (maybe virtual) machine this object belongs to.
+ */
+ protected final byte tier;
+
+ public OverclockDescriber(byte tier) {
+ this.tier = tier;
+ }
+
+ /**
+ * @return Tier of this object. Used to limit recipes shown on NEI, based on recipe EU/t.
+ */
+ public final byte getTier() {
+ return tier;
+ }
+
+ /**
+ * @return Tier display of this object, shown on NEI header in a form of {@code Machine Name (tier)}
+ */
+ public abstract String getTierString();
+
+ /**
+ * Creates overclock calculator from given template. This template should be used instead of building from the
+ * ground to avoid issues coming from different caller using different templates, but it's not applicable when using
+ * {@link GT_OverclockCalculator#ofNoOverclock(GT_Recipe)}.
+ *
+ * @param template Calculator that can be used as template. Recipe EU/t and duration are already set.
+ * @param recipe Recipe to calculate.
+ */
+ public abstract GT_OverclockCalculator createCalculator(GT_OverclockCalculator template, GT_Recipe recipe);
+
+ /**
+ * Draws info about the energy this object can handle on NEI recipe GUI.
+ */
+ public abstract void drawEnergyInfo(RecipeDisplayInfo recipeInfo);
+
+ public void drawDurationInfo(RecipeDisplayInfo recipeInfo) {
+ if (getDurationTicks(recipeInfo.calculator) <= 0) return;
+
+ String textToDraw = trans("158", "Time: ");
+ if (GT_Mod.gregtechproxy.mNEIRecipeSecondMode) {
+ textToDraw += getDurationStringSeconds(recipeInfo.calculator);
+ if (getDurationSeconds(recipeInfo.calculator) <= 1.0d) {
+ textToDraw += String.format(" (%s)", getDurationStringTicks(recipeInfo.calculator));
+ }
+ } else {
+ textToDraw += getDurationStringTicks(recipeInfo.calculator);
+ }
+ recipeInfo.drawText(textToDraw);
+ }
+
+ /**
+ * Used to limit the shown recipes when searching recipes with NEI recipe catalyst. Unless overridden, this method
+ * doesn't do anything special (except for a bit worse performance).
+ * <p>
+ * In order to make use of this method, {@link gregtech.api.recipe.RecipeMapBuilder#useCustomFilterForNEI}
+ * should be enabled for the recipemap.
+ *
+ * @return If this object can handle the supplied recipe
+ */
+ public boolean canHandle(GT_Recipe recipe) {
+ byte tier = GT_Utility.getTier(recipe.mEUt);
+ return this.tier >= tier;
+ }
+
+ private int getDurationTicks(GT_OverclockCalculator calculator) {
+ return calculator.getDuration();
+ }
+
+ private double getDurationSeconds(GT_OverclockCalculator calculator) {
+ return 0.05d * getDurationTicks(calculator);
+ }
+
+ private String getDurationStringSeconds(GT_OverclockCalculator calculator) {
+ return GT_Utility.formatNumbers(getDurationSeconds(calculator)) + GT_Utility.trans("161", " secs");
+ }
+
+ private String getDurationStringTicks(GT_OverclockCalculator calculator) {
+ String ticksString = getDurationTicks(calculator) == 1 ? GT_Utility.trans("209.1", " tick")
+ : GT_Utility.trans("209", " ticks");
+ return GT_Utility.formatNumbers(getDurationTicks(calculator)) + ticksString;
+ }
+}
diff --git a/src/main/java/gregtech/api/objects/overclockdescriber/SteamOverclockDescriber.java b/src/main/java/gregtech/api/objects/overclockdescriber/SteamOverclockDescriber.java
new file mode 100644
index 0000000000..5da64d4028
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/overclockdescriber/SteamOverclockDescriber.java
@@ -0,0 +1,64 @@
+package gregtech.api.objects.overclockdescriber;
+
+import static gregtech.api.util.GT_Utility.trans;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+import net.minecraft.util.StatCollector;
+
+import gregtech.api.enums.SteamVariant;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.MethodsReturnNonnullByDefault;
+import gregtech.nei.RecipeDisplayInfo;
+
+@ParametersAreNonnullByDefault
+@MethodsReturnNonnullByDefault
+public class SteamOverclockDescriber extends OverclockDescriber {
+
+ private final SteamVariant steamVariant;
+ private final int euPerTickMultiplier;
+ private final int durationMultiplier;
+
+ public SteamOverclockDescriber(SteamVariant steamVariant, int euPerTickMultiplier, int durationMultiplier) {
+ super((byte) 1); // recipe tier is always LV
+ this.steamVariant = steamVariant;
+ this.euPerTickMultiplier = euPerTickMultiplier;
+ this.durationMultiplier = durationMultiplier;
+ }
+
+ @Override
+ public String getTierString() {
+ return StatCollector.translateToLocal("GT5U.steam_variant." + steamVariant.toString());
+ }
+
+ @Override
+ public GT_OverclockCalculator createCalculator(GT_OverclockCalculator template, GT_Recipe recipe) {
+ return GT_OverclockCalculator.ofNoOverclock(recipe)
+ .setEUtDiscount(euPerTickMultiplier)
+ .setSpeedBoost(durationMultiplier);
+ }
+
+ @Override
+ public void drawEnergyInfo(RecipeDisplayInfo recipeInfo) {
+ if (recipeInfo.calculator.getConsumption() <= 0) return;
+
+ recipeInfo.drawText(trans("152", "Total: ") + getTotalPowerString(recipeInfo.calculator));
+ recipeInfo.drawText(trans("153", "Usage: ") + getSteamUsageString(recipeInfo.calculator));
+ }
+
+ private String getTotalPowerString(GT_OverclockCalculator calculator) {
+ return GT_Utility.formatNumbers(convertEUToSteam(calculator.getConsumption() * calculator.getDuration()))
+ + " Steam";
+ }
+
+ private String getSteamUsageString(GT_OverclockCalculator calculator) {
+ return GT_Utility.formatNumbers(20 * convertEUToSteam(calculator.getConsumption())) + " L/s Steam";
+ }
+
+ private static long convertEUToSteam(long eu) {
+ // 2L normal steam == 1EU
+ return 2 * eu;
+ }
+}