diff options
Diffstat (limited to 'src/main/java/gregtech/api/objects')
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; + } +} |