aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/util/GTUtility.java
diff options
context:
space:
mode:
authorNotAPenguin <michiel.vandeginste@gmail.com>2024-09-02 23:17:17 +0200
committerGitHub <noreply@github.com>2024-09-02 23:17:17 +0200
commit1b820de08a05070909a267e17f033fcf58ac8710 (patch)
tree02831a025986a06b20f87e5bcc69d1e0c639a342 /src/main/java/gregtech/api/util/GTUtility.java
parentafd3fd92b6a6ab9ab0d0dc3214e6bc8ff7a86c9b (diff)
downloadGT5-Unofficial-1b820de08a05070909a267e17f033fcf58ac8710.tar.gz
GT5-Unofficial-1b820de08a05070909a267e17f033fcf58ac8710.tar.bz2
GT5-Unofficial-1b820de08a05070909a267e17f033fcf58ac8710.zip
The Great Renaming (#3014)
* move kekztech to a single root dir * move detrav to a single root dir * move gtnh-lanthanides to a single root dir * move tectech and delete some gross reflection in gt++ * remove more reflection inside gt5u * delete more reflection in gt++ * fix imports * move bartworks and bwcrossmod * fix proxies * move galactigreg and ggfab * move gtneioreplugin * try to fix gt++ bee loader * apply the rename rules to BW * apply rename rules to bwcrossmod * apply rename rules to detrav scanner mod * apply rename rules to galacticgreg * apply rename rules to ggfab * apply rename rules to goodgenerator * apply rename rules to gtnh-lanthanides * apply rename rules to gt++ * apply rename rules to kekztech * apply rename rules to kubatech * apply rename rules to tectech * apply rename rules to gt apply the rename rules to gt * fix tt import * fix mui hopefully * fix coremod except intergalactic * rename assline recipe class * fix a class name i stumbled on * rename StructureUtility to GTStructureUtility to prevent conflict with structurelib * temporary rename of GTTooltipDataCache to old name * fix gt client/server proxy names
Diffstat (limited to 'src/main/java/gregtech/api/util/GTUtility.java')
-rw-r--r--src/main/java/gregtech/api/util/GTUtility.java4852
1 files changed, 4852 insertions, 0 deletions
diff --git a/src/main/java/gregtech/api/util/GTUtility.java b/src/main/java/gregtech/api/util/GTUtility.java
new file mode 100644
index 0000000000..08a1711b97
--- /dev/null
+++ b/src/main/java/gregtech/api/util/GTUtility.java
@@ -0,0 +1,4852 @@
+package gregtech.api.util;
+
+import static gregtech.GTMod.GT_FML_LOGGER;
+import static gregtech.api.enums.GTValues.COMPASS_DIRECTIONS;
+import static gregtech.api.enums.GTValues.D1;
+import static gregtech.api.enums.GTValues.E;
+import static gregtech.api.enums.GTValues.GT;
+import static gregtech.api.enums.GTValues.L;
+import static gregtech.api.enums.GTValues.M;
+import static gregtech.api.enums.GTValues.NW;
+import static gregtech.api.enums.GTValues.V;
+import static gregtech.api.enums.GTValues.W;
+import static gregtech.api.enums.Materials.FLUID_MAP;
+import static gregtech.api.enums.Mods.Translocator;
+import static gregtech.common.UndergroundOil.undergroundOilReadInformation;
+import static net.minecraftforge.common.util.ForgeDirection.DOWN;
+import static net.minecraftforge.common.util.ForgeDirection.EAST;
+import static net.minecraftforge.common.util.ForgeDirection.NORTH;
+import static net.minecraftforge.common.util.ForgeDirection.SOUTH;
+import static net.minecraftforge.common.util.ForgeDirection.UNKNOWN;
+import static net.minecraftforge.common.util.ForgeDirection.UP;
+import static net.minecraftforge.common.util.ForgeDirection.WEST;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.AbstractCollection;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Function;
+import java.util.function.IntFunction;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.minecraft.block.Block;
+import net.minecraft.client.Minecraft;
+import net.minecraft.enchantment.Enchantment;
+import net.minecraft.enchantment.EnchantmentHelper;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityList;
+import net.minecraft.entity.EntityLiving;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.EnumCreatureAttribute;
+import net.minecraft.entity.item.EntityItem;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.init.Blocks;
+import net.minecraft.init.Items;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.ISidedInventory;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTBase;
+import net.minecraft.nbt.NBTBase.NBTPrimitive;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.nbt.NBTTagString;
+import net.minecraft.network.play.server.S07PacketRespawn;
+import net.minecraft.network.play.server.S1DPacketEntityEffect;
+import net.minecraft.network.play.server.S1FPacketSetExperience;
+import net.minecraft.potion.Potion;
+import net.minecraft.potion.PotionEffect;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.tileentity.TileEntityChest;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.util.ChatComponentText;
+import net.minecraft.util.DamageSource;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.MathHelper;
+import net.minecraft.util.MovingObjectPosition;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.Vec3;
+import net.minecraft.world.World;
+import net.minecraft.world.WorldServer;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraftforge.common.DimensionManager;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.common.util.BlockSnapshot;
+import net.minecraftforge.common.util.Constants;
+import net.minecraftforge.common.util.FakePlayer;
+import net.minecraftforge.common.util.FakePlayerFactory;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.event.ForgeEventFactory;
+import net.minecraftforge.event.world.BlockEvent;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidContainerRegistry.FluidContainerData;
+import net.minecraftforge.fluids.FluidRegistry;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+import net.minecraftforge.fluids.IFluidContainerItem;
+import net.minecraftforge.fluids.IFluidHandler;
+import net.minecraftforge.oredict.OreDictionary;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.SetMultimap;
+import com.gtnewhorizon.structurelib.alignment.IAlignment;
+import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
+import com.mojang.authlib.GameProfile;
+
+import buildcraft.api.transport.IPipeTile;
+import cofh.api.energy.IEnergyReceiver;
+import cofh.api.transport.IItemDuct;
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.common.registry.GameRegistry;
+import gregtech.api.GregTechAPI;
+import gregtech.api.damagesources.GTDamageSources;
+import gregtech.api.damagesources.GTDamageSources.DamageSourceHotItem;
+import gregtech.api.enchants.EnchantmentHazmat;
+import gregtech.api.enchants.EnchantmentRadioactivity;
+import gregtech.api.enums.GTValues;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.Mods;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.SubTag;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.ToolDictNames;
+import gregtech.api.events.BlockScanningEvent;
+import gregtech.api.interfaces.IBlockContainer;
+import gregtech.api.interfaces.IDebugableBlock;
+import gregtech.api.interfaces.IHasIndexedTexture;
+import gregtech.api.interfaces.IProjectileItem;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IBasicEnergyContainer;
+import gregtech.api.interfaces.tileentity.ICoverable;
+import gregtech.api.interfaces.tileentity.IGregTechDeviceInformation;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.interfaces.tileentity.IMachineProgress;
+import gregtech.api.interfaces.tileentity.IUpgradableMachine;
+import gregtech.api.items.GTGenericItem;
+import gregtech.api.items.ItemEnergyArmor;
+import gregtech.api.items.MetaGeneratedTool;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.net.GTPacketSound;
+import gregtech.api.objects.CollectorUtils;
+import gregtech.api.objects.GTItemStack;
+import gregtech.api.objects.GTItemStack2;
+import gregtech.api.objects.ItemData;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.threads.RunnableSound;
+import gregtech.api.util.extensions.ArrayExt;
+import gregtech.common.Pollution;
+import gregtech.common.blocks.BlockOresAbstract;
+import ic2.api.recipe.IRecipeInput;
+import ic2.api.recipe.RecipeInputItemStack;
+import ic2.api.recipe.RecipeInputOreDict;
+import ic2.api.recipe.RecipeOutput;
+import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap;
+
+/**
+ * NEVER INCLUDE THIS FILE IN YOUR MOD!!!
+ * <p/>
+ * Just a few Utility Functions I use.
+ */
+public class GTUtility {
+
+ /**
+ * Formats a number with group separator and at most 2 fraction digits.
+ */
+ private static final Map<Locale, DecimalFormat> decimalFormatters = new HashMap<>();
+
+ /**
+ * Forge screwed the Fluid Registry up again, so I make my own, which is also much more efficient than the stupid
+ * Stuff over there.
+ */
+ private static final List<FluidContainerData> sFluidContainerList = new ArrayList<>();
+
+ private static final Map<GTItemStack, FluidContainerData> sFilledContainerToData = new /* Concurrent */ HashMap<>();
+ private static final Map<GTItemStack, Map<String, FluidContainerData>> sEmptyContainerToFluidToData = new HashMap<>();
+ private static final Map<String, List<ItemStack>> sFluidToContainers = new HashMap<>();
+ /**
+ * Must use {@code Supplier} here because the ore prefixes have not yet been registered at class load time.
+ */
+ private static final Map<OrePrefixes, Supplier<ItemStack>> sOreToCobble = new HashMap<>();
+
+ private static final Map<Integer, Boolean> sOreTable = new HashMap<>();
+ public static boolean TE_CHECK = false, BC_CHECK = false, CHECK_ALL = true, RF_CHECK = false;
+ public static Map<GTPlayedSound, Integer> sPlayedSoundMap = new /* Concurrent */ HashMap<>();
+ private static int sBookCount = 0;
+ public static UUID defaultUuid = null; // maybe default non-null?
+ // UUID.fromString("00000000-0000-0000-0000-000000000000");
+
+ static {
+ GregTechAPI.sItemStackMappings.add(sFilledContainerToData);
+ GregTechAPI.sItemStackMappings.add(sEmptyContainerToFluidToData);
+
+ // 1 is the magic index to get the cobblestone block.
+ // See: GT_Block_Stones.java, GT_Block_Granites.java
+ Function<Materials, Supplier<ItemStack>> materialToCobble = m -> Suppliers.memoize(
+ () -> GTOreDictUnificator.getOres(OrePrefixes.stone, m)
+ .get(1))::get;
+ sOreToCobble.put(OrePrefixes.oreBlackgranite, materialToCobble.apply(Materials.GraniteBlack));
+ sOreToCobble.put(OrePrefixes.oreRedgranite, materialToCobble.apply(Materials.GraniteRed));
+ sOreToCobble.put(OrePrefixes.oreMarble, materialToCobble.apply(Materials.Marble));
+ sOreToCobble.put(OrePrefixes.oreBasalt, materialToCobble.apply(Materials.Basalt));
+ sOreToCobble.put(OrePrefixes.oreNetherrack, () -> new ItemStack(Blocks.netherrack));
+ sOreToCobble.put(OrePrefixes.oreEndstone, () -> new ItemStack(Blocks.end_stone));
+ }
+
+ public static int safeInt(long number, int margin) {
+ return number > Integer.MAX_VALUE - margin ? Integer.MAX_VALUE - margin : (int) number;
+ }
+
+ public static int safeInt(long number) {
+ return number > V[V.length - 1] ? safeInt(V[V.length - 1], 1)
+ : number < Integer.MIN_VALUE ? Integer.MIN_VALUE : (int) number;
+ }
+
+ public static Field getPublicField(Object aObject, String aField) {
+ Field rField = null;
+ try {
+ rField = aObject.getClass()
+ .getDeclaredField(aField);
+ } catch (Throwable e) {
+ /* Do nothing */
+ }
+ return rField;
+ }
+
+ public static Field getField(Object aObject, String aField) {
+ Field rField = null;
+ try {
+ rField = aObject.getClass()
+ .getDeclaredField(aField);
+ rField.setAccessible(true);
+ } catch (Throwable e) {
+ /* Do nothing */
+ }
+ return rField;
+ }
+
+ public static Field getField(Class<?> aObject, String aField) {
+ Field rField = null;
+ try {
+ rField = aObject.getDeclaredField(aField);
+ rField.setAccessible(true);
+ } catch (Throwable e) {
+ /* Do nothing */
+ }
+ return rField;
+ }
+
+ public static Method getMethod(Class<?> aObject, String aMethod, Class<?>... aParameterTypes) {
+ Method rMethod = null;
+ try {
+ rMethod = aObject.getMethod(aMethod, aParameterTypes);
+ rMethod.setAccessible(true);
+ } catch (Throwable e) {
+ /* Do nothing */
+ }
+ return rMethod;
+ }
+
+ public static Method getMethod(Object aObject, String aMethod, Class<?>... aParameterTypes) {
+ Method rMethod = null;
+ try {
+ rMethod = aObject.getClass()
+ .getMethod(aMethod, aParameterTypes);
+ rMethod.setAccessible(true);
+ } catch (Throwable e) {
+ /* Do nothing */
+ }
+ return rMethod;
+ }
+
+ public static Field getField(Object aObject, String aField, boolean aPrivate, boolean aLogErrors) {
+ try {
+ Field tField = (aObject instanceof Class) ? ((Class<?>) aObject).getDeclaredField(aField)
+ : (aObject instanceof String) ? Class.forName((String) aObject)
+ .getDeclaredField(aField)
+ : aObject.getClass()
+ .getDeclaredField(aField);
+ if (aPrivate) tField.setAccessible(true);
+ return tField;
+ } catch (Throwable e) {
+ if (aLogErrors) e.printStackTrace(GTLog.err);
+ }
+ return null;
+ }
+
+ public static Object getFieldContent(Object aObject, String aField, boolean aPrivate, boolean aLogErrors) {
+ try {
+ Field tField = (aObject instanceof Class) ? ((Class<?>) aObject).getDeclaredField(aField)
+ : (aObject instanceof String) ? Class.forName((String) aObject)
+ .getDeclaredField(aField)
+ : aObject.getClass()
+ .getDeclaredField(aField);
+ if (aPrivate) tField.setAccessible(true);
+ return tField.get(aObject instanceof Class || aObject instanceof String ? null : aObject);
+ } catch (Throwable e) {
+ if (aLogErrors) e.printStackTrace(GTLog.err);
+ }
+ return null;
+ }
+
+ public static Object callPublicMethod(Object aObject, String aMethod, Object... aParameters) {
+ return callMethod(aObject, aMethod, false, false, true, aParameters);
+ }
+
+ public static Object callPrivateMethod(Object aObject, String aMethod, Object... aParameters) {
+ return callMethod(aObject, aMethod, true, false, true, aParameters);
+ }
+
+ public static Object callMethod(Object aObject, String aMethod, boolean aPrivate, boolean aUseUpperCasedDataTypes,
+ boolean aLogErrors, Object... aParameters) {
+ try {
+ Class<?>[] tParameterTypes = new Class<?>[aParameters.length];
+ for (byte i = 0; i < aParameters.length; i++) {
+ if (aParameters[i] instanceof Class) {
+ tParameterTypes[i] = (Class<?>) aParameters[i];
+ aParameters[i] = null;
+ } else {
+ tParameterTypes[i] = aParameters[i].getClass();
+ }
+ if (!aUseUpperCasedDataTypes) {
+ if (tParameterTypes[i] == Boolean.class) tParameterTypes[i] = boolean.class;
+ else if (tParameterTypes[i] == Byte.class) tParameterTypes[i] = byte.class;
+ else if (tParameterTypes[i] == Short.class) tParameterTypes[i] = short.class;
+ else if (tParameterTypes[i] == Integer.class) tParameterTypes[i] = int.class;
+ else if (tParameterTypes[i] == Long.class) tParameterTypes[i] = long.class;
+ else if (tParameterTypes[i] == Float.class) tParameterTypes[i] = float.class;
+ else if (tParameterTypes[i] == Double.class) tParameterTypes[i] = double.class;
+ }
+ }
+
+ Method tMethod = (aObject instanceof Class) ? ((Class<?>) aObject).getMethod(aMethod, tParameterTypes)
+ : aObject.getClass()
+ .getMethod(aMethod, tParameterTypes);
+ if (aPrivate) tMethod.setAccessible(true);
+ return tMethod.invoke(aObject, aParameters);
+ } catch (Throwable e) {
+ if (aLogErrors) e.printStackTrace(GTLog.err);
+ }
+ return null;
+ }
+
+ public static Object callConstructor(String aClass, int aConstructorIndex, Object aReplacementObject,
+ boolean aLogErrors, Object... aParameters) {
+ try {
+ return callConstructor(
+ Class.forName(aClass),
+ aConstructorIndex,
+ aReplacementObject,
+ aLogErrors,
+ aParameters);
+ } catch (Throwable e) {
+ if (aLogErrors) e.printStackTrace(GTLog.err);
+ }
+ return aReplacementObject;
+ }
+
+ public static Object callConstructor(Class<?> aClass, int aConstructorIndex, Object aReplacementObject,
+ boolean aLogErrors, Object... aParameters) {
+ if (aConstructorIndex < 0) {
+ try {
+ for (Constructor<?> tConstructor : aClass.getConstructors()) {
+ try {
+ return tConstructor.newInstance(aParameters);
+ } catch (Throwable ignored) {}
+ }
+ } catch (Throwable e) {
+ if (aLogErrors) e.printStackTrace(GTLog.err);
+ }
+ } else {
+ try {
+ return aClass.getConstructors()[aConstructorIndex].newInstance(aParameters);
+ } catch (Throwable e) {
+ if (aLogErrors) e.printStackTrace(GTLog.err);
+ }
+ }
+ return aReplacementObject;
+ }
+
+ public static String capitalizeString(String aString) {
+ if (aString != null && aString.length() > 0) return aString.substring(0, 1)
+ .toUpperCase() + aString.substring(1);
+ return E;
+ }
+
+ public static boolean getPotion(EntityLivingBase aPlayer, int aPotionIndex) {
+ try {
+ Field tPotionHashmap = null;
+
+ Field[] fields = EntityLiving.class.getDeclaredFields();
+
+ for (Field field : fields) {
+ if (field.getType() == HashMap.class) {
+ tPotionHashmap = field;
+ tPotionHashmap.setAccessible(true);
+ break;
+ }
+ }
+
+ if (tPotionHashmap != null) return ((HashMap<?, ?>) tPotionHashmap.get(aPlayer)).get(aPotionIndex) != null;
+ } catch (Throwable e) {
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ return false;
+ }
+
+ public static String getClassName(Object aObject) {
+ if (aObject == null) return "null";
+ return aObject.getClass()
+ .getName()
+ .substring(
+ aObject.getClass()
+ .getName()
+ .lastIndexOf(".") + 1);
+ }
+
+ public static void removePotion(EntityLivingBase aPlayer, int aPotionIndex) {
+ try {
+ Field tPotionHashmap = null;
+
+ Field[] fields = EntityLiving.class.getDeclaredFields();
+
+ for (Field field : fields) {
+ if (field.getType() == HashMap.class) {
+ tPotionHashmap = field;
+ tPotionHashmap.setAccessible(true);
+ break;
+ }
+ }
+
+ if (tPotionHashmap != null) ((HashMap<?, ?>) tPotionHashmap.get(aPlayer)).remove(aPotionIndex);
+ } catch (Throwable e) {
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ }
+
+ public static boolean getFullInvisibility(EntityPlayer aPlayer) {
+ try {
+ if (aPlayer.isInvisible()) {
+ for (int i = 0; i < 4; i++) {
+ if (aPlayer.inventory.armorInventory[i] != null) {
+ if (aPlayer.inventory.armorInventory[i].getItem() instanceof ItemEnergyArmor) {
+ if ((((ItemEnergyArmor) aPlayer.inventory.armorInventory[i].getItem()).mSpecials & 512)
+ != 0) {
+ if (GTModHandler.canUseElectricItem(aPlayer.inventory.armorInventory[i], 10000)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (Throwable e) {
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ return false;
+ }
+
+ public static ItemStack suckOneItemStackAt(World aWorld, double aX, double aY, double aZ, double aL, double aH,
+ double aW) {
+ for (EntityItem tItem : aWorld.getEntitiesWithinAABB(
+ EntityItem.class,
+ AxisAlignedBB.getBoundingBox(aX, aY, aZ, aX + aL, aY + aH, aZ + aW))) {
+ if (!tItem.isDead) {
+ aWorld.removeEntity(tItem);
+ tItem.setDead();
+ return tItem.getEntityItem();
+ }
+ }
+ return null;
+ }
+
+ public static byte getOppositeSide(ForgeDirection side) {
+ return (byte) side.getOpposite()
+ .ordinal();
+ }
+
+ public static byte getTier(long l) {
+ byte i = -1;
+ while (++i < V.length) if (l <= V[i]) return i;
+ return (byte) (V.length - 1);
+ }
+
+ public static long getAmperageForTier(long voltage, byte tier) {
+ return ceilDiv(voltage, GTValues.V[tier]);
+ }
+
+ /**
+ * Rounds up partial voltage that exceeds tiered voltage, e.g. 4,096 -> 8,192(IV)
+ */
+ public static long roundUpVoltage(long voltage) {
+ if (voltage > V[V.length - 1]) {
+ return voltage;
+ }
+ return V[GTUtility.getTier(voltage)];
+ }
+
+ public static String getColoredTierNameFromVoltage(long voltage) {
+ return getColoredTierNameFromTier(getTier(voltage));
+ }
+
+ public static String getColoredTierNameFromTier(byte tier) {
+ return GTValues.TIER_COLORS[tier] + GTValues.VN[tier] + EnumChatFormatting.RESET;
+ }
+
+ /**
+ * @return e.g. {@code " (LV)"}
+ */
+ @Nonnull
+ public static String getTierNameWithParentheses(long voltage) {
+ byte tier = getTier(voltage);
+ if (tier < 0) {
+ return "";
+ } else if (tier == 0) {
+ return " (" + GTValues.VN[1] + ")";
+ } else if (tier >= GTValues.VN.length - 1) {
+ return " (MAX+)";
+ }
+ return " (" + GTValues.VN[tier] + ")";
+ }
+
+ public static void sendChatToPlayer(EntityPlayer aPlayer, String aChatMessage) {
+ if (aPlayer instanceof EntityPlayerMP && aChatMessage != null) {
+ aPlayer.addChatComponentMessage(new ChatComponentText(aChatMessage));
+ }
+ }
+
+ public static void checkAvailabilities() {
+ if (CHECK_ALL) {
+ try {
+ Class<IItemDuct> tClass = IItemDuct.class;
+ tClass.getCanonicalName();
+ TE_CHECK = true;
+ } catch (Throwable e) {
+ /**/
+ }
+ try {
+ Class<IPipeTile> tClass = buildcraft.api.transport.IPipeTile.class;
+ tClass.getCanonicalName();
+ BC_CHECK = true;
+ } catch (Throwable e) {
+ /**/
+ }
+ try {
+ Class<IEnergyReceiver> tClass = cofh.api.energy.IEnergyReceiver.class;
+ tClass.getCanonicalName();
+ RF_CHECK = true;
+ } catch (Throwable e) {
+ /**/
+ }
+ CHECK_ALL = false;
+ }
+ }
+
+ public static boolean isConnectableNonInventoryPipe(TileEntity tileEntity, ForgeDirection side) {
+ if (tileEntity == null) return false;
+ checkAvailabilities();
+ if (TE_CHECK && tileEntity instanceof IItemDuct) return true;
+ if (BC_CHECK && tileEntity instanceof buildcraft.api.transport.IPipeTile pipeTile)
+ return pipeTile.isPipeConnected(side);
+ return Translocator.isModLoaded() && tileEntity instanceof codechicken.translocator.TileItemTranslocator;
+ }
+
+ /**
+ * Moves Stack from Inv-Slot to Inv-Slot, without checking if its even allowed.
+ *
+ * @return the Amount of moved Items
+ */
+ public static byte moveStackIntoPipe(IInventory aTileEntity1, Object aTileEntity2, int[] aGrabSlots,
+ ForgeDirection fromSide, ForgeDirection putSide, List<ItemStack> aFilter, boolean aInvertFilter,
+ byte aMaxTargetStackSize, byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce) {
+ return moveStackIntoPipe(
+ aTileEntity1,
+ aTileEntity2,
+ aGrabSlots,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ true);
+ }
+
+ /**
+ * Moves Stack from Inv-Slot to Inv-Slot, without checking if it is even allowed.
+ *
+ * @return the Amount of moved Items
+ */
+ public static byte moveStackIntoPipe(IInventory fromInventory, Object toObject, int[] fromSlots,
+ ForgeDirection fromSide, ForgeDirection putSide, List<ItemStack> aFilter, boolean aInvertFilter,
+ byte aMaxTargetStackSize, byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce,
+ boolean dropItem) {
+ if (fromInventory == null || aMaxTargetStackSize <= 0
+ || aMinTargetStackSize <= 0
+ || aMinTargetStackSize > aMaxTargetStackSize
+ || aMaxMoveAtOnce <= 0
+ || aMinMoveAtOnce > aMaxMoveAtOnce) return 0;
+ if (toObject != null) {
+ checkAvailabilities();
+ if (TE_CHECK && toObject instanceof IItemDuct itemDuct) {
+ for (final int aGrabSlot : fromSlots) {
+ if (listContainsItem(aFilter, fromInventory.getStackInSlot(aGrabSlot), true, aInvertFilter)) {
+ if (isAllowedToTakeFromSlot(
+ fromInventory,
+ aGrabSlot,
+ fromSide,
+ fromInventory.getStackInSlot(aGrabSlot))) {
+ if (Math.max(aMinMoveAtOnce, aMinTargetStackSize)
+ <= fromInventory.getStackInSlot(aGrabSlot).stackSize) {
+ ItemStack tStack = copyAmount(
+ Math.min(
+ fromInventory.getStackInSlot(aGrabSlot).stackSize,
+ Math.min(aMaxMoveAtOnce, aMaxTargetStackSize)),
+ fromInventory.getStackInSlot(aGrabSlot));
+ ItemStack rStack = itemDuct.insertItem(putSide, copyOrNull(tStack));
+ byte tMovedItemCount = (byte) (tStack.stackSize
+ - (rStack == null ? 0 : rStack.stackSize));
+ if (tMovedItemCount >= 1 /* Math.max(aMinMoveAtOnce, aMinTargetStackSize) */) {
+ fromInventory.decrStackSize(aGrabSlot, tMovedItemCount);
+ fromInventory.markDirty();
+ return tMovedItemCount;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+ }
+ if (BC_CHECK && toObject instanceof buildcraft.api.transport.IPipeTile bcPipe) {
+ for (int fromSlot : fromSlots) {
+ if (listContainsItem(aFilter, fromInventory.getStackInSlot(fromSlot), true, aInvertFilter)) {
+ if (isAllowedToTakeFromSlot(
+ fromInventory,
+ fromSlot,
+ fromSide,
+ fromInventory.getStackInSlot(fromSlot))) {
+ if (Math.max(aMinMoveAtOnce, aMinTargetStackSize)
+ <= fromInventory.getStackInSlot(fromSlot).stackSize) {
+ ItemStack tStack = copyAmount(
+ Math.min(
+ fromInventory.getStackInSlot(fromSlot).stackSize,
+ Math.min(aMaxMoveAtOnce, aMaxTargetStackSize)),
+ fromInventory.getStackInSlot(fromSlot));
+ byte tMovedItemCount = (byte) bcPipe.injectItem(copyOrNull(tStack), false, putSide);
+ if (tMovedItemCount >= Math.max(aMinMoveAtOnce, aMinTargetStackSize)) {
+ tMovedItemCount = (byte) (bcPipe
+ .injectItem(copyAmount(tMovedItemCount, tStack), true, putSide));
+ fromInventory.decrStackSize(fromSlot, tMovedItemCount);
+ fromInventory.markDirty();
+ return tMovedItemCount;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+ }
+ }
+
+ if (fromInventory instanceof TileEntity fromTileEntity && fromSide != ForgeDirection.UNKNOWN
+ && fromSide.getOpposite() == ForgeDirection.getOrientation(putSide.ordinal())) {
+ int tX = fromTileEntity.xCoord + fromSide.offsetX, tY = fromTileEntity.yCoord + fromSide.offsetY,
+ tZ = fromTileEntity.zCoord + fromSide.offsetZ;
+ if (!hasBlockHitBox(((TileEntity) fromInventory).getWorldObj(), tX, tY, tZ) && dropItem) {
+ for (final int fromSlot : fromSlots) {
+ if (listContainsItem(aFilter, fromInventory.getStackInSlot(fromSlot), true, aInvertFilter)) {
+ if (isAllowedToTakeFromSlot(
+ fromInventory,
+ fromSlot,
+ fromSide,
+ fromInventory.getStackInSlot(fromSlot))) {
+ if (Math.max(aMinMoveAtOnce, aMinTargetStackSize)
+ <= fromInventory.getStackInSlot(fromSlot).stackSize) {
+ final ItemStack tStack = copyAmount(
+ Math.min(
+ fromInventory.getStackInSlot(fromSlot).stackSize,
+ Math.min(aMaxMoveAtOnce, aMaxTargetStackSize)),
+ fromInventory.getStackInSlot(fromSlot));
+ final EntityItem tEntity = new EntityItem(
+ ((TileEntity) fromInventory).getWorldObj(),
+ tX + 0.5,
+ tY + 0.5,
+ tZ + 0.5,
+ tStack);
+ tEntity.motionX = tEntity.motionY = tEntity.motionZ = 0;
+ ((TileEntity) fromInventory).getWorldObj()
+ .spawnEntityInWorld(tEntity);
+ assert tStack != null;
+ fromInventory.decrStackSize(fromSlot, tStack.stackSize);
+ fromInventory.markDirty();
+ return (byte) tStack.stackSize;
+ }
+ }
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Moves Stack from Inv-Slot to Inv-Slot, without checking if its even allowed. (useful for internal Inventory
+ * Operations)
+ *
+ * @return the Amount of moved Items
+ */
+ public static byte moveStackFromSlotAToSlotB(IInventory aTileEntity1, IInventory aTileEntity2, int aGrabFrom,
+ int aPutTo, byte aMaxTargetStackSize, byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce) {
+ if (aTileEntity1 == null || aTileEntity2 == null
+ || aMinTargetStackSize <= 0
+ || aMinTargetStackSize > aMaxTargetStackSize
+ || aMaxMoveAtOnce <= 0
+ || aMinMoveAtOnce > aMaxMoveAtOnce) return 0;
+
+ ItemStack tStack1 = aTileEntity1.getStackInSlot(aGrabFrom), tStack2 = aTileEntity2.getStackInSlot(aPutTo),
+ tStack3;
+ if (tStack1 != null) {
+ if (tStack2 != null && !areStacksEqual(tStack1, tStack2)) return 0;
+ tStack3 = copyOrNull(tStack1);
+ aMaxTargetStackSize = (byte) Math.min(
+ aMaxTargetStackSize,
+ Math.min(
+ tStack3.getMaxStackSize(),
+ Math.min(
+ tStack2 == null ? Integer.MAX_VALUE : tStack2.getMaxStackSize(),
+ aTileEntity2.getInventoryStackLimit())));
+ tStack3.stackSize = Math
+ .min(tStack3.stackSize, aMaxTargetStackSize - (tStack2 == null ? 0 : tStack2.stackSize));
+ if (tStack3.stackSize > aMaxMoveAtOnce) tStack3.stackSize = aMaxMoveAtOnce;
+ if (tStack3.stackSize + (tStack2 == null ? 0 : tStack2.stackSize)
+ >= Math.min(tStack3.getMaxStackSize(), aMinTargetStackSize) && tStack3.stackSize >= aMinMoveAtOnce) {
+ tStack3 = aTileEntity1.decrStackSize(aGrabFrom, tStack3.stackSize);
+ aTileEntity1.markDirty();
+ if (tStack3 != null) {
+ if (tStack2 == null) {
+ aTileEntity2.setInventorySlotContents(aPutTo, copyOrNull(tStack3));
+ } else {
+ tStack2.stackSize += tStack3.stackSize;
+ }
+ aTileEntity2.markDirty();
+ return (byte) tStack3.stackSize;
+ }
+ }
+ }
+ return 0;
+ }
+
+ public static boolean isAllowedToTakeFromSlot(IInventory aTileEntity, int aSlot, ForgeDirection side,
+ ItemStack aStack) {
+ if (side == ForgeDirection.UNKNOWN) {
+ return Arrays.stream(ForgeDirection.VALID_DIRECTIONS)
+ .anyMatch(d -> isAllowedToTakeFromSlot(aTileEntity, aSlot, d, aStack));
+ }
+ if (aTileEntity instanceof ISidedInventory sided) return sided.canExtractItem(aSlot, aStack, side.ordinal());
+ return true;
+ }
+
+ public static boolean isAllowedToPutIntoSlot(IInventory aTileEntity, int aSlot, ForgeDirection side,
+ ItemStack aStack, byte aMaxStackSize) {
+ ItemStack tStack = aTileEntity.getStackInSlot(aSlot);
+ if (tStack != null && (!areStacksEqual(tStack, aStack) || tStack.stackSize >= tStack.getMaxStackSize()))
+ return false;
+ if (side == ForgeDirection.UNKNOWN) {
+ return Arrays.stream(ForgeDirection.VALID_DIRECTIONS)
+ .anyMatch(d -> isAllowedToPutIntoSlot(aTileEntity, aSlot, d, aStack, aMaxStackSize));
+ }
+ if (aTileEntity instanceof ISidedInventory
+ && !((ISidedInventory) aTileEntity).canInsertItem(aSlot, aStack, side.ordinal())) return false;
+ return aSlot < aTileEntity.getSizeInventory() && aTileEntity.isItemValidForSlot(aSlot, aStack);
+ }
+
+ /**
+ * moves multiple stacks from Inv-Side to Inv-Side
+ *
+ * @return the Amount of moved Items
+ */
+ public static int moveMultipleItemStacks(Object aTileEntity1, Object aTileEntity2, ForgeDirection fromSide,
+ ForgeDirection putSide, List<ItemStack> aFilter, boolean aInvertFilter, byte aMaxTargetStackSize,
+ byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce, int aStackAmount) {
+ if (aTileEntity1 instanceof IInventory) return moveMultipleItemStacks(
+ (IInventory) aTileEntity1,
+ aTileEntity2,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aStackAmount,
+ true);
+ return 0;
+ }
+
+ public static int moveMultipleItemStacks(IInventory fromInventory, Object toObject, ForgeDirection fromSide,
+ ForgeDirection putSide, List<ItemStack> aFilter, boolean aInvertFilter, byte aMaxTargetStackSize,
+ byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce, int aMaxStackTransfer,
+ boolean aDoCheckChests) {
+ if (fromInventory == null || aMaxTargetStackSize <= 0
+ || aMinTargetStackSize <= 0
+ || aMaxMoveAtOnce <= 0
+ || aMinTargetStackSize > aMaxTargetStackSize
+ || aMinMoveAtOnce > aMaxMoveAtOnce
+ || aMaxStackTransfer == 0) return 0;
+
+ // find where to take from
+ final int[] tGrabSlots = new int[fromInventory.getSizeInventory()];
+ int tGrabSlotsSize = 0;
+ if (fromInventory instanceof ISidedInventory) {
+ for (int i : ((ISidedInventory) fromInventory).getAccessibleSlotsFromSide(fromSide.ordinal())) {
+ final ItemStack s = fromInventory.getStackInSlot(i);
+ if (s == null || !isAllowedToTakeFromSlot(fromInventory, i, fromSide, s)
+ || s.stackSize < aMinMoveAtOnce
+ || !listContainsItem(aFilter, s, true, aInvertFilter)) continue;
+ tGrabSlots[tGrabSlotsSize++] = i;
+ }
+ } else {
+ for (int i = 0; i < tGrabSlots.length; i++) {
+ ItemStack s = fromInventory.getStackInSlot(i);
+ if (s == null || s.stackSize < aMinMoveAtOnce || !listContainsItem(aFilter, s, true, aInvertFilter))
+ continue;
+ tGrabSlots[tGrabSlotsSize++] = i;
+ }
+ }
+
+ // no source, bail out
+ if (tGrabSlotsSize == 0) {
+ // maybe source is a double chest. check it
+ if (aDoCheckChests && fromInventory instanceof TileEntityChest chest) return moveFromAdjacentChests(
+ chest,
+ toObject,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aMaxStackTransfer);
+ return 0;
+ }
+
+ // if target is an inventory, e.g. chest, machine, drawers...
+ if (toObject instanceof IInventory toInventory) {
+
+ // partially filled slot spare space mapping.
+ // value is the sum of all spare space left not counting completely empty slot
+ final HashMap<ItemId, Integer> tPutItems = new HashMap<>(toInventory.getSizeInventory());
+ // partially filled slot contents
+ final HashMap<ItemId, List<ItemStack>> tPutItemStacks = new HashMap<>(toInventory.getSizeInventory());
+ // completely empty slots
+ final List<Integer> tPutFreeSlots = new ArrayList<>(toInventory.getSizeInventory());
+
+ // find possible target slots
+ int[] accessibleSlots = null;
+ if (toObject instanceof ISidedInventory sided)
+ accessibleSlots = sided.getAccessibleSlotsFromSide(putSide.ordinal());
+ for (int i = 0; i < toInventory.getSizeInventory(); i++) {
+ int slot = i;
+ if (accessibleSlots != null) {
+ if (accessibleSlots.length <= i) break;
+ slot = accessibleSlots[slot];
+ }
+ ItemStack s = toInventory.getStackInSlot(slot);
+ if (s == null) {
+ tPutFreeSlots.add(slot);
+ } else if ((s.stackSize < s.getMaxStackSize() && s.stackSize < toInventory.getInventoryStackLimit())
+ && aMinMoveAtOnce <= s.getMaxStackSize() - s.stackSize
+ && isAllowedToPutIntoSlot(toInventory, slot, putSide, s, (byte) 64)) {
+ ItemId sID = ItemId.createNoCopy(s);
+ tPutItems.merge(
+ sID,
+ (Math.min(s.getMaxStackSize(), toInventory.getInventoryStackLimit()) - s.stackSize),
+ Integer::sum);
+ tPutItemStacks.computeIfAbsent(sID, k -> new ArrayList<>())
+ .add(s);
+ }
+ }
+
+ // target completely filled, bail out
+ if (tPutItems.isEmpty() && tPutFreeSlots.isEmpty()) {
+ // maybe target is a double chest. check it.
+ if (aDoCheckChests && toObject instanceof TileEntityChest chest) return moveToAdjacentChests(
+ fromInventory,
+ chest,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aMaxStackTransfer);
+ return 0;
+ }
+
+ // go over source stacks one by one
+ int tStacksMoved = 0, tTotalItemsMoved = 0;
+ for (int j = 0; j < tGrabSlotsSize; j++) {
+ final int grabSlot = tGrabSlots[j];
+ int tMovedItems;
+ int tStackSize;
+ do {
+ tMovedItems = 0;
+ final ItemStack tGrabStack = fromInventory.getStackInSlot(grabSlot);
+ if (tGrabStack == null) break;
+ tStackSize = tGrabStack.stackSize;
+ final ItemId sID = ItemId.createNoCopy(tGrabStack);
+
+ if (tPutItems.containsKey(sID)) {
+ // there is a partially filled slot, try merging
+ final int canPut = Math.min(tPutItems.get(sID), aMaxMoveAtOnce);
+ if (canPut >= aMinMoveAtOnce) {
+ final List<ItemStack> putStack = tPutItemStacks.get(sID);
+ if (!putStack.isEmpty()) {
+ // can move, do merge
+ int toPut = Math.min(canPut, tStackSize);
+ tMovedItems = toPut;
+ for (int i = 0; i < putStack.size(); i++) {
+ final ItemStack s = putStack.get(i);
+ final int sToPut = Math.min(
+ Math.min(
+ Math.min(toPut, s.getMaxStackSize() - s.stackSize),
+ toInventory.getInventoryStackLimit() - s.stackSize),
+ aMaxTargetStackSize - s.stackSize);
+ if (sToPut <= 0) continue;
+ if (sToPut < aMinMoveAtOnce) continue;
+ if (s.stackSize + sToPut < aMinTargetStackSize) continue;
+ toPut -= sToPut;
+ s.stackSize += sToPut;
+ if (s.stackSize == s.getMaxStackSize()
+ || s.stackSize == toInventory.getInventoryStackLimit()) {
+ // this slot is full. remove this stack from candidate list
+ putStack.remove(i);
+ i--;
+ }
+ if (toPut == 0) break;
+ }
+ tMovedItems -= toPut;
+ if (tMovedItems > 0) {
+ tStackSize -= tMovedItems;
+ tTotalItemsMoved += tMovedItems;
+ // deduct spare space
+ tPutItems.merge(sID, tMovedItems, (a, b) -> a.equals(b) ? null : a - b);
+
+ if (tStackSize == 0) fromInventory.setInventorySlotContents(grabSlot, null);
+ else tGrabStack.stackSize = tStackSize;
+
+ fromInventory.markDirty();
+ toInventory.markDirty();
+ }
+ }
+ }
+ }
+ // still stuff to move & have completely empty slots
+ if (tStackSize > 0 && !tPutFreeSlots.isEmpty()) {
+ for (int i = 0; i < tPutFreeSlots.size(); i++) {
+ final int tPutSlot = tPutFreeSlots.get(i);
+ if (isAllowedToPutIntoSlot(toInventory, tPutSlot, putSide, tGrabStack, (byte) 64)) {
+ // allowed, now do moving
+ final int tMoved = moveStackFromSlotAToSlotB(
+ fromInventory,
+ toInventory,
+ grabSlot,
+ tPutSlot,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ (byte) (aMaxMoveAtOnce - tMovedItems),
+ aMinMoveAtOnce);
+ if (tMoved > 0) {
+ final ItemStack s = toInventory.getStackInSlot(tPutSlot);
+ if (s != null) {
+ // s might be null if tPutInventory is very special, e.g. infinity chest
+ // if s is null, we will not mark this slot as target candidate for anything
+ final int spare = Math
+ .min(s.getMaxStackSize(), toInventory.getInventoryStackLimit())
+ - s.stackSize;
+ if (spare > 0) {
+ final ItemId ssID = ItemId.createNoCopy(s);
+ // add back to spare space count
+ tPutItems.merge(ssID, spare, Integer::sum);
+ // add to partially filled slot list
+ tPutItemStacks.computeIfAbsent(ssID, k -> new ArrayList<>())
+ .add(s);
+ }
+ // this is no longer free
+ tPutFreeSlots.remove(i);
+ i--;
+ }
+ // else -> noop
+ // this is still a free slot. no need to do anything.
+ tTotalItemsMoved += tMoved;
+ tMovedItems += tMoved;
+ tStackSize -= tMoved;
+ if (tStackSize == 0) break;
+ }
+ }
+ }
+ }
+
+ if (tMovedItems > 0) {
+ // check if we have moved enough stacks
+ if (++tStacksMoved >= aMaxStackTransfer) return tTotalItemsMoved;
+ }
+ } while (tMovedItems > 0 && tStackSize > 0); // support inventories that store more than a stack in a
+ // slot
+ }
+
+ // check if source is a double chest, if yes, try move from the adjacent as well
+ if (aDoCheckChests && fromInventory instanceof TileEntityChest chest) {
+ final int tAmount = moveFromAdjacentChests(
+ chest,
+ toObject,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aMaxStackTransfer - tStacksMoved);
+ if (tAmount != 0) return tAmount + tTotalItemsMoved;
+ }
+
+ // check if target is a double chest, if yes, try move to the adjacent as well
+ if (aDoCheckChests && toObject instanceof TileEntityChest chest) {
+ final int tAmount = moveToAdjacentChests(
+ fromInventory,
+ chest,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aMaxStackTransfer - tStacksMoved);
+ if (tAmount != 0) return tAmount + tTotalItemsMoved;
+ }
+
+ return tTotalItemsMoved;
+ }
+ // there should be a function to transfer more than 1 stack in a pipe
+ // however I do not see any ways to improve it. too much work for what it is worth
+ int tTotalItemsMoved = 0;
+ final int tGrabInventorySize = tGrabSlots.length;
+ for (int i = 0; i < tGrabInventorySize; i++) {
+ final int tMoved = moveStackIntoPipe(
+ fromInventory,
+ toObject,
+ tGrabSlots,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aDoCheckChests);
+ if (tMoved == 0) return tTotalItemsMoved;
+ else tTotalItemsMoved += tMoved;
+ }
+ return 0;
+ }
+
+ private static int moveToAdjacentChests(IInventory aTileEntity1, TileEntityChest aTargetChest,
+ ForgeDirection fromSide, ForgeDirection putSide, List<ItemStack> aFilter, boolean aInvertFilter,
+ byte aMaxTargetStackSize, byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce,
+ int aMaxStackTransfer) {
+ if (aTargetChest.adjacentChestChecked) {
+ if (aTargetChest.adjacentChestXNeg != null) {
+ return moveMultipleItemStacks(
+ aTileEntity1,
+ aTargetChest.adjacentChestXNeg,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aMaxStackTransfer,
+ false);
+ } else if (aTargetChest.adjacentChestZNeg != null) {
+ return moveMultipleItemStacks(
+ aTileEntity1,
+ aTargetChest.adjacentChestZNeg,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aMaxStackTransfer,
+ false);
+ } else if (aTargetChest.adjacentChestXPos != null) {
+ return moveMultipleItemStacks(
+ aTileEntity1,
+ aTargetChest.adjacentChestXPos,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aMaxStackTransfer,
+ false);
+ } else if (aTargetChest.adjacentChestZPos != null) {
+ return moveMultipleItemStacks(
+ aTileEntity1,
+ aTargetChest.adjacentChestZPos,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aMaxStackTransfer,
+ false);
+ }
+ }
+ return 0;
+ }
+
+ private static int moveFromAdjacentChests(TileEntityChest fromTileEntityChest, Object toObject,
+ ForgeDirection fromSide, ForgeDirection putSide, List<ItemStack> aFilter, boolean aInvertFilter,
+ byte aMaxTargetStackSize, byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce,
+ int aMaxStackTransfer) {
+ if (fromTileEntityChest.adjacentChestXNeg != null) {
+ return moveMultipleItemStacks(
+ fromTileEntityChest.adjacentChestXNeg,
+ toObject,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aMaxStackTransfer,
+ false);
+ } else if (fromTileEntityChest.adjacentChestZNeg != null) {
+ return moveMultipleItemStacks(
+ fromTileEntityChest.adjacentChestZNeg,
+ toObject,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aMaxStackTransfer,
+ false);
+ } else if (fromTileEntityChest.adjacentChestXPos != null) {
+ return moveMultipleItemStacks(
+ fromTileEntityChest.adjacentChestXPos,
+ toObject,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aMaxStackTransfer,
+ false);
+ } else if (fromTileEntityChest.adjacentChestZPos != null) {
+ return moveMultipleItemStacks(
+ fromTileEntityChest.adjacentChestZPos,
+ toObject,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aMaxStackTransfer,
+ false);
+ }
+ return 0;
+ }
+
+ /**
+ * Moves Stack from Inv-Side to Inv-Side.
+ *
+ * @return the Amount of moved Items
+ */
+ public static byte moveOneItemStack(Object fromObject, Object toObject, ForgeDirection fromSide,
+ ForgeDirection putSide, List<ItemStack> aFilter, boolean aInvertFilter, byte aMaxTargetStackSize,
+ byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce) {
+ if (fromObject instanceof IInventory inv) return moveOneItemStack(
+ inv,
+ toObject,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ true);
+ return 0;
+ }
+
+ /**
+ * This is only because I needed an additional Parameter for the Double Chest Check.
+ */
+ private static byte moveOneItemStack(IInventory fromInventory, Object toObject, ForgeDirection fromSide,
+ ForgeDirection putSide, List<ItemStack> aFilter, boolean aInvertFilter, byte aMaxTargetStackSize,
+ byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce, boolean aDoCheckChests) {
+ if (fromInventory == null || aMaxTargetStackSize <= 0
+ || aMinTargetStackSize <= 0
+ || aMaxMoveAtOnce <= 0
+ || aMinTargetStackSize > aMaxTargetStackSize
+ || aMinMoveAtOnce > aMaxMoveAtOnce) return 0;
+
+ int[] tGrabSlots = null;
+ if (fromInventory instanceof ISidedInventory)
+ tGrabSlots = ((ISidedInventory) fromInventory).getAccessibleSlotsFromSide(fromSide.ordinal());
+ if (tGrabSlots == null) {
+ tGrabSlots = new int[fromInventory.getSizeInventory()];
+ for (int i = 0; i < tGrabSlots.length; i++) tGrabSlots[i] = i;
+ }
+
+ if (toObject instanceof IInventory inv) {
+ int[] tPutSlots = null;
+ if (toObject instanceof ISidedInventory sided)
+ tPutSlots = sided.getAccessibleSlotsFromSide(putSide.ordinal());
+
+ if (tPutSlots == null) {
+ tPutSlots = new int[inv.getSizeInventory()];
+ for (int i = 0; i < tPutSlots.length; i++) tPutSlots[i] = i;
+ }
+
+ for (final int tGrabSlot : tGrabSlots) {
+ byte tMovedItemCount = 0;
+ final ItemStack tGrabStack = fromInventory.getStackInSlot(tGrabSlot);
+ if (listContainsItem(aFilter, tGrabStack, true, aInvertFilter)
+ && (tGrabStack.stackSize >= aMinMoveAtOnce
+ && isAllowedToTakeFromSlot(fromInventory, tGrabSlot, fromSide, tGrabStack))) {
+ for (final int tPutSlot : tPutSlots) {
+ if (isAllowedToPutIntoSlot(inv, tPutSlot, putSide, tGrabStack, aMaxTargetStackSize)) {
+ tMovedItemCount += moveStackFromSlotAToSlotB(
+ fromInventory,
+ inv,
+ tGrabSlot,
+ tPutSlot,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ (byte) (aMaxMoveAtOnce - tMovedItemCount),
+ aMinMoveAtOnce);
+ if (tMovedItemCount >= aMaxMoveAtOnce || (tMovedItemCount > 0 && aMaxTargetStackSize < 64))
+ return tMovedItemCount;
+ }
+ }
+
+ }
+ if (tMovedItemCount > 0) return tMovedItemCount;
+ }
+
+ if (aDoCheckChests && fromInventory instanceof TileEntityChest fromChest
+ && (fromChest.adjacentChestChecked)) {
+ byte tAmount = 0;
+ if (fromChest.adjacentChestXNeg != null) {
+ tAmount = moveOneItemStack(
+ fromChest.adjacentChestXNeg,
+ toObject,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ false);
+ } else if (fromChest.adjacentChestZNeg != null) {
+ tAmount = moveOneItemStack(
+ fromChest.adjacentChestZNeg,
+ toObject,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ false);
+ } else if (fromChest.adjacentChestXPos != null) {
+ tAmount = moveOneItemStack(
+ fromChest.adjacentChestXPos,
+ toObject,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ false);
+ } else if (fromChest.adjacentChestZPos != null) {
+ tAmount = moveOneItemStack(
+ fromChest.adjacentChestZPos,
+ toObject,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ false);
+ }
+ if (tAmount != 0) return tAmount;
+
+ }
+ if (aDoCheckChests && toObject instanceof TileEntityChest toChest && (toChest.adjacentChestChecked)) {
+ byte tAmount = 0;
+ if (toChest.adjacentChestXNeg != null) {
+ tAmount = moveOneItemStack(
+ fromInventory,
+ toChest.adjacentChestXNeg,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ false);
+ } else if (toChest.adjacentChestZNeg != null) {
+ tAmount = moveOneItemStack(
+ fromInventory,
+ toChest.adjacentChestZNeg,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ false);
+ } else if (toChest.adjacentChestXPos != null) {
+ tAmount = moveOneItemStack(
+ fromInventory,
+ toChest.adjacentChestXPos,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ false);
+ } else if (toChest.adjacentChestZPos != null) {
+ tAmount = moveOneItemStack(
+ fromInventory,
+ toChest.adjacentChestZPos,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ false);
+ }
+ if (tAmount != 0) return tAmount;
+
+ }
+ }
+
+ return moveStackIntoPipe(
+ fromInventory,
+ toObject,
+ tGrabSlots,
+ fromSide,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aDoCheckChests);
+ }
+
+ /**
+ * Moves Stack from Inv-Side to Inv-Slot.
+ *
+ * @return the Amount of moved Items
+ */
+ public static byte moveOneItemStackIntoSlot(Object fromTileEntity, Object toTileEntity, ForgeDirection fromSide,
+ int putSlot, List<ItemStack> aFilter, boolean aInvertFilter, byte aMaxTargetStackSize, byte aMinTargetStackSize,
+ byte aMaxMoveAtOnce, byte aMinMoveAtOnce) {
+ if (!(fromTileEntity instanceof IInventory fromInv) || aMaxTargetStackSize <= 0
+ || aMinTargetStackSize <= 0
+ || aMaxMoveAtOnce <= 0
+ || aMinTargetStackSize > aMaxTargetStackSize
+ || aMinMoveAtOnce > aMaxMoveAtOnce) return 0;
+
+ int[] tGrabSlots = null;
+ if (fromTileEntity instanceof ISidedInventory sided)
+ tGrabSlots = sided.getAccessibleSlotsFromSide(fromSide.ordinal());
+ if (tGrabSlots == null) {
+ tGrabSlots = new int[fromInv.getSizeInventory()];
+ for (int i = 0; i < tGrabSlots.length; i++) tGrabSlots[i] = i;
+ }
+
+ if (toTileEntity instanceof IInventory toInv) {
+ for (final int tGrabSlot : tGrabSlots) {
+ if (listContainsItem(aFilter, fromInv.getStackInSlot(tGrabSlot), true, aInvertFilter)) {
+ if (isAllowedToTakeFromSlot(fromInv, tGrabSlot, fromSide, fromInv.getStackInSlot(tGrabSlot))) {
+ if (isAllowedToPutIntoSlot(
+ toInv,
+ putSlot,
+ ForgeDirection.UNKNOWN,
+ fromInv.getStackInSlot(tGrabSlot),
+ aMaxTargetStackSize)) {
+ byte tMovedItemCount = moveStackFromSlotAToSlotB(
+ fromInv,
+ toInv,
+ tGrabSlot,
+ putSlot,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce);
+ if (tMovedItemCount > 0) return tMovedItemCount;
+ }
+ }
+ }
+ }
+ }
+
+ final ForgeDirection toSide = fromSide.getOpposite();
+ moveStackIntoPipe(
+ fromInv,
+ toTileEntity,
+ tGrabSlots,
+ fromSide,
+ ForgeDirection.UNKNOWN,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce);
+ return 0;
+ }
+
+ /**
+ * Moves Stack from Inv-Slot to Inv-Slot.
+ *
+ * @return the Amount of moved Items
+ */
+ public static byte moveFromSlotToSlot(IInventory fromInv, IInventory toInv, int aGrabFrom, int aPutTo,
+ List<ItemStack> aFilter, boolean aInvertFilter, byte aMaxTargetStackSize, byte aMinTargetStackSize,
+ byte aMaxMoveAtOnce, byte aMinMoveAtOnce) {
+ if (fromInv == null || toInv == null
+ || aGrabFrom < 0
+ || aPutTo < 0
+ || aMinTargetStackSize <= 0
+ || aMaxMoveAtOnce <= 0
+ || aMinTargetStackSize > aMaxTargetStackSize
+ || aMinMoveAtOnce > aMaxMoveAtOnce) return 0;
+ if (listContainsItem(aFilter, fromInv.getStackInSlot(aGrabFrom), true, aInvertFilter)) {
+ if (isAllowedToTakeFromSlot(
+ fromInv,
+ aGrabFrom,
+ ForgeDirection.UNKNOWN,
+ fromInv.getStackInSlot(aGrabFrom))) {
+ if (isAllowedToPutIntoSlot(
+ toInv,
+ aPutTo,
+ ForgeDirection.UNKNOWN,
+ fromInv.getStackInSlot(aGrabFrom),
+ aMaxTargetStackSize)) {
+ byte tMovedItemCount = moveStackFromSlotAToSlotB(
+ fromInv,
+ toInv,
+ aGrabFrom,
+ aPutTo,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce);
+ if (tMovedItemCount > 0) return tMovedItemCount;
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Moves Stack from Inv-Side to Inv-Slot.
+ *
+ * @return the Amount of moved Items
+ */
+ public static byte moveFromSlotToSide(IInventory fromTile, Object toTile, int fromSlot, ForgeDirection putSide,
+ List<ItemStack> aFilter, boolean aInvertFilter, byte aMaxTargetStackSize, byte aMinTargetStackSize,
+ byte aMaxMoveAtOnce, byte aMinMoveAtOnce, boolean aDoCheckChests) {
+ if (fromTile == null || fromSlot < 0
+ || aMinTargetStackSize <= 0
+ || aMaxMoveAtOnce <= 0
+ || aMinTargetStackSize > aMaxTargetStackSize
+ || aMinMoveAtOnce > aMaxMoveAtOnce) return 0;
+
+ if (!listContainsItem(aFilter, fromTile.getStackInSlot(fromSlot), true, aInvertFilter)
+ || !isAllowedToTakeFromSlot(fromTile, fromSlot, ForgeDirection.UNKNOWN, fromTile.getStackInSlot(fromSlot)))
+ return 0;
+
+ if (toTile instanceof IInventory) {
+ int[] tPutSlots = null;
+ if (toTile instanceof ISidedInventory sided)
+ tPutSlots = sided.getAccessibleSlotsFromSide(putSide.ordinal());
+
+ if (tPutSlots == null) {
+ tPutSlots = new int[((IInventory) toTile).getSizeInventory()];
+ for (int i = 0; i < tPutSlots.length; i++) tPutSlots[i] = i;
+ }
+
+ byte tMovedItemCount = 0;
+ for (final int tPutSlot : tPutSlots) {
+ if (isAllowedToPutIntoSlot(
+ (IInventory) toTile,
+ tPutSlot,
+ putSide,
+ fromTile.getStackInSlot(fromSlot),
+ aMaxTargetStackSize)) {
+ tMovedItemCount += moveStackFromSlotAToSlotB(
+ fromTile,
+ (IInventory) toTile,
+ fromSlot,
+ tPutSlot,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ (byte) (aMaxMoveAtOnce - tMovedItemCount),
+ aMinMoveAtOnce);
+ if (tMovedItemCount >= aMaxMoveAtOnce) {
+ return tMovedItemCount;
+ }
+ }
+ }
+ if (tMovedItemCount > 0) return tMovedItemCount;
+
+ if (aDoCheckChests && toTile instanceof TileEntityChest tTileEntity2) {
+ if (tTileEntity2.adjacentChestChecked) {
+ if (tTileEntity2.adjacentChestXNeg != null) {
+ tMovedItemCount = moveFromSlotToSide(
+ fromTile,
+ tTileEntity2.adjacentChestXNeg,
+ fromSlot,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ false);
+ } else if (tTileEntity2.adjacentChestZNeg != null) {
+ tMovedItemCount = moveFromSlotToSide(
+ fromTile,
+ tTileEntity2.adjacentChestZNeg,
+ fromSlot,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ false);
+ } else if (tTileEntity2.adjacentChestXPos != null) {
+ tMovedItemCount = moveFromSlotToSide(
+ fromTile,
+ tTileEntity2.adjacentChestXPos,
+ fromSlot,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ false);
+ } else if (tTileEntity2.adjacentChestZPos != null) {
+ tMovedItemCount = moveFromSlotToSide(
+ fromTile,
+ tTileEntity2.adjacentChestZPos,
+ fromSlot,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ false);
+ }
+ if (tMovedItemCount > 0) return tMovedItemCount;
+ }
+ }
+ }
+ return moveStackIntoPipe(
+ fromTile,
+ toTile,
+ new int[] { fromSlot },
+ ForgeDirection.UNKNOWN,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ aDoCheckChests);
+ }
+
+ public static byte moveFromSlotToSide(IInventory fromTile, Object toTile, int fromSlot, ForgeDirection putSide,
+ List<ItemStack> aFilter, boolean aInvertFilter, byte aMaxTargetStackSize, byte aMinTargetStackSize,
+ byte aMaxMoveAtOnce, byte aMinMoveAtOnce) {
+ return moveFromSlotToSide(
+ fromTile,
+ toTile,
+ fromSlot,
+ putSide,
+ aFilter,
+ aInvertFilter,
+ aMaxTargetStackSize,
+ aMinTargetStackSize,
+ aMaxMoveAtOnce,
+ aMinMoveAtOnce,
+ true);
+ }
+
+ /**
+ * Move up to maxAmount amount of fluid from source to dest, with optional filtering via allowMove. note that this
+ * filter cannot bypass filtering done by IFluidHandlers themselves.
+ *
+ * this overload will assume the fill side is the opposite of drainSide
+ *
+ * @param source tank to drain from. method become noop if this is null
+ * @param dest tank to fill to. method become noop if this is null
+ * @param drainSide side used during draining operation
+ * @param maxAmount max amount of fluid to transfer. method become noop if this is not a positive integer
+ * @param allowMove filter. can be null to signal all fluids are accepted
+ */
+ public static void moveFluid(IFluidHandler source, IFluidHandler dest, ForgeDirection drainSide, int maxAmount,
+ @Nullable Predicate<FluidStack> allowMove) {
+ moveFluid(source, dest, drainSide, drainSide.getOpposite(), maxAmount, allowMove);
+ }
+
+ /**
+ * Move up to maxAmount amount of fluid from source to dest, with optional filtering via allowMove. note that this
+ * filter cannot bypass filtering done by IFluidHandlers themselves.
+ *
+ * @param source tank to drain from. method become noop if this is null
+ * @param dest tank to fill to. method become noop if this is null
+ * @param drainSide side used during draining operation
+ * @param fillSide side used during filling operation
+ * @param maxAmount max amount of fluid to transfer. method become noop if this is not a positive integer
+ * @param allowMove filter. can be null to signal all fluids are accepted
+ */
+ public static void moveFluid(IFluidHandler source, IFluidHandler dest, ForgeDirection drainSide,
+ ForgeDirection fillSide, int maxAmount, @Nullable Predicate<FluidStack> allowMove) {
+ if (source == null || dest == null || maxAmount <= 0) return;
+ FluidStack liquid = source.drain(drainSide, maxAmount, false);
+ if (liquid == null) return;
+ liquid = liquid.copy();
+ liquid.amount = dest.fill(fillSide, liquid, false);
+ if (liquid.amount > 0 && (allowMove == null || allowMove.test(liquid))) {
+ dest.fill(fillSide, source.drain(drainSide, liquid.amount, true), true);
+ }
+ }
+
+ public static boolean listContainsItem(Collection<ItemStack> aList, ItemStack aStack, boolean aTIfListEmpty,
+ boolean aInvertFilter) {
+ if (aStack == null || aStack.stackSize < 1) return false;
+ if (aList == null) return aTIfListEmpty;
+ boolean tEmpty = true;
+ for (ItemStack tStack : aList) {
+ if (tStack != null) {
+ tEmpty = false;
+ if (areStacksEqual(aStack, tStack)) {
+ return !aInvertFilter;
+ }
+ }
+ }
+ return tEmpty ? aTIfListEmpty : aInvertFilter;
+ }
+
+ public static boolean areStacksOrToolsEqual(ItemStack aStack1, ItemStack aStack2) {
+ if (aStack1 != null && aStack2 != null && aStack1.getItem() == aStack2.getItem()) {
+ if (aStack1.getItem()
+ .isDamageable()) return true;
+ return ((aStack1.getTagCompound() == null) == (aStack2.getTagCompound() == null))
+ && (aStack1.getTagCompound() == null || aStack1.getTagCompound()
+ .equals(aStack2.getTagCompound()))
+ && (Items.feather.getDamage(aStack1) == Items.feather.getDamage(aStack2)
+ || Items.feather.getDamage(aStack1) == W
+ || Items.feather.getDamage(aStack2) == W);
+ }
+ return false;
+ }
+
+ public static boolean areFluidsEqual(FluidStack aFluid1, FluidStack aFluid2) {
+ return areFluidsEqual(aFluid1, aFluid2, false);
+ }
+
+ public static boolean areFluidsEqual(FluidStack aFluid1, FluidStack aFluid2, boolean aIgnoreNBT) {
+ return aFluid1 != null && aFluid2 != null
+ && aFluid1.getFluid() == aFluid2.getFluid()
+ && (aIgnoreNBT || ((aFluid1.tag == null) == (aFluid2.tag == null))
+ && (aFluid1.tag == null || aFluid1.tag.equals(aFluid2.tag)));
+ }
+
+ public static boolean areStacksEqual(ItemStack aStack1, ItemStack aStack2) {
+ return areStacksEqual(aStack1, aStack2, false);
+ }
+
+ public static boolean areStacksEqual(ItemStack aStack1, ItemStack aStack2, boolean aIgnoreNBT) {
+ return aStack1 != null && aStack2 != null
+ && aStack1.getItem() == aStack2.getItem()
+ && (Items.feather.getDamage(aStack1) == Items.feather.getDamage(aStack2)
+ || Items.feather.getDamage(aStack1) == W
+ || Items.feather.getDamage(aStack2) == W)
+ && (aIgnoreNBT || (((aStack1.getTagCompound() == null) == (aStack2.getTagCompound() == null))
+ && (aStack1.getTagCompound() == null || aStack1.getTagCompound()
+ .equals(aStack2.getTagCompound()))));
+ }
+
+ public static boolean areStacksEqualOrNull(ItemStack stack1, ItemStack stack2) {
+ return (stack1 == null && stack2 == null) || GTUtility.areStacksEqual(stack1, stack2);
+ }
+
+ /**
+ * Treat both null list, or both null item stack at same list position as equal.
+ * <p>
+ * Since ItemStack doesn't override equals and hashCode, you cannot just use Objects.equals
+ */
+ public static boolean areStackListsEqual(List<ItemStack> lhs, List<ItemStack> rhs, boolean ignoreStackSize,
+ boolean ignoreNBT) {
+ if (lhs == null) return rhs == null;
+ if (rhs == null) return false;
+ if (lhs.size() != rhs.size()) return false;
+ for (Iterator<ItemStack> it1 = lhs.iterator(), it2 = rhs.iterator(); it1.hasNext() && it2.hasNext();) {
+ if (!areStacksEqualExtended(it1.next(), it2.next(), ignoreStackSize, ignoreNBT)) return false;
+ }
+ return true;
+ }
+
+ private static boolean areStacksEqualExtended(ItemStack lhs, ItemStack rhs, boolean ignoreStackSize,
+ boolean ignoreNBT) {
+ if (lhs == null) return rhs == null;
+ if (rhs == null) return false;
+ return lhs.getItem() == rhs.getItem()
+ && (ignoreNBT || Objects.equals(lhs.stackTagCompound, rhs.stackTagCompound))
+ && (ignoreStackSize || lhs.stackSize == rhs.stackSize);
+ }
+
+ public static boolean areUnificationsEqual(ItemStack aStack1, ItemStack aStack2) {
+ return areUnificationsEqual(aStack1, aStack2, false);
+ }
+
+ public static boolean areUnificationsEqual(ItemStack aStack1, ItemStack aStack2, boolean aIgnoreNBT) {
+ return areStacksEqual(
+ GTOreDictUnificator.get_nocopy(aStack1),
+ GTOreDictUnificator.get_nocopy(aStack2),
+ aIgnoreNBT);
+ }
+
+ public static String getFluidName(Fluid aFluid, boolean aLocalized) {
+ if (aFluid == null) return E;
+ String rName = aLocalized ? aFluid.getLocalizedName(new FluidStack(aFluid, 0)) : aFluid.getUnlocalizedName();
+ if (rName.contains("fluid.") || rName.contains("tile.")) return capitalizeString(
+ rName.replaceAll("fluid.", E)
+ .replaceAll("tile.", E));
+ return rName;
+ }
+
+ public static String getFluidName(FluidStack aFluid, boolean aLocalized) {
+ if (aFluid == null) return E;
+ return getFluidName(aFluid.getFluid(), aLocalized);
+ }
+
+ public static void reInit() {
+ sFilledContainerToData.clear();
+ sEmptyContainerToFluidToData.clear();
+ sFluidToContainers.clear();
+ for (FluidContainerData tData : sFluidContainerList) {
+ String fluidName = tData.fluid.getFluid()
+ .getName();
+ sFilledContainerToData.put(new GTItemStack(tData.filledContainer), tData);
+ Map<String, FluidContainerData> tFluidToContainer = sEmptyContainerToFluidToData
+ .get(new GTItemStack(tData.emptyContainer));
+ List<ItemStack> tContainers = sFluidToContainers.get(fluidName);
+ if (tFluidToContainer == null) {
+ sEmptyContainerToFluidToData
+ .put(new GTItemStack(tData.emptyContainer), tFluidToContainer = new /* Concurrent */ HashMap<>());
+ }
+ tFluidToContainer.put(fluidName, tData);
+ if (tContainers == null) {
+ tContainers = new ArrayList<>();
+ tContainers.add(tData.filledContainer);
+ sFluidToContainers.put(fluidName, tContainers);
+ } else tContainers.add(tData.filledContainer);
+ }
+ }
+
+ public static void addFluidContainerData(FluidContainerData aData) {
+ String fluidName = aData.fluid.getFluid()
+ .getName();
+ sFluidContainerList.add(aData);
+ sFilledContainerToData.put(new GTItemStack(aData.filledContainer), aData);
+ Map<String, FluidContainerData> tFluidToContainer = sEmptyContainerToFluidToData
+ .get(new GTItemStack(aData.emptyContainer));
+ List<ItemStack> tContainers = sFluidToContainers.get(fluidName);
+ if (tFluidToContainer == null) {
+ sEmptyContainerToFluidToData
+ .put(new GTItemStack(aData.emptyContainer), tFluidToContainer = new /* Concurrent */ HashMap<>());
+ }
+ tFluidToContainer.put(fluidName, aData);
+ if (tContainers == null) {
+ tContainers = new ArrayList<>();
+ tContainers.add(aData.filledContainer);
+ sFluidToContainers.put(fluidName, tContainers);
+ } else tContainers.add(aData.filledContainer);
+ }
+
+ public static List<ItemStack> getContainersFromFluid(FluidStack tFluidStack) {
+ if (tFluidStack != null) {
+ List<ItemStack> tContainers = sFluidToContainers.get(
+ tFluidStack.getFluid()
+ .getName());
+ if (tContainers == null) return new ArrayList<>();
+ return tContainers;
+ }
+ return new ArrayList<>();
+ }
+
+ public static ItemStack fillFluidContainer(FluidStack aFluid, ItemStack aStack, boolean aRemoveFluidDirectly,
+ boolean aCheckIFluidContainerItems) {
+ if (isStackInvalid(aStack) || aFluid == null) return null;
+ if (GTModHandler.isWater(aFluid) && ItemList.Bottle_Empty.isStackEqual(aStack)) {
+ if (aFluid.amount >= 1000) {
+ return new ItemStack(Items.potionitem, 1, 0);
+ }
+ return null;
+ }
+ if (aCheckIFluidContainerItems && aStack.getItem() instanceof IFluidContainerItem
+ && ((IFluidContainerItem) aStack.getItem()).getFluid(aStack) == null
+ && ((IFluidContainerItem) aStack.getItem()).getCapacity(aStack) <= aFluid.amount) {
+ if (aRemoveFluidDirectly) aFluid.amount -= ((IFluidContainerItem) aStack.getItem())
+ .fill(aStack = copyAmount(1, aStack), aFluid, true);
+ else((IFluidContainerItem) aStack.getItem()).fill(aStack = copyAmount(1, aStack), aFluid, true);
+ return aStack;
+ }
+ Map<String, FluidContainerData> tFluidToContainer = sEmptyContainerToFluidToData.get(new GTItemStack(aStack));
+ if (tFluidToContainer == null) return null;
+ FluidContainerData tData = tFluidToContainer.get(
+ aFluid.getFluid()
+ .getName());
+ if (tData == null || tData.fluid.amount > aFluid.amount) return null;
+ if (aRemoveFluidDirectly) aFluid.amount -= tData.fluid.amount;
+ return copyAmount(1, tData.filledContainer);
+ }
+
+ public static int calculateRecipeEU(Materials aMaterial, int defaultRecipeEUPerTick) {
+ return aMaterial.getProcessingMaterialTierEU() == 0 ? defaultRecipeEUPerTick
+ : aMaterial.getProcessingMaterialTierEU();
+ }
+
+ public static ItemStack getFluidDisplayStack(Fluid aFluid) {
+ return aFluid == null ? null : getFluidDisplayStack(new FluidStack(aFluid, 0), false);
+ }
+
+ public static ItemStack getFluidDisplayStack(FluidStack aFluid, boolean aUseStackSize) {
+ return getFluidDisplayStack(aFluid, aUseStackSize, false);
+ }
+
+ public static ItemStack getFluidDisplayStack(FluidStack aFluid, boolean aUseStackSize, boolean aHideStackSize) {
+ if (aFluid == null || aFluid.getFluid() == null) return null;
+ int tmp = 0;
+ try {
+ tmp = aFluid.getFluid()
+ .getID();
+ } catch (Exception e) {
+ System.err.println(e);
+ }
+ ItemStack rStack = ItemList.Display_Fluid.getWithDamage(1, tmp);
+ NBTTagCompound tNBT = new NBTTagCompound();
+ tNBT.setLong("mFluidDisplayAmount", aUseStackSize ? aFluid.amount : 0);
+ tNBT.setLong(
+ "mFluidDisplayHeat",
+ aFluid.getFluid()
+ .getTemperature(aFluid));
+ tNBT.setBoolean(
+ "mFluidState",
+ aFluid.getFluid()
+ .isGaseous(aFluid));
+ tNBT.setBoolean("mHideStackSize", aHideStackSize);
+ try {
+ tNBT.setString("mFluidMaterialName", FLUID_MAP.get(aFluid.getFluid()).mName);
+ } catch (Exception ignored) {}
+ rStack.setTagCompound(tNBT);
+ return rStack;
+ }
+
+ public static FluidStack getFluidFromDisplayStack(ItemStack aDisplayStack) {
+ if (!isStackValid(aDisplayStack) || aDisplayStack.getItem() != ItemList.Display_Fluid.getItem()
+ || !aDisplayStack.hasTagCompound()) return null;
+ Fluid tFluid = FluidRegistry.getFluid(
+ ItemList.Display_Fluid.getItem()
+ .getDamage(aDisplayStack));
+ return new FluidStack(
+ tFluid,
+ (int) aDisplayStack.getTagCompound()
+ .getLong("mFluidDisplayAmount"));
+ }
+
+ public static boolean containsFluid(ItemStack aStack, FluidStack aFluid, boolean aCheckIFluidContainerItems) {
+ if (isStackInvalid(aStack) || aFluid == null) return false;
+ if (aCheckIFluidContainerItems && aStack.getItem() instanceof IFluidContainerItem
+ && ((IFluidContainerItem) aStack.getItem()).getCapacity(aStack) > 0)
+ return aFluid
+ .isFluidEqual(((IFluidContainerItem) aStack.getItem()).getFluid(aStack = copyAmount(1, aStack)));
+ FluidContainerData tData = sFilledContainerToData.get(new GTItemStack(aStack));
+ return tData != null && tData.fluid.isFluidEqual(aFluid);
+ }
+
+ public static FluidStack getFluidForFilledItem(ItemStack aStack, boolean aCheckIFluidContainerItems) {
+ if (isStackInvalid(aStack)) return null;
+ if (aCheckIFluidContainerItems && aStack.getItem() instanceof IFluidContainerItem
+ && ((IFluidContainerItem) aStack.getItem()).getCapacity(aStack) > 0)
+ return ((IFluidContainerItem) aStack.getItem()).drain(copyAmount(1, aStack), Integer.MAX_VALUE, true);
+ FluidContainerData tData = sFilledContainerToData.get(new GTItemStack(aStack));
+ return tData == null ? null : tData.fluid.copy();
+ }
+
+ /**
+ * Get empty fluid container from filled one.
+ */
+ public static ItemStack getContainerForFilledItem(ItemStack aStack, boolean aCheckIFluidContainerItems) {
+ if (isStackInvalid(aStack)) return null;
+ FluidContainerData tData = sFilledContainerToData.get(new GTItemStack(aStack));
+ if (tData != null) return copyAmount(1, tData.emptyContainer);
+ if (aCheckIFluidContainerItems && aStack.getItem() instanceof IFluidContainerItem
+ && ((IFluidContainerItem) aStack.getItem()).getCapacity(aStack) > 0) {
+ ((IFluidContainerItem) aStack.getItem()).drain(aStack = copyAmount(1, aStack), Integer.MAX_VALUE, true);
+ return aStack;
+ }
+ return null;
+ }
+
+ /**
+ * This is NOT meant for fluid manipulation! It's for getting item container, which is generally used for
+ * crafting recipes. While it also works for many of the fluid containers, some don't.
+ * <p>
+ * Use {@link #getContainerForFilledItem} for getting empty fluid container.
+ */
+ public static ItemStack getContainerItem(ItemStack aStack, boolean aCheckIFluidContainerItems) {
+ if (isStackInvalid(aStack)) return null;
+ if (aStack.getItem()
+ .hasContainerItem(aStack))
+ return aStack.getItem()
+ .getContainerItem(aStack);
+ /*
+ * These are all special Cases, in which it is intended to have only GT Blocks outputting those Container Items
+ */
+ if (ItemList.Cell_Empty.isStackEqual(aStack, false, true)) return null;
+ if (aStack.getItem() == Items.potionitem || aStack.getItem() == Items.experience_bottle
+ || ItemList.TF_Vial_FieryBlood.isStackEqual(aStack)
+ || ItemList.TF_Vial_FieryTears.isStackEqual(aStack)) return ItemList.Bottle_Empty.get(1);
+
+ if (aCheckIFluidContainerItems && aStack.getItem() instanceof IFluidContainerItem
+ && ((IFluidContainerItem) aStack.getItem()).getCapacity(aStack) > 0) {
+ ItemStack tStack = copyAmount(1, aStack);
+ ((IFluidContainerItem) aStack.getItem()).drain(tStack, Integer.MAX_VALUE, true);
+ if (!areStacksEqual(aStack, tStack)) return tStack;
+ return null;
+ }
+
+ int tCapsuleCount = GTModHandler.getCapsuleCellContainerCount(aStack);
+ if (tCapsuleCount > 0) return ItemList.Cell_Empty.get(tCapsuleCount);
+
+ if (ItemList.IC2_ForgeHammer.isStackEqual(aStack) || ItemList.IC2_WireCutter.isStackEqual(aStack))
+ return copyMetaData(Items.feather.getDamage(aStack) + 1, aStack);
+ return null;
+ }
+
+ public static FluidStack getFluidFromContainerOrFluidDisplay(ItemStack stack) {
+ FluidStack fluidStack = GTUtility.getFluidForFilledItem(stack, true);
+ if (fluidStack == null) {
+ fluidStack = GTUtility.getFluidFromDisplayStack(stack);
+ }
+ return fluidStack;
+ }
+
+ public static synchronized boolean removeIC2BottleRecipe(ItemStack aContainer, ItemStack aInput,
+ Map<ic2.api.recipe.ICannerBottleRecipeManager.Input, RecipeOutput> aRecipeList, ItemStack aOutput) {
+ if ((isStackInvalid(aInput) && isStackInvalid(aOutput) && isStackInvalid(aContainer)) || aRecipeList == null)
+ return false;
+ boolean rReturn = false;
+ Iterator<Map.Entry<ic2.api.recipe.ICannerBottleRecipeManager.Input, RecipeOutput>> tIterator = aRecipeList
+ .entrySet()
+ .iterator();
+ aOutput = GTOreDictUnificator.get(aOutput);
+ while (tIterator.hasNext()) {
+ Map.Entry<ic2.api.recipe.ICannerBottleRecipeManager.Input, RecipeOutput> tEntry = tIterator.next();
+ if (aInput == null || tEntry.getKey()
+ .matches(aContainer, aInput)) {
+ List<ItemStack> tList = tEntry.getValue().items;
+ if (tList != null) for (ItemStack tOutput : tList)
+ if (aOutput == null || areStacksEqual(GTOreDictUnificator.get(tOutput), aOutput)) {
+ tIterator.remove();
+ rReturn = true;
+ break;
+ }
+ }
+ }
+ return rReturn;
+ }
+
+ public static synchronized boolean removeSimpleIC2MachineRecipe(ItemStack aInput,
+ Map<IRecipeInput, RecipeOutput> aRecipeList, ItemStack aOutput) {
+ if ((isStackInvalid(aInput) && isStackInvalid(aOutput)) || aRecipeList == null) return false;
+ boolean rReturn = false;
+ Iterator<Map.Entry<IRecipeInput, RecipeOutput>> tIterator = aRecipeList.entrySet()
+ .iterator();
+ aOutput = GTOreDictUnificator.get(aOutput);
+ while (tIterator.hasNext()) {
+ Map.Entry<IRecipeInput, RecipeOutput> tEntry = tIterator.next();
+ if (aInput == null || tEntry.getKey()
+ .matches(aInput)) {
+ List<ItemStack> tList = tEntry.getValue().items;
+ if (tList != null) for (ItemStack tOutput : tList)
+ if (aOutput == null || areStacksEqual(GTOreDictUnificator.get(tOutput), aOutput)) {
+ tIterator.remove();
+ rReturn = true;
+ break;
+ }
+ }
+ }
+ return rReturn;
+ }
+
+ public static synchronized void bulkRemoveSimpleIC2MachineRecipe(Map<ItemStack, ItemStack> toRemove,
+ Map<IRecipeInput, RecipeOutput> aRecipeList) {
+ if (aRecipeList == null || aRecipeList.isEmpty()) return;
+ toRemove.entrySet()
+ .removeIf(aEntry -> (isStackInvalid(aEntry.getKey()) && isStackInvalid(aEntry.getValue())));
+ final Map<ItemStack, ItemStack> finalToRemove = Maps.transformValues(toRemove, GTOreDictUnificator::get_nocopy);
+
+ aRecipeList.entrySet()
+ .removeIf(
+ tEntry -> finalToRemove.entrySet()
+ .stream()
+ .anyMatch(aEntry -> {
+ final ItemStack aInput = aEntry.getKey(), aOutput = aEntry.getValue();
+ final List<ItemStack> tList = tEntry.getValue().items;
+
+ if (tList == null) return false;
+ if (aInput != null && !tEntry.getKey()
+ .matches(aInput)) return false;
+
+ return tList.stream()
+ .anyMatch(
+ tOutput -> (aOutput == null
+ || areStacksEqual(GTOreDictUnificator.get(tOutput), aOutput)));
+ }));
+ }
+
+ public static boolean addSimpleIC2MachineRecipe(ItemStack aInput, Map<IRecipeInput, RecipeOutput> aRecipeList,
+ NBTTagCompound aNBT, Object... aOutput) {
+ if (isStackInvalid(aInput) || aOutput.length == 0 || aRecipeList == null) return false;
+ ItemData tOreName = GTOreDictUnificator.getAssociation(aInput);
+ for (Object o : aOutput) {
+ if (o == null) {
+ GT_FML_LOGGER.info("EmptyIC2Output!" + aInput.getUnlocalizedName());
+ return false;
+ }
+ }
+ ItemStack[] tStack = GTOreDictUnificator.getStackArray(true, aOutput);
+ if (tStack.length > 0 && areStacksEqual(aInput, tStack[0])) return false;
+ if (tOreName != null) {
+ if (tOreName.toString()
+ .equals("dustAsh")
+ && tStack[0].getUnlocalizedName()
+ .equals("tile.volcanicAsh"))
+ return false;
+ aRecipeList
+ .put(new RecipeInputOreDict(tOreName.toString(), aInput.stackSize), new RecipeOutput(aNBT, tStack));
+ } else {
+ aRecipeList
+ .put(new RecipeInputItemStack(copyOrNull(aInput), aInput.stackSize), new RecipeOutput(aNBT, tStack));
+ }
+ return true;
+ }
+
+ public static ItemStack getWrittenBook(String aMapping, ItemStack aStackToPutNBT) {
+ if (isStringInvalid(aMapping)) return null;
+ ItemStack rStack = GregTechAPI.sBookList.get(aMapping);
+ if (rStack == null) return aStackToPutNBT;
+ if (aStackToPutNBT != null) {
+ aStackToPutNBT.setTagCompound(rStack.getTagCompound());
+ return aStackToPutNBT;
+ }
+ return copyAmount(1, rStack);
+ }
+
+ public static ItemStack getWrittenBook(String aMapping, String aTitle, String aAuthor, String... aPages) {
+ if (isStringInvalid(aMapping)) return null;
+ ItemStack rStack = GregTechAPI.sBookList.get(aMapping);
+ if (rStack != null) return copyAmount(1, rStack);
+ if (isStringInvalid(aTitle) || isStringInvalid(aAuthor) || aPages.length == 0) return null;
+ sBookCount++;
+ rStack = new ItemStack(Items.written_book, 1);
+ NBTTagCompound tNBT = new NBTTagCompound();
+ tNBT.setString("title", GTLanguageManager.addStringLocalization("Book." + aTitle + ".Name", aTitle));
+ tNBT.setString("author", aAuthor);
+ NBTTagList tNBTList = new NBTTagList();
+ for (byte i = 0; i < aPages.length; i++) {
+ aPages[i] = GTLanguageManager
+ .addStringLocalization("Book." + aTitle + ".Page" + ((i < 10) ? "0" + i : i), aPages[i]);
+ if (i < 48) {
+ if (aPages[i].length() < 256) tNBTList.appendTag(new NBTTagString(aPages[i]));
+ else GTLog.err.println("WARNING: String for written Book too long! -> " + aPages[i]);
+ } else {
+ GTLog.err.println("WARNING: Too much Pages for written Book! -> " + aTitle);
+ break;
+ }
+ }
+ tNBTList.appendTag(
+ new NBTTagString(
+ "Credits to " + aAuthor
+ + " for writing this Book. This was Book Nr. "
+ + sBookCount
+ + " at its creation. Gotta get 'em all!"));
+ tNBT.setTag("pages", tNBTList);
+ rStack.setTagCompound(tNBT);
+ GTLog.out.println(
+ "GTMod: Added Book to Book List - Mapping: '" + aMapping
+ + "' - Name: '"
+ + aTitle
+ + "' - Author: '"
+ + aAuthor
+ + "'");
+ GregTechAPI.sBookList.put(aMapping, rStack);
+ return copyOrNull(rStack);
+ }
+
+ public static boolean doSoundAtClient(String aSoundName, int aTimeUntilNextSound, float aSoundStrength) {
+ if (aSoundName == null) return false;
+ return doSoundAtClient(aSoundName, aTimeUntilNextSound, aSoundStrength, GT.getThePlayer());
+ }
+
+ public static boolean doSoundAtClient(SoundResource sound, int aTimeUntilNextSound, float aSoundStrength) {
+ return doSoundAtClient(sound.resourceLocation, aTimeUntilNextSound, aSoundStrength, GT.getThePlayer());
+ }
+
+ public static boolean doSoundAtClient(ResourceLocation aSoundResourceLocation, int aTimeUntilNextSound,
+ float aSoundStrength) {
+ return doSoundAtClient(aSoundResourceLocation, aTimeUntilNextSound, aSoundStrength, GT.getThePlayer());
+ }
+
+ public static boolean doSoundAtClient(String aSoundName, int aTimeUntilNextSound, float aSoundStrength,
+ Entity aEntity) {
+ if (aEntity == null || aSoundName == null) return false;
+ return doSoundAtClient(
+ aSoundName,
+ aTimeUntilNextSound,
+ aSoundStrength,
+ aEntity.posX,
+ aEntity.posY,
+ aEntity.posZ);
+ }
+
+ public static boolean doSoundAtClient(ResourceLocation aSoundResourceLocation, int aTimeUntilNextSound,
+ float aSoundStrength, Entity aEntity) {
+ if (aEntity == null) return false;
+ return doSoundAtClient(
+ aSoundResourceLocation.toString(),
+ aTimeUntilNextSound,
+ aSoundStrength,
+ aEntity.posX,
+ aEntity.posY,
+ aEntity.posZ);
+ }
+
+ public static boolean doSoundAtClient(ResourceLocation aSoundResourceLocation, int aTimeUntilNextSound,
+ float aSoundStrength, double aX, double aY, double aZ) {
+ return doSoundAtClient(aSoundResourceLocation, aTimeUntilNextSound, aSoundStrength, 1.01818028F, aX, aY, aZ);
+ }
+
+ /**
+ * @inheritDoc
+ * @deprecated Use {@link #doSoundAtClient(ResourceLocation, int, float, double, double, double)}
+ */
+ @Deprecated
+ public static boolean doSoundAtClient(String aSoundName, int aTimeUntilNextSound, float aSoundStrength, double aX,
+ double aY, double aZ) {
+ if (aSoundName == null) return false;
+ return doSoundAtClient(
+ new ResourceLocation(aSoundName),
+ aTimeUntilNextSound,
+ aSoundStrength,
+ 1.01818028F,
+ aX,
+ aY,
+ aZ);
+ }
+
+ public static boolean doSoundAtClient(SoundResource aSound, int aTimeUntilNextSound, float aSoundStrength,
+ double aX, double aY, double aZ) {
+ return doSoundAtClient(aSound.resourceLocation, aTimeUntilNextSound, aSoundStrength, aX, aY, aZ);
+ }
+
+ public static boolean doSoundAtClient(SoundResource aSound, int aTimeUntilNextSound, float aSoundStrength,
+ float aSoundModulation, double aX, double aY, double aZ) {
+ return doSoundAtClient(
+ aSound.resourceLocation,
+ aTimeUntilNextSound,
+ aSoundStrength,
+ aSoundModulation,
+ aX,
+ aY,
+ aZ);
+ }
+
+ public static boolean doSoundAtClient(ResourceLocation aSoundResourceLocation, int aTimeUntilNextSound,
+ float aSoundStrength, float aSoundModulation, double aX, double aY, double aZ) {
+ if (!FMLCommonHandler.instance()
+ .getEffectiveSide()
+ .isClient() || GT.getThePlayer() == null || !GT.getThePlayer().worldObj.isRemote) return false;
+ if (GregTechAPI.sMultiThreadedSounds) new Thread(
+ new RunnableSound(
+ GT.getThePlayer().worldObj,
+ aX,
+ aY,
+ aZ,
+ aTimeUntilNextSound,
+ aSoundResourceLocation,
+ aSoundStrength,
+ aSoundModulation),
+ "Sound Effect").start();
+ else new RunnableSound(
+ GT.getThePlayer().worldObj,
+ aX,
+ aY,
+ aZ,
+ aTimeUntilNextSound,
+ aSoundResourceLocation,
+ aSoundStrength,
+ aSoundModulation).run();
+ return true;
+ }
+
+ public static boolean sendSoundToPlayers(World aWorld, String aSoundName, float aSoundStrength,
+ float aSoundModulation, int aX, int aY, int aZ) {
+ if (isStringInvalid(aSoundName) || aWorld == null || aWorld.isRemote) return false;
+ NW.sendPacketToAllPlayersInRange(
+ aWorld,
+ new GTPacketSound(aSoundName, aSoundStrength, aSoundModulation, aX, (short) aY, aZ),
+ aX,
+ aZ);
+ return true;
+ }
+
+ public static boolean sendSoundToPlayers(World aWorld, SoundResource sound, float aSoundStrength,
+ float aSoundModulation, int aX, int aY, int aZ) {
+ if (aWorld == null || aWorld.isRemote) return false;
+ NW.sendPacketToAllPlayersInRange(
+ aWorld,
+ new GTPacketSound(sound.resourceLocation.toString(), aSoundStrength, aSoundModulation, aX, (short) aY, aZ),
+ aX,
+ aZ);
+ return true;
+ }
+
+ public static int stackToInt(ItemStack aStack) {
+ if (isStackInvalid(aStack)) return 0;
+ return itemToInt(aStack.getItem(), Items.feather.getDamage(aStack));
+ }
+
+ public static int itemToInt(Item aItem, int aMeta) {
+ return Item.getIdFromItem(aItem) | (aMeta << 16);
+ }
+
+ public static int stackToWildcard(ItemStack aStack) {
+ if (isStackInvalid(aStack)) return 0;
+ return Item.getIdFromItem(aStack.getItem()) | (W << 16);
+ }
+
+ public static ItemStack intToStack(int aStack) {
+ int tID = aStack & (~0 >>> 16), tMeta = aStack >>> 16;
+ Item tItem = Item.getItemById(tID);
+ if (tItem != null) return new ItemStack(tItem, 1, tMeta);
+ return null;
+ }
+
+ public static Integer[] stacksToIntegerArray(ItemStack... aStacks) {
+ Integer[] rArray = new Integer[aStacks.length];
+ for (int i = 0; i < rArray.length; i++) {
+ rArray[i] = stackToInt(aStacks[i]);
+ }
+ return rArray;
+ }
+
+ public static int[] stacksToIntArray(ItemStack... aStacks) {
+ int[] rArray = new int[aStacks.length];
+ for (int i = 0; i < rArray.length; i++) {
+ rArray[i] = stackToInt(aStacks[i]);
+ }
+ return rArray;
+ }
+
+ public static boolean arrayContains(Object aObject, Object... aObjects) {
+ return listContains(aObject, Arrays.asList(aObjects));
+ }
+
+ public static boolean listContains(Object aObject, Collection<?> aObjects) {
+ if (aObjects == null) return false;
+ return aObjects.contains(aObject);
+ }
+
+ @SafeVarargs
+ public static <T> boolean arrayContainsNonNull(T... aArray) {
+ if (aArray != null) for (Object tObject : aArray) if (tObject != null) return true;
+ return false;
+ }
+
+ /**
+ * Note: use {@link ArrayExt#withoutNulls(Object[], IntFunction)} if you want an array as a result.
+ */
+ @SafeVarargs
+ public static <T> ArrayList<T> getArrayListWithoutNulls(T... aArray) {
+ if (aArray == null) return new ArrayList<>();
+ ArrayList<T> rList = new ArrayList<>(Arrays.asList(aArray));
+ for (int i = 0; i < rList.size(); i++) if (rList.get(i) == null) rList.remove(i--);
+ return rList;
+ }
+
+ /**
+ * Note: use {@link ArrayExt#withoutTrailingNulls(Object[], IntFunction)} if you want an array as a result.
+ */
+ @SafeVarargs
+ public static <T> ArrayList<T> getArrayListWithoutTrailingNulls(T... aArray) {
+ if (aArray == null) return new ArrayList<>();
+ ArrayList<T> rList = new ArrayList<>(Arrays.asList(aArray));
+ for (int i = rList.size() - 1; i >= 0 && rList.get(i) == null;) rList.remove(i--);
+ return rList;
+ }
+
+ public static Block getBlockFromStack(ItemStack itemStack) {
+ if (isStackInvalid(itemStack)) return Blocks.air;
+ return getBlockFromItem(itemStack.getItem());
+ }
+
+ public static Block getBlockFromItem(Item item) {
+ return Block.getBlockFromItem(item);
+ }
+
+ public static boolean isStringValid(Object aString) {
+ return aString != null && !aString.toString()
+ .isEmpty();
+ }
+
+ public static boolean isStringInvalid(Object aString) {
+ return aString == null || aString.toString()
+ .isEmpty();
+ }
+
+ @Deprecated
+ public static boolean isStackValid(Object aStack) {
+ return (aStack instanceof ItemStack stack) && isStackValid(stack);
+ }
+
+ public static boolean isStackValid(ItemStack aStack) {
+ return (aStack != null) && aStack.getItem() != null && aStack.stackSize >= 0;
+ }
+
+ @Deprecated
+ public static boolean isStackInvalid(Object aStack) {
+ return !(aStack instanceof ItemStack stack) || isStackInvalid(stack);
+ }
+
+ public static boolean isStackInvalid(ItemStack aStack) {
+ return aStack == null || aStack.getItem() == null || aStack.stackSize < 0;
+ }
+
+ public static boolean isDebugItem(ItemStack aStack) {
+ return /* ItemList.Armor_Cheat.isStackEqual(aStack, T, T) || */ areStacksEqual(
+ GTModHandler.getIC2Item("debug", 1),
+ aStack,
+ true);
+ }
+
+ public static ItemStack updateItemStack(ItemStack aStack) {
+ if (isStackValid(aStack) && aStack.getItem() instanceof GTGenericItem)
+ ((GTGenericItem) aStack.getItem()).isItemStackUsable(aStack);
+ return aStack;
+ }
+
+ public static boolean isOpaqueBlock(World aWorld, int aX, int aY, int aZ) {
+ return aWorld.getBlock(aX, aY, aZ)
+ .isOpaqueCube();
+ }
+
+ public static boolean isBlockAir(World aWorld, int aX, int aY, int aZ) {
+ return aWorld.getBlock(aX, aY, aZ)
+ .isAir(aWorld, aX, aY, aZ);
+ }
+
+ public static boolean hasBlockHitBox(World aWorld, int aX, int aY, int aZ) {
+ return aWorld.getBlock(aX, aY, aZ)
+ .getCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ) != null;
+ }
+
+ public static void setCoordsOnFire(World aWorld, int aX, int aY, int aZ, boolean aReplaceCenter) {
+ if (aReplaceCenter) if (aWorld.getBlock(aX, aY, aZ)
+ .getCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ) == null) aWorld.setBlock(aX, aY, aZ, Blocks.fire);
+ if (aWorld.getBlock(aX + 1, aY, aZ)
+ .getCollisionBoundingBoxFromPool(aWorld, aX + 1, aY, aZ) == null)
+ aWorld.setBlock(aX + 1, aY, aZ, Blocks.fire);
+ if (aWorld.getBlock(aX - 1, aY, aZ)
+ .getCollisionBoundingBoxFromPool(aWorld, aX - 1, aY, aZ) == null)
+ aWorld.setBlock(aX - 1, aY, aZ, Blocks.fire);
+ if (aWorld.getBlock(aX, aY + 1, aZ)
+ .getCollisionBoundingBoxFromPool(aWorld, aX, aY + 1, aZ) == null)
+ aWorld.setBlock(aX, aY + 1, aZ, Blocks.fire);
+ if (aWorld.getBlock(aX, aY - 1, aZ)
+ .getCollisionBoundingBoxFromPool(aWorld, aX, aY - 1, aZ) == null)
+ aWorld.setBlock(aX, aY - 1, aZ, Blocks.fire);
+ if (aWorld.getBlock(aX, aY, aZ + 1)
+ .getCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ + 1) == null)
+ aWorld.setBlock(aX, aY, aZ + 1, Blocks.fire);
+ if (aWorld.getBlock(aX, aY, aZ - 1)
+ .getCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ - 1) == null)
+ aWorld.setBlock(aX, aY, aZ - 1, Blocks.fire);
+ }
+
+ public static ItemStack getProjectile(SubTag aProjectileType, IInventory aInventory) {
+ if (aInventory != null) for (int i = 0, j = aInventory.getSizeInventory(); i < j; i++) {
+ ItemStack rStack = aInventory.getStackInSlot(i);
+ if (isStackValid(rStack) && rStack.getItem() instanceof IProjectileItem
+ && ((IProjectileItem) rStack.getItem()).hasProjectile(aProjectileType, rStack))
+ return updateItemStack(rStack);
+ }
+ return null;
+ }
+
+ public static void removeNullStacksFromInventory(IInventory aInventory) {
+ if (aInventory != null) for (int i = 0, j = aInventory.getSizeInventory(); i < j; i++) {
+ ItemStack tStack = aInventory.getStackInSlot(i);
+ if (tStack != null && (tStack.stackSize == 0 || tStack.getItem() == null))
+ aInventory.setInventorySlotContents(i, null);
+ }
+ }
+
+ /**
+ * Initializes new empty texture page for casings page 0 is old CASING_BLOCKS
+ * <p>
+ * Then casings should be registered like this: for (byte i = MIN_USED_META; i < MAX_USED_META; i = (byte) (i + 1))
+ * { Textures.BlockIcons.casingTexturePages[PAGE][i+START_INDEX] = new GT_CopiedBlockTexture(this, 6, i); }
+ *
+ * @param page 0 to 127
+ * @return true if it made empty page, false if one already existed...
+ */
+ public static boolean addTexturePage(byte page) {
+ if (Textures.BlockIcons.casingTexturePages[page] == null) {
+ Textures.BlockIcons.casingTexturePages[page] = new ITexture[128];
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Return texture id from page and index, for use when determining hatches, but can also be precomputed from:
+ * (page<<7)+index
+ *
+ * @param page 0 to 127 page
+ * @param index 0 to 127 texture index
+ * @return casing texture 0 to 16383
+ */
+ public static int getTextureId(byte page, byte index) {
+ if (page >= 0 && index >= 0) {
+ return (page << 7) + index;
+ }
+ throw new RuntimeException("Index out of range: [" + page + "][" + index + "]");
+ }
+
+ /**
+ * Return texture id from page and index, for use when determining hatches, but can also be precomputed from:
+ * (page<<7)+index
+ *
+ * @param page 0 to 127 page
+ * @param startIndex 0 to 127 start texture index
+ * @param blockMeta meta of the block
+ * @return casing texture 0 to 16383
+ */
+ public static int getTextureId(byte page, byte startIndex, byte blockMeta) {
+ if (page >= 0 && startIndex >= 0 && blockMeta >= 0 && (startIndex + blockMeta) <= 127) {
+ return (page << 7) + (startIndex + blockMeta);
+ }
+ throw new RuntimeException(
+ "Index out of range: [" + page
+ + "]["
+ + startIndex
+ + "+"
+ + blockMeta
+ + "="
+ + (startIndex + blockMeta)
+ + "]");
+ }
+
+ /**
+ * Return texture id from item stack, unoptimized but readable?
+ *
+ * @return casing texture 0 to 16383
+ */
+ public static int getTextureId(Block blockFromBlock, byte metaFromBlock) {
+ for (int page = 0; page < Textures.BlockIcons.casingTexturePages.length; page++) {
+ ITexture[] casingTexturePage = Textures.BlockIcons.casingTexturePages[page];
+ if (casingTexturePage != null) {
+ for (int index = 0; index < casingTexturePage.length; index++) {
+ ITexture iTexture = casingTexturePage[index];
+ if (iTexture instanceof IBlockContainer) {
+ Block block = ((IBlockContainer) iTexture).getBlock();
+ byte meta = ((IBlockContainer) iTexture).getMeta();
+ if (meta == metaFromBlock && blockFromBlock == block) {
+ return (page << 7) + index;
+ }
+ }
+ }
+ }
+ }
+ throw new RuntimeException(
+ "Probably missing mapping or different texture class used for: " + blockFromBlock.getUnlocalizedName()
+ + " meta:"
+ + metaFromBlock);
+ }
+
+ /**
+ * Converts a Number to a String
+ */
+ public static String parseNumberToString(int aNumber) {
+ boolean temp = true, negative = false;
+
+ if (aNumber < 0) {
+ aNumber *= -1;
+ negative = true;
+ }
+
+ StringBuilder tStringB = new StringBuilder();
+ for (int i = 1000000000; i > 0; i /= 10) {
+ int tDigit = (aNumber / i) % 10;
+ if (temp && tDigit != 0) temp = false;
+ if (!temp) {
+ tStringB.append(tDigit);
+ if (i != 1) for (int j = i; j > 0; j /= 1000) if (j == 1) tStringB.append(",");
+ }
+ }
+
+ String tString = tStringB.toString();
+
+ if (tString.equals(E)) tString = "0";
+
+ return negative ? "-" + tString : tString;
+ }
+
+ public static NBTTagCompound getNBTContainingBoolean(NBTTagCompound aNBT, Object aTag, boolean aValue) {
+ if (aNBT == null) aNBT = new NBTTagCompound();
+ aNBT.setBoolean(aTag.toString(), aValue);
+ return aNBT;
+ }
+
+ public static NBTTagCompound getNBTContainingByte(NBTTagCompound aNBT, Object aTag, byte aValue) {
+ if (aNBT == null) aNBT = new NBTTagCompound();
+ aNBT.setByte(aTag.toString(), aValue);
+ return aNBT;
+ }
+
+ public static NBTTagCompound getNBTContainingShort(NBTTagCompound aNBT, Object aTag, short aValue) {
+ if (aNBT == null) aNBT = new NBTTagCompound();
+ aNBT.setShort(aTag.toString(), aValue);
+ return aNBT;
+ }
+
+ public static NBTTagCompound getNBTContainingInteger(NBTTagCompound aNBT, Object aTag, int aValue) {
+ if (aNBT == null) aNBT = new NBTTagCompound();
+ aNBT.setInteger(aTag.toString(), aValue);
+ return aNBT;
+ }
+
+ public static NBTTagCompound getNBTContainingFloat(NBTTagCompound aNBT, Object aTag, float aValue) {
+ if (aNBT == null) aNBT = new NBTTagCompound();
+ aNBT.setFloat(aTag.toString(), aValue);
+ return aNBT;
+ }
+
+ public static NBTTagCompound getNBTContainingDouble(NBTTagCompound aNBT, Object aTag, double aValue) {
+ if (aNBT == null) aNBT = new NBTTagCompound();
+ aNBT.setDouble(aTag.toString(), aValue);
+ return aNBT;
+ }
+
+ public static NBTTagCompound getNBTContainingString(NBTTagCompound aNBT, Object aTag, Object aValue) {
+ if (aNBT == null) aNBT = new NBTTagCompound();
+ if (aValue == null) return aNBT;
+ aNBT.setString(aTag.toString(), aValue.toString());
+ return aNBT;
+ }
+
+ public static boolean isWearingFullFrostHazmat(EntityLivingBase aEntity) {
+ for (byte i = 1; i < 5; i++) {
+ ItemStack tStack = aEntity.getEquipmentInSlot(i);
+
+ if (!isStackInList(tStack, GregTechAPI.sFrostHazmatList) && !hasHazmatEnchant(tStack)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isWearingFullHeatHazmat(EntityLivingBase aEntity) {
+ for (byte i = 1; i < 5; i++) {
+ ItemStack tStack = aEntity.getEquipmentInSlot(i);
+
+ if (!isStackInList(tStack, GregTechAPI.sHeatHazmatList) && !hasHazmatEnchant(tStack)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean isWearingFullBioHazmat(EntityLivingBase aEntity) {
+ for (byte i = 1; i < 5; i++) {
+ ItemStack tStack = aEntity.getEquipmentInSlot(i);
+
+ if (!isStackInList(tStack, GregTechAPI.sBioHazmatList) && !hasHazmatEnchant(tStack)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isWearingFullRadioHazmat(EntityLivingBase aEntity) {
+ for (byte i = 1; i < 5; i++) {
+ ItemStack tStack = aEntity.getEquipmentInSlot(i);
+
+ if (!isStackInList(tStack, GregTechAPI.sRadioHazmatList) && !hasHazmatEnchant(tStack)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isWearingFullElectroHazmat(EntityLivingBase aEntity) {
+ for (byte i = 1; i < 5; i++) {
+ ItemStack tStack = aEntity.getEquipmentInSlot(i);
+
+ if (!isStackInList(tStack, GregTechAPI.sElectroHazmatList) && !hasHazmatEnchant(tStack)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isWearingFullGasHazmat(EntityLivingBase aEntity) {
+ for (byte i = 1; i < 5; i++) {
+ ItemStack tStack = aEntity.getEquipmentInSlot(i);
+
+ if (!isStackInList(tStack, GregTechAPI.sGasHazmatList) && !hasHazmatEnchant(tStack)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean hasHazmatEnchant(ItemStack aStack) {
+ if (aStack == null) return false;
+ Map<Integer, Integer> tEnchantments = EnchantmentHelper.getEnchantments(aStack);
+ Integer tLevel = tEnchantments.get(EnchantmentHazmat.INSTANCE.effectId);
+
+ return tLevel != null && tLevel >= 1;
+ }
+
+ public static float getHeatDamageFromItem(ItemStack aStack) {
+ ItemData tData = GTOreDictUnificator.getItemData(aStack);
+ return tData == null ? 0
+ : (tData.mPrefix == null ? 0 : tData.mPrefix.mHeatDamage)
+ + (tData.hasValidMaterialData() ? tData.mMaterial.mMaterial.mHeatDamage : 0);
+ }
+
+ public static int getRadioactivityLevel(ItemStack aStack) {
+ ItemData tData = GTOreDictUnificator.getItemData(aStack);
+ if (tData != null && tData.hasValidMaterialData()) {
+ if (tData.mMaterial.mMaterial.mEnchantmentArmors instanceof EnchantmentRadioactivity)
+ return tData.mMaterial.mMaterial.mEnchantmentArmorsLevel;
+ if (tData.mMaterial.mMaterial.mEnchantmentTools instanceof EnchantmentRadioactivity)
+ return tData.mMaterial.mMaterial.mEnchantmentToolsLevel;
+ }
+ return EnchantmentHelper.getEnchantmentLevel(EnchantmentRadioactivity.INSTANCE.effectId, aStack);
+ }
+
+ public static boolean isImmuneToBreathingGasses(EntityLivingBase aEntity) {
+ return isWearingFullGasHazmat(aEntity);
+ }
+
+ public static boolean applyHeatDamage(EntityLivingBase entity, float damage) {
+ return applyHeatDamage(entity, damage, GTDamageSources.getHeatDamage());
+ }
+
+ public static boolean applyHeatDamageFromItem(EntityLivingBase entity, float damage, ItemStack item) {
+ return applyHeatDamage(entity, damage, new DamageSourceHotItem(item));
+ }
+
+ private static boolean applyHeatDamage(EntityLivingBase aEntity, float aDamage, DamageSource source) {
+ if (aDamage > 0 && aEntity != null && !isWearingFullHeatHazmat(aEntity)) {
+ return aEntity.attackEntityFrom(source, aDamage);
+ }
+ return false;
+ }
+
+ public static boolean applyFrostDamage(EntityLivingBase aEntity, float aDamage) {
+ if (aDamage > 0 && aEntity != null && !isWearingFullFrostHazmat(aEntity)) {
+ return aEntity.attackEntityFrom(GTDamageSources.getFrostDamage(), aDamage);
+ }
+ return false;
+ }
+
+ public static boolean applyElectricityDamage(EntityLivingBase aEntity, long aVoltage, long aAmperage) {
+ long aDamage = getTier(aVoltage) * aAmperage * 4;
+ if (aDamage > 0 && aEntity != null && !isWearingFullElectroHazmat(aEntity)) {
+ return aEntity.attackEntityFrom(GTDamageSources.getElectricDamage(), aDamage);
+ }
+ return false;
+ }
+
+ public static boolean applyRadioactivity(EntityLivingBase aEntity, int aLevel, int aAmountOfItems) {
+ if (aLevel > 0 && aEntity != null
+ && aEntity.getCreatureAttribute() != EnumCreatureAttribute.UNDEAD
+ && aEntity.getCreatureAttribute() != EnumCreatureAttribute.ARTHROPOD
+ && !isWearingFullRadioHazmat(aEntity)) {
+ PotionEffect tEffect = null;
+ aEntity.addPotionEffect(
+ new PotionEffect(
+ Potion.moveSlowdown.id,
+ aLevel * 140 * aAmountOfItems + Math.max(
+ 0,
+ ((tEffect = aEntity.getActivePotionEffect(Potion.moveSlowdown)) == null ? 0
+ : tEffect.getDuration())),
+ Math.max(0, (5 * aLevel) / 7)));
+ aEntity.addPotionEffect(
+ new PotionEffect(
+ Potion.digSlowdown.id,
+ aLevel * 150 * aAmountOfItems + Math.max(
+ 0,
+ ((tEffect = aEntity.getActivePotionEffect(Potion.digSlowdown)) == null ? 0
+ : tEffect.getDuration())),
+ Math.max(0, (5 * aLevel) / 7)));
+ aEntity.addPotionEffect(
+ new PotionEffect(
+ Potion.confusion.id,
+ aLevel * 130 * aAmountOfItems + Math.max(
+ 0,
+ ((tEffect = aEntity.getActivePotionEffect(Potion.confusion)) == null ? 0
+ : tEffect.getDuration())),
+ Math.max(0, (5 * aLevel) / 7)));
+ aEntity.addPotionEffect(
+ new PotionEffect(
+ Potion.weakness.id,
+ aLevel * 150 * aAmountOfItems + Math.max(
+ 0,
+ ((tEffect = aEntity.getActivePotionEffect(Potion.weakness)) == null ? 0
+ : tEffect.getDuration())),
+ Math.max(0, (5 * aLevel) / 7)));
+ aEntity.addPotionEffect(
+ new PotionEffect(
+ Potion.hunger.id,
+ aLevel * 130 * aAmountOfItems + Math.max(
+ 0,
+ ((tEffect = aEntity.getActivePotionEffect(Potion.hunger)) == null ? 0 : tEffect.getDuration())),
+ Math.max(0, (5 * aLevel) / 7)));
+ aEntity.addPotionEffect(
+ new PotionEffect(
+ 24 /* IC2 Radiation */,
+ aLevel * 180 * aAmountOfItems + Math.max(
+ 0,
+ ((tEffect = aEntity.getActivePotionEffect(Potion.potionTypes[24])) == null ? 0
+ : tEffect.getDuration())),
+ Math.max(0, (5 * aLevel) / 7)));
+ return true;
+ }
+ return false;
+ }
+
+ public static ItemStack setStack(ItemStack aSetStack, ItemStack aToStack) {
+ if (isStackInvalid(aSetStack) || isStackInvalid(aToStack)) return null;
+ aSetStack.func_150996_a(aToStack.getItem());
+ aSetStack.stackSize = aToStack.stackSize;
+ Items.feather.setDamage(aSetStack, Items.feather.getDamage(aToStack));
+ aSetStack.setTagCompound(aToStack.getTagCompound());
+ return aSetStack;
+ }
+
+ public static FluidStack[] copyFluidArray(FluidStack... aStacks) {
+ if (aStacks == null) return null;
+ FluidStack[] rStacks = new FluidStack[aStacks.length];
+ for (int i = 0; i < aStacks.length; i++) if (aStacks[i] != null) rStacks[i] = aStacks[i].copy();
+ return rStacks;
+ }
+
+ public static ItemStack[] copyItemArray(ItemStack... aStacks) {
+ if (aStacks == null) return null;
+ ItemStack[] rStacks = new ItemStack[aStacks.length];
+ for (int i = 0; i < aStacks.length; i++) rStacks[i] = copy(aStacks[i]);
+ return rStacks;
+ }
+
+ /**
+ * @deprecated use {@link #copy(ItemStack)} instead
+ */
+ @Deprecated
+ public static ItemStack copy(Object... aStacks) {
+ for (Object tStack : aStacks) if (isStackValid(tStack)) return ((ItemStack) tStack).copy();
+ return null;
+ }
+
+ public static ItemStack copy(ItemStack aStack) {
+ if (isStackValid(aStack)) return aStack.copy();
+ return null;
+ }
+
+ @Deprecated
+ private static ItemStack firstStackOrNull(Object... aStacks) {
+ for (Object tStack : aStacks) if (tStack instanceof ItemStack stack) return stack;
+ return null;
+ }
+
+ @Nullable
+ public static ItemStack copyOrNull(@Nullable ItemStack stack) {
+ if (isStackValid(stack)) return stack.copy();
+ return null;
+ }
+
+ public static FluidStack copyAmount(int aAmount, FluidStack aStack) {
+ if (aStack == null) return null;
+ FluidStack rStack = aStack.copy();
+ rStack.amount = aAmount;
+ return rStack;
+ }
+
+ /**
+ * @deprecated use {@link #copyAmount(int, ItemStack)} instead
+ */
+ @Deprecated
+ public static ItemStack copyAmount(long aAmount, Object... aStacks) {
+ return copyAmount(aAmount, firstStackOrNull(aStacks));
+ }
+
+ /**
+ * @deprecated use {@link #copyAmount(int, ItemStack)} instead
+ */
+ @Deprecated
+ public static ItemStack copyAmount(long aAmount, ItemStack aStack) {
+ return copyAmount((int) aAmount, aStack);
+ }
+
+ public static ItemStack copyAmount(int aAmount, ItemStack aStack) {
+ ItemStack rStack = copy(aStack);
+ if (isStackInvalid(rStack)) return null;
+ if (aAmount > 64) aAmount = 64;
+ else if (aAmount == -1) aAmount = 111;
+ else if (aAmount < 0) aAmount = 0;
+ rStack.stackSize = (byte) aAmount;
+ return rStack;
+ }
+
+ public static ItemStack multiplyStack(int aMultiplier, ItemStack aStack) {
+ ItemStack rStack = copy(aStack);
+ if (isStackInvalid(rStack)) return null;
+ int tAmount = rStack.stackSize * aMultiplier;
+ if (tAmount > 64) tAmount = 64;
+ else if (tAmount == -1) tAmount = 111;
+ else if (tAmount < 0) tAmount = 0;
+ rStack.stackSize = (byte) tAmount;
+ return rStack;
+ }
+
+ /**
+ * Unlike {@link #copyAmount(int, ItemStack)}, this method does not restrict stack size by 64.
+ */
+ public static ItemStack copyAmountUnsafe(int aAmount, ItemStack aStack) {
+ ItemStack rStack = copy(aStack);
+ if (isStackInvalid(rStack)) return null;
+ else if (aAmount < 0) aAmount = 0;
+ rStack.stackSize = (int) aAmount;
+ return rStack;
+ }
+
+ public static ItemStack copyMetaData(int aMetaData, ItemStack aStack) {
+ ItemStack rStack = copy(aStack);
+ if (isStackInvalid(rStack)) return null;
+ Items.feather.setDamage(rStack, aMetaData);
+ return rStack;
+ }
+
+ /**
+ * @deprecated use {@link #copyAmountAndMetaData(int, int, ItemStack)} instead
+ */
+ @Deprecated
+ public static ItemStack copyAmountAndMetaData(long aAmount, long aMetaData, ItemStack aStack) {
+ return copyAmountAndMetaData((int) aAmount, (int) aMetaData, aStack);
+ }
+
+ public static ItemStack copyAmountAndMetaData(int aAmount, int aMetaData, ItemStack aStack) {
+ ItemStack rStack = copyAmount(aAmount, aStack);
+ if (isStackInvalid(rStack)) return null;
+ Items.feather.setDamage(rStack, aMetaData);
+ return rStack;
+ }
+
+ /**
+ * returns a copy of an ItemStack with its Stacksize being multiplied by aMultiplier
+ */
+ public static ItemStack mul(int aMultiplier, ItemStack aStack) {
+ ItemStack rStack = copy(aStack);
+ if (rStack == null) return null;
+ rStack.stackSize *= aMultiplier;
+ return rStack;
+ }
+
+ /**
+ * Loads an ItemStack properly.
+ */
+ public static ItemStack loadItem(NBTTagCompound aNBT, String aTagName) {
+ return loadItem(aNBT.getCompoundTag(aTagName));
+ }
+
+ public static FluidStack loadFluid(NBTTagCompound aNBT, String aTagName) {
+ return loadFluid(aNBT.getCompoundTag(aTagName));
+ }
+
+ /**
+ * Loads an ItemStack properly.
+ */
+ public static ItemStack loadItem(NBTTagCompound aNBT) {
+ if (aNBT == null) return null;
+ ItemStack tRawStack = ItemStack.loadItemStackFromNBT(aNBT);
+ int tRealStackSize = 0;
+ if (tRawStack != null && aNBT.hasKey("Count", Constants.NBT.TAG_INT)) {
+ tRealStackSize = aNBT.getInteger("Count");
+ tRawStack.stackSize = tRealStackSize;
+ } else if (tRawStack != null) {
+ tRealStackSize = tRawStack.stackSize;
+ }
+ ItemStack tRet = GTOreDictUnificator.get(true, tRawStack);
+ if (tRet != null) tRet.stackSize = tRealStackSize;
+ return tRet;
+ }
+
+ public static void saveItem(NBTTagCompound aParentTag, String aTagName, ItemStack aStack) {
+ if (aStack != null) aParentTag.setTag(aTagName, saveItem(aStack));
+ }
+
+ public static NBTTagCompound saveItem(ItemStack aStack) {
+ if (aStack == null) return new NBTTagCompound();
+ NBTTagCompound t = new NBTTagCompound();
+ aStack.writeToNBT(t);
+ if (aStack.stackSize > Byte.MAX_VALUE) t.setInteger("Count", aStack.stackSize);
+ return t;
+ }
+
+ /**
+ * Loads an FluidStack properly.
+ */
+ public static FluidStack loadFluid(NBTTagCompound aNBT) {
+ if (aNBT == null) return null;
+ return FluidStack.loadFluidStackFromNBT(aNBT);
+ }
+
+ public static <E> E selectItemInList(int aIndex, E aReplacement, List<E> aList) {
+ if (aList == null || aList.isEmpty()) return aReplacement;
+ if (aList.size() <= aIndex) return aList.get(aList.size() - 1);
+ if (aIndex < 0) return aList.get(0);
+ return aList.get(aIndex);
+ }
+
+ @SafeVarargs
+ public static <E> E selectItemInList(int aIndex, E aReplacement, E... aList) {
+ if (aList == null || aList.length == 0) return aReplacement;
+ if (aList.length <= aIndex) return aList[aList.length - 1];
+ if (aIndex < 0) return aList[0];
+ return aList[aIndex];
+ }
+
+ public static boolean isStackInStackSet(ItemStack aStack, Set<ItemStack> aSet) {
+ if (aStack == null) return false;
+ if (aSet.contains(aStack)) return true;
+
+ return aSet.contains(GTItemStack.internalCopyStack(aStack, true));
+ }
+
+ public static boolean isStackInList(ItemStack aStack, Collection<GTItemStack> aList) {
+ if (aStack == null) {
+ return false;
+ }
+ return isStackInList(new GTItemStack(aStack), aList);
+ }
+
+ public static boolean isStackInList(ItemStack aStack, Set<GTItemStack2> aList) {
+ if (aStack == null) {
+ return false;
+ }
+ return isStackInList(new GTItemStack2(aStack), aList);
+ }
+
+ public static boolean isStackInList(GTItemStack aStack, Collection<GTItemStack> aList) {
+ return aStack != null
+ && (aList.contains(aStack) || aList.contains(new GTItemStack(aStack.mItem, aStack.mStackSize, W)));
+ }
+
+ public static boolean isStackInList(GTItemStack2 aStack, Set<GTItemStack2> aList) {
+ return aStack != null
+ && (aList.contains(aStack) || aList.contains(new GTItemStack2(aStack.mItem, aStack.mStackSize, W)));
+ }
+
+ /**
+ * re-maps all Keys of a Map after the Keys were weakened.
+ */
+ public static <X, Y> Map<X, Y> reMap(Map<X, Y> aMap) {
+ Map<X, Y> tMap = null;
+ // We try to clone the Map first (most Maps are Cloneable) in order to retain as much state of the Map as
+ // possible when rehashing. For example, "Custom" HashMaps from fastutil may have a custom hash function which
+ // would not be used to rehash if we just create a new HashMap.
+ if (aMap instanceof Cloneable) {
+ try {
+ tMap = (Map<X, Y>) aMap.getClass()
+ .getMethod("clone")
+ .invoke(aMap);
+ } catch (Throwable e) {
+ GTLog.err.println("Failed to clone Map of type " + aMap.getClass());
+ e.printStackTrace(GTLog.err);
+ }
+ }
+
+ if (tMap == null) {
+ tMap = new HashMap<>(aMap);
+ }
+
+ aMap.clear();
+ aMap.putAll(tMap);
+ return aMap;
+ }
+
+ /**
+ * re-maps all Keys of a Map after the Keys were weakened.
+ */
+ public static <X, Y> SetMultimap<X, Y> reMap(SetMultimap<X, Y> aMap) {
+ @SuppressWarnings("unchecked")
+ Map.Entry<X, Y>[] entries = aMap.entries()
+ .toArray(new Map.Entry[0]);
+ aMap.clear();
+ for (Map.Entry<X, Y> entry : entries) {
+ aMap.put(entry.getKey(), entry.getValue());
+ }
+ return aMap;
+ }
+
+ public static <X, Y extends Comparable<Y>> LinkedHashMap<X, Y> sortMapByValuesAcending(Map<X, Y> map) {
+ return map.entrySet()
+ .stream()
+ .sorted(Map.Entry.comparingByValue())
+ .collect(CollectorUtils.entriesToMap(LinkedHashMap::new));
+ }
+
+ /**
+ * Why the fuck do neither Java nor Guava have a Function to do this?
+ */
+ public static <X, Y extends Comparable<Y>> LinkedHashMap<X, Y> sortMapByValuesDescending(Map<X, Y> aMap) {
+ List<Map.Entry<X, Y>> tEntrySet = new LinkedList<>(aMap.entrySet());
+ tEntrySet.sort((aValue1, aValue2) -> {
+ return aValue2.getValue()
+ .compareTo(aValue1.getValue()); // FB: RV - RV_NEGATING_RESULT_OF_COMPARETO
+ });
+ LinkedHashMap<X, Y> rMap = new LinkedHashMap<>();
+ for (Map.Entry<X, Y> tEntry : tEntrySet) rMap.put(tEntry.getKey(), tEntry.getValue());
+ return rMap;
+ }
+
+ /**
+ * Translates a Material Amount into an Amount of Fluid in Fluid Material Units.
+ */
+ public static long translateMaterialToFluidAmount(long aMaterialAmount, boolean aRoundUp) {
+ return translateMaterialToAmount(aMaterialAmount, L, aRoundUp);
+ }
+
+ /**
+ * Translates a Material Amount into an Amount of Fluid. Second Parameter for things like Bucket Amounts (1000) and
+ * similar
+ */
+ public static long translateMaterialToAmount(long aMaterialAmount, long aAmountPerUnit, boolean aRoundUp) {
+ return Math.max(
+ 0,
+ ((aMaterialAmount * aAmountPerUnit) / M)
+ + (aRoundUp && (aMaterialAmount * aAmountPerUnit) % M > 0 ? 1 : 0));
+ }
+
+ /**
+ * This checks if the Dimension is really a Dimension and not another Planet or something. Used for my Teleporter.
+ */
+ public static boolean isRealDimension(int aDimensionID) {
+ if (aDimensionID <= 1 && aDimensionID >= -1 && !GregTechAPI.sDimensionalList.contains(aDimensionID))
+ return true;
+ return !GregTechAPI.sDimensionalList.contains(aDimensionID)
+ && DimensionManager.isDimensionRegistered(aDimensionID);
+ }
+
+ public static boolean moveEntityToDimensionAtCoords(Entity entity, int aDimension, double aX, double aY,
+ double aZ) {
+ // Credit goes to BrandonCore Author :!:
+
+ if (entity == null || entity.worldObj.isRemote) return false;
+ if (entity.ridingEntity != null) entity.mountEntity(null);
+ if (entity.riddenByEntity != null) entity.riddenByEntity.mountEntity(null);
+
+ World startWorld = entity.worldObj;
+ WorldServer destinationWorld = FMLCommonHandler.instance()
+ .getMinecraftServerInstance()
+ .worldServerForDimension(aDimension);
+
+ if (destinationWorld == null) {
+ return false;
+ }
+
+ boolean interDimensional = startWorld.provider.dimensionId != destinationWorld.provider.dimensionId;
+ if (!interDimensional) return false;
+ startWorld.updateEntityWithOptionalForce(entity, false); // added
+
+ if (entity instanceof EntityPlayerMP player) {
+ player.closeScreen(); // added
+ player.dimension = aDimension;
+ player.playerNetServerHandler.sendPacket(
+ new S07PacketRespawn(
+ player.dimension,
+ player.worldObj.difficultySetting,
+ destinationWorld.getWorldInfo()
+ .getTerrainType(),
+ player.theItemInWorldManager.getGameType()));
+ ((WorldServer) startWorld).getPlayerManager()
+ .removePlayer(player);
+
+ startWorld.playerEntities.remove(player);
+ startWorld.updateAllPlayersSleepingFlag();
+ int i = entity.chunkCoordX;
+ int j = entity.chunkCoordZ;
+ if ((entity.addedToChunk) && (startWorld.getChunkProvider()
+ .chunkExists(i, j))) {
+ startWorld.getChunkFromChunkCoords(i, j)
+ .removeEntity(entity);
+ startWorld.getChunkFromChunkCoords(i, j).isModified = true;
+ }
+ startWorld.loadedEntityList.remove(entity);
+ startWorld.onEntityRemoved(entity);
+ }
+
+ entity.setLocationAndAngles(aX, aY, aZ, entity.rotationYaw, entity.rotationPitch);
+
+ destinationWorld.theChunkProviderServer.loadChunk((int) aX >> 4, (int) aZ >> 4);
+
+ destinationWorld.theProfiler.startSection("placing");
+ if (!(entity instanceof EntityPlayer)) {
+ NBTTagCompound entityNBT = new NBTTagCompound();
+ entity.isDead = false;
+ entityNBT.setString("id", EntityList.getEntityString(entity));
+ entity.writeToNBT(entityNBT);
+ entity.isDead = true;
+ entity = EntityList.createEntityFromNBT(entityNBT, destinationWorld);
+ if (entity == null) {
+ return false;
+ }
+ entity.dimension = destinationWorld.provider.dimensionId;
+ }
+ destinationWorld.spawnEntityInWorld(entity);
+ entity.setWorld(destinationWorld);
+ entity.setLocationAndAngles(aX, aY, aZ, entity.rotationYaw, entity.rotationPitch);
+
+ destinationWorld.updateEntityWithOptionalForce(entity, false);
+ entity.setLocationAndAngles(aX, aY, aZ, entity.rotationYaw, entity.rotationPitch);
+
+ if ((entity instanceof EntityPlayerMP player)) {
+ player.mcServer.getConfigurationManager()
+ .func_72375_a(player, destinationWorld);
+ player.playerNetServerHandler.setPlayerLocation(aX, aY, aZ, player.rotationYaw, player.rotationPitch);
+ }
+
+ destinationWorld.updateEntityWithOptionalForce(entity, false);
+
+ if (entity instanceof EntityPlayerMP player) {
+ player.theItemInWorldManager.setWorld(destinationWorld);
+ player.mcServer.getConfigurationManager()
+ .updateTimeAndWeatherForPlayer(player, destinationWorld);
+ player.mcServer.getConfigurationManager()
+ .syncPlayerInventory(player);
+
+ for (PotionEffect potionEffect : player.getActivePotionEffects()) {
+ player.playerNetServerHandler.sendPacket(new S1DPacketEntityEffect(player.getEntityId(), potionEffect));
+ }
+
+ player.playerNetServerHandler.sendPacket(
+ new S1FPacketSetExperience(player.experience, player.experienceTotal, player.experienceLevel));
+ FMLCommonHandler.instance()
+ .firePlayerChangedDimensionEvent(
+ player,
+ startWorld.provider.dimensionId,
+ destinationWorld.provider.dimensionId);
+ }
+ entity.setLocationAndAngles(aX, aY, aZ, entity.rotationYaw, entity.rotationPitch);
+
+ destinationWorld.theProfiler.endSection();
+ entity.fallDistance = 0;
+ return true;
+ }
+
+ public static int getScaleCoordinates(double aValue, int aScale) {
+ return (int) Math.floor(aValue / aScale);
+ }
+
+ public static int getCoordinateScan(ArrayList<String> aList, EntityPlayer aPlayer, World aWorld, int aScanLevel,
+ int aX, int aY, int aZ, ForgeDirection side, float aClickX, float aClickY, float aClickZ) {
+ if (aList == null) return 0;
+
+ ArrayList<String> tList = new ArrayList<>();
+ int rEUAmount = 0;
+
+ TileEntity tTileEntity = aWorld.getTileEntity(aX, aY, aZ);
+
+ final Block tBlock = aWorld.getBlock(aX, aY, aZ);
+
+ addBaseInfo(aPlayer, aWorld, aX, aY, aZ, tList, tTileEntity, tBlock);
+
+ if (tTileEntity != null) {
+ rEUAmount += addFluidHandlerInfo(side, tList, tTileEntity);
+
+ try {
+ if (tTileEntity instanceof ic2.api.reactor.IReactorChamber chamber) {
+ rEUAmount += 500;
+ // Redirect the rest of the scans to the reactor itself
+ tTileEntity = (TileEntity) chamber.getReactor();
+ }
+ } catch (Throwable e) {
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ rEUAmount += addReactorInfo(tList, tTileEntity);
+ rEUAmount += addAlignmentInfo(tList, tTileEntity);
+ rEUAmount += addIC2WrenchableInfo(aPlayer, tList, tTileEntity);
+ rEUAmount += addIC2EnergyConductorInfo(tList, tTileEntity);
+ rEUAmount += addIC2EnergyStorageInfo(tList, tTileEntity);
+ rEUAmount += addUpgradableMachineInfo(tList, tTileEntity);
+ rEUAmount += addMachineProgressInfo(tList, tTileEntity);
+ rEUAmount += addCoverableInfo(side, tList, tTileEntity);
+ addEnergyContainerInfo(tList, tTileEntity);
+ addOwnerInfo(tList, tTileEntity);
+ addDeviceInfo(tList, tTileEntity);
+
+ rEUAmount += addIC2CropInfo(tList, tTileEntity);
+
+ rEUAmount += addForestryLeavesInfo(tList, tTileEntity);
+ }
+
+ final Chunk currentChunk = aWorld.getChunkFromBlockCoords(aX, aZ);
+ addUndergroundFluidInfo(aPlayer, tList, currentChunk);
+ addPollutionInfo(tList, currentChunk);
+
+ rEUAmount += addDebuggableBlockInfo(aPlayer, aX, aY, aZ, tList, tBlock);
+
+ final BlockScanningEvent tEvent = new BlockScanningEvent(
+ aWorld,
+ aPlayer,
+ aX,
+ aY,
+ aZ,
+ side,
+ aScanLevel,
+ tBlock,
+ tTileEntity,
+ tList,
+ aClickX,
+ aClickY,
+ aClickZ);
+ tEvent.mEUCost = rEUAmount;
+ MinecraftForge.EVENT_BUS.post(tEvent);
+ if (!tEvent.isCanceled()) aList.addAll(tList);
+ return tEvent.mEUCost;
+ }
+
+ private static void addBaseInfo(EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, ArrayList<String> tList,
+ TileEntity tTileEntity, Block tBlock) {
+ tList.add(
+ "----- X: " + EnumChatFormatting.AQUA
+ + formatNumbers(aX)
+ + EnumChatFormatting.RESET
+ + " Y: "
+ + EnumChatFormatting.AQUA
+ + formatNumbers(aY)
+ + EnumChatFormatting.RESET
+ + " Z: "
+ + EnumChatFormatting.AQUA
+ + formatNumbers(aZ)
+ + EnumChatFormatting.RESET
+ + " D: "
+ + EnumChatFormatting.AQUA
+ + aWorld.provider.dimensionId
+ + EnumChatFormatting.RESET
+ + " -----");
+ try {
+ tList.add(
+ GTUtility.trans("162", "Name: ") + EnumChatFormatting.BLUE
+ + ((tTileEntity instanceof IInventory inv) ? inv.getInventoryName() : tBlock.getUnlocalizedName())
+ + EnumChatFormatting.RESET
+ + GTUtility.trans("163", " MetaData: ")
+ + EnumChatFormatting.AQUA
+ + aWorld.getBlockMetadata(aX, aY, aZ)
+ + EnumChatFormatting.RESET);
+ tList.add(
+ GTUtility.trans("164", "Hardness: ") + EnumChatFormatting.YELLOW
+ + tBlock.getBlockHardness(aWorld, aX, aY, aZ)
+ + EnumChatFormatting.RESET
+ + GTUtility.trans("165", " Blast Resistance: ")
+ + EnumChatFormatting.YELLOW
+ + tBlock
+ .getExplosionResistance(aPlayer, aWorld, aX, aY, aZ, aPlayer.posX, aPlayer.posY, aPlayer.posZ)
+ + EnumChatFormatting.RESET);
+ if (tBlock.isBeaconBase(aWorld, aX, aY, aZ, aX, aY + 1, aZ)) tList.add(
+ EnumChatFormatting.GOLD + GTUtility.trans("166", "Is valid Beacon Pyramid Material")
+ + EnumChatFormatting.RESET);
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this block's info.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ }
+
+ private static int addFluidHandlerInfo(ForgeDirection side, ArrayList<String> tList, TileEntity tTileEntity) {
+ int rEUAmount = 0;
+ try {
+ if (tTileEntity instanceof IFluidHandler fluidHandler) {
+ rEUAmount += 500;
+ final FluidTankInfo[] tTanks = fluidHandler.getTankInfo(side);
+ if (tTanks != null) for (byte i = 0; i < tTanks.length; i++) {
+ tList.add(
+ GTUtility.trans("167", "Tank ") + i
+ + ": "
+ + EnumChatFormatting.GREEN
+ + formatNumbers((tTanks[i].fluid == null ? 0 : tTanks[i].fluid.amount))
+ + EnumChatFormatting.RESET
+ + " L / "
+ + EnumChatFormatting.YELLOW
+ + formatNumbers(tTanks[i].capacity)
+ + EnumChatFormatting.RESET
+ + " L "
+ + EnumChatFormatting.GOLD
+ + getFluidName(tTanks[i].fluid, true)
+ + EnumChatFormatting.RESET);
+ }
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this tile's fluid tank info.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ return rEUAmount;
+ }
+
+ private static int addDebuggableBlockInfo(EntityPlayer aPlayer, int aX, int aY, int aZ, ArrayList<String> tList,
+ Block tBlock) {
+ int rEUAmount = 0;
+ try {
+ if (tBlock instanceof IDebugableBlock debugableBlock) {
+ rEUAmount += 500;
+ final ArrayList<String> temp = debugableBlock.getDebugInfo(aPlayer, aX, aY, aZ, 3);
+ if (temp != null) tList.addAll(temp);
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this block's debug info.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ return rEUAmount;
+ }
+
+ private static void addPollutionInfo(ArrayList<String> tList, Chunk currentChunk) {
+ if (Pollution.hasPollution(currentChunk)) {
+ tList.add(
+ GTUtility.trans("202", "Pollution in Chunk: ") + EnumChatFormatting.RED
+ + formatNumbers(Pollution.getPollution(currentChunk))
+ + EnumChatFormatting.RESET
+ + GTUtility.trans("203", " gibbl"));
+ } else {
+ tList.add(
+ EnumChatFormatting.GREEN + GTUtility.trans("204", "No Pollution in Chunk! HAYO!")
+ + EnumChatFormatting.RESET);
+ }
+ }
+
+ private static void addUndergroundFluidInfo(EntityPlayer aPlayer, ArrayList<String> tList, Chunk currentChunk) {
+ if (aPlayer.capabilities.isCreativeMode) {
+ final FluidStack tFluid = undergroundOilReadInformation(currentChunk); // -# to only read
+ if (tFluid != null) tList.add(
+ EnumChatFormatting.GOLD + tFluid.getLocalizedName()
+ + EnumChatFormatting.RESET
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + formatNumbers(tFluid.amount)
+ + EnumChatFormatting.RESET
+ + " L");
+ else tList.add(
+ EnumChatFormatting.GOLD + GTUtility.trans("201", "Nothing")
+ + EnumChatFormatting.RESET
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + '0'
+ + EnumChatFormatting.RESET
+ + " L");
+ }
+ }
+
+ private static int addForestryLeavesInfo(ArrayList<String> tList, TileEntity tTileEntity) {
+ int rEUAmount = 0;
+ try {
+ if (Mods.Forestry.isModLoaded()
+ && tTileEntity instanceof forestry.arboriculture.tiles.TileLeaves tileLeaves) {
+ final forestry.api.arboriculture.ITree tree = tileLeaves.getTree();
+ if (tree != null) {
+ rEUAmount += 1000;
+ if (!tree.isAnalyzed()) tree.analyze();
+ tree.addTooltip(tList);
+ }
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this leaves' info.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ return rEUAmount;
+ }
+
+ private static int addIC2CropInfo(ArrayList<String> tList, TileEntity tTileEntity) {
+ int rEUAmount = 0;
+ try {
+ if (tTileEntity instanceof ic2.api.crops.ICropTile crop) {
+ rEUAmount += 1000;
+ if (crop.getScanLevel() < 4) crop.setScanLevel((byte) 4);
+ if (crop.getCrop() != null) {
+ tList.add(
+ GTUtility.trans("187", "Type -- Crop-Name: ") + crop.getCrop()
+ .name()
+ + GTUtility.trans("188", " Growth: ")
+ + crop.getGrowth()
+ + GTUtility.trans("189", " Gain: ")
+ + crop.getGain()
+ + GTUtility.trans("190", " Resistance: ")
+ + crop.getResistance());
+ }
+ tList.add(
+ GTUtility.trans("191", "Plant -- Fertilizer: ") + crop.getNutrientStorage()
+ + GTUtility.trans("192", " Water: ")
+ + crop.getHydrationStorage()
+ + GTUtility.trans("193", " Weed-Ex: ")
+ + crop.getWeedExStorage()
+ + GTUtility.trans("194", " Scan-Level: ")
+ + crop.getScanLevel());
+ tList.add(
+ GTUtility.trans("195", "Environment -- Nutrients: ") + crop.getNutrients()
+ + GTUtility.trans("196", " Humidity: ")
+ + crop.getHumidity()
+ + GTUtility.trans("197", " Air-Quality: ")
+ + crop.getAirQuality());
+ if (crop.getCrop() != null) {
+ final StringBuilder tStringB = new StringBuilder();
+ for (final String tAttribute : crop.getCrop()
+ .attributes()) {
+ tStringB.append(", ")
+ .append(tAttribute);
+ }
+ final String tString = tStringB.toString();
+ tList.add(GTUtility.trans("198", "Attributes:") + tString.replaceFirst(",", E));
+ tList.add(
+ GTUtility.trans("199", "Discovered by: ") + crop.getCrop()
+ .discoveredBy());
+ }
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this crop's info.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ return rEUAmount;
+ }
+
+ private static void addDeviceInfo(ArrayList<String> tList, TileEntity tTileEntity) {
+ try {
+ if (tTileEntity instanceof IGregTechDeviceInformation info && info.isGivingInformation()) {
+ tList.addAll(Arrays.asList(info.getInfoData()));
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this device's info.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ }
+
+ private static void addOwnerInfo(ArrayList<String> tList, TileEntity tTileEntity) {
+ try {
+ if (tTileEntity instanceof IGregTechTileEntity gtTE) {
+ tList.add(
+ GTUtility.trans("186", "Owned by: ") + EnumChatFormatting.BLUE
+ + gtTE.getOwnerName()
+ + EnumChatFormatting.RESET);
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this device's owner.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ }
+
+ private static void addEnergyContainerInfo(ArrayList<String> tList, TileEntity tTileEntity) {
+ try {
+ if (tTileEntity instanceof IBasicEnergyContainer energyContainer && energyContainer.getEUCapacity() > 0) {
+ tList.add(
+ GTUtility.trans("179", "Max IN: ") + EnumChatFormatting.RED
+ + formatNumbers(energyContainer.getInputVoltage())
+ + " ("
+ + GTValues.VN[getTier(energyContainer.getInputVoltage())]
+ + ") "
+ + EnumChatFormatting.RESET
+ + GTUtility.trans("182", " EU at ")
+ + EnumChatFormatting.RED
+ + formatNumbers(energyContainer.getInputAmperage())
+ + EnumChatFormatting.RESET
+ + GTUtility.trans("183", " A"));
+ tList.add(
+ GTUtility.trans("181", "Max OUT: ") + EnumChatFormatting.RED
+ + formatNumbers(energyContainer.getOutputVoltage())
+ + " ("
+ + GTValues.VN[getTier(energyContainer.getOutputVoltage())]
+ + ") "
+ + EnumChatFormatting.RESET
+ + GTUtility.trans("182", " EU at ")
+ + EnumChatFormatting.RED
+ + formatNumbers(energyContainer.getOutputAmperage())
+ + EnumChatFormatting.RESET
+ + GTUtility.trans("183", " A"));
+ tList.add(
+ GTUtility.trans("184", "Energy: ") + EnumChatFormatting.GREEN
+ + formatNumbers(energyContainer.getStoredEU())
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + EnumChatFormatting.YELLOW
+ + formatNumbers(energyContainer.getEUCapacity())
+ + EnumChatFormatting.RESET
+ + " EU");
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this device's energy info.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ }
+
+ private static int addCoverableInfo(ForgeDirection side, ArrayList<String> tList, TileEntity tTileEntity) {
+ int rEUAmount = 0;
+ try {
+ if (tTileEntity instanceof ICoverable coverable) {
+ rEUAmount += 300;
+ final String tString = coverable.getCoverInfoAtSide(side)
+ .getBehaviorDescription();
+ if (tString != null && !tString.equals(E)) tList.add(tString);
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this device's covers.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ return rEUAmount;
+ }
+
+ private static int addMachineProgressInfo(ArrayList<String> tList, TileEntity tTileEntity) {
+ int rEUAmount = 0;
+ try {
+ if (tTileEntity instanceof IMachineProgress progress) {
+ if (progress.isAllowedToWork() && !progress.hasThingsToDo()) {
+ tList.add(EnumChatFormatting.RED + "Disabled." + EnumChatFormatting.RESET);
+ }
+ if (progress.wasShutdown() && isStringValid(
+ progress.getLastShutDownReason()
+ .getDisplayString())) {
+ tList.add(
+ progress.getLastShutDownReason()
+ .getDisplayString());
+ }
+ rEUAmount += 400;
+ int tValue = 0;
+ if (0 < (tValue = progress.getMaxProgress())) tList.add(
+ GTUtility.trans("178", "Progress/Load: ") + EnumChatFormatting.GREEN
+ + formatNumbers(progress.getProgress())
+ + EnumChatFormatting.RESET
+ + " / "
+ + EnumChatFormatting.YELLOW
+ + formatNumbers(tValue)
+ + EnumChatFormatting.RESET);
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this device's progress.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ return rEUAmount;
+ }
+
+ private static int addUpgradableMachineInfo(ArrayList<String> tList, TileEntity tTileEntity) {
+ int rEUAmount = 0;
+ try {
+ if (tTileEntity instanceof IUpgradableMachine upgradableMachine) {
+ rEUAmount += 500;
+ if (upgradableMachine.hasMufflerUpgrade()) tList.add(
+ EnumChatFormatting.GREEN + GTUtility.trans("177", "Has Muffler Upgrade")
+ + EnumChatFormatting.RESET);
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this device's upgrades.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ return rEUAmount;
+ }
+
+ private static int addIC2EnergyStorageInfo(ArrayList<String> tList, TileEntity tTileEntity) {
+ int rEUAmount = 0;
+ try {
+ if (tTileEntity instanceof ic2.api.tile.IEnergyStorage storage) {
+ rEUAmount += 200;
+ tList.add(
+ GTUtility.trans("176", "Contained Energy: ") + EnumChatFormatting.YELLOW
+ + formatNumbers(storage.getStored())
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + EnumChatFormatting.YELLOW
+ + formatNumbers(storage.getCapacity())
+ + EnumChatFormatting.RESET
+ + " EU");
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this device's IC2 energy info.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ return rEUAmount;
+ }
+
+ private static int addIC2EnergyConductorInfo(ArrayList<String> tList, TileEntity tTileEntity) {
+ int rEUAmount = 0;
+ try {
+ if (tTileEntity instanceof ic2.api.energy.tile.IEnergyConductor conductor) {
+ rEUAmount += 200;
+ tList.add(
+ GTUtility.trans("175", "Conduction Loss: ") + EnumChatFormatting.YELLOW
+ + conductor.getConductionLoss()
+ + EnumChatFormatting.RESET);
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this device's EU conduction info.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ return rEUAmount;
+ }
+
+ private static int addIC2WrenchableInfo(EntityPlayer aPlayer, ArrayList<String> tList, TileEntity tTileEntity) {
+ int rEUAmount = 0;
+ try {
+ if (tTileEntity instanceof ic2.api.tile.IWrenchable wrenchable) {
+ rEUAmount += 100;
+ tList.add(
+ GTUtility.trans("171", "Facing: ") + EnumChatFormatting.GREEN
+ + wrenchable.getFacing()
+ + EnumChatFormatting.RESET
+ + GTUtility.trans("172", " / Chance: ")
+ + EnumChatFormatting.YELLOW
+ + (wrenchable.getWrenchDropRate() * 100)
+ + EnumChatFormatting.RESET
+ + "%");
+ tList.add(
+ wrenchable.wrenchCanRemove(aPlayer)
+ ? EnumChatFormatting.GREEN + GTUtility.trans("173", "You can remove this with a Wrench")
+ + EnumChatFormatting.RESET
+ : EnumChatFormatting.RED + GTUtility.trans("174", "You can NOT remove this with a Wrench")
+ + EnumChatFormatting.RESET);
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this device's IC@ wrenchability.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ return rEUAmount;
+ }
+
+ private static int addAlignmentInfo(ArrayList<String> tList, TileEntity tTileEntity) {
+ int rEUAmount = 0;
+ try {
+ if (tTileEntity instanceof IAlignmentProvider alignmentProvider) {
+ final IAlignment tAlignment = alignmentProvider.getAlignment();
+ if (tAlignment != null) {
+ rEUAmount += 100;
+ tList.add(
+ GTUtility.trans("219", "Extended Facing: ") + EnumChatFormatting.GREEN
+ + tAlignment.getExtendedFacing()
+ + EnumChatFormatting.RESET);
+ }
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this device's alignment info.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ return rEUAmount;
+ }
+
+ private static int addReactorInfo(ArrayList<String> tList, TileEntity tTileEntity) {
+ int rEUAmount = 0;
+ try {
+ if (tTileEntity instanceof ic2.api.reactor.IReactor reactor) {
+ rEUAmount += 500;
+ tList.add(
+ GTUtility.trans("168", "Heat: ") + EnumChatFormatting.GREEN
+ + formatNumbers(reactor.getHeat())
+ + EnumChatFormatting.RESET
+ + " / "
+ + EnumChatFormatting.YELLOW
+ + formatNumbers(reactor.getMaxHeat())
+ + EnumChatFormatting.RESET);
+ tList.add(
+ GTUtility.trans("169", "HEM: ") + EnumChatFormatting.YELLOW
+ + reactor.getHeatEffectModifier()
+ + EnumChatFormatting.RESET);
+ }
+ } catch (Throwable e) {
+ tList.add(String.format("§cAn exception was thrown while fetching this reactor's info.§r"));
+ if (D1) e.printStackTrace(GTLog.err);
+ }
+ return rEUAmount;
+ }
+
+ public static String trans(String aKey, String aEnglish) {
+ return GTLanguageManager.addStringLocalization("Interaction_DESCRIPTION_Index_" + aKey, aEnglish);
+ }
+
+ public static String getTrans(String aKey) {
+ return GTLanguageManager.getTranslation("Interaction_DESCRIPTION_Index_" + aKey);
+ }
+
+ /**
+ * @return an Array containing the X and the Y Coordinate of the clicked Point, with the top left Corner as Origin,
+ * like on the Texture Sheet. return values should always be between [0.0F and 0.99F].
+ */
+ // TODO: use clamp()
+ public static float[] getClickedFacingCoords(ForgeDirection side, float aX, float aY, float aZ) {
+ return switch (side) {
+ case DOWN -> new float[] { Math.min(0.99F, Math.max(0, 1 - aX)), Math.min(0.99F, Math.max(0, aZ)) };
+ case UP -> new float[] { Math.min(0.99F, Math.max(0, aX)), Math.min(0.99F, Math.max(0, aZ)) };
+ case NORTH -> new float[] { Math.min(0.99F, Math.max(0, 1 - aX)), Math.min(0.99F, Math.max(0, 1 - aY)) };
+ case SOUTH -> new float[] { Math.min(0.99F, Math.max(0, aX)), Math.min(0.99F, Math.max(0, 1 - aY)) };
+ case WEST -> new float[] { Math.min(0.99F, Math.max(0, aZ)), Math.min(0.99F, Math.max(0, 1 - aY)) };
+ case EAST -> new float[] { Math.min(0.99F, Math.max(0, 1 - aZ)), Math.min(0.99F, Math.max(0, 1 - aY)) };
+ default -> new float[] { 0.5F, 0.5F };
+ };
+ }
+
+ /**
+ * This Function determines the direction a Block gets when being Wrenched. returns -1 if invalid. Even though that
+ * could never happen.
+ */
+ public static ForgeDirection determineWrenchingSide(ForgeDirection side, float aX, float aY, float aZ) {
+ ForgeDirection tBack = side.getOpposite();
+ switch (side) {
+ case DOWN, UP -> {
+ if (aX < 0.25) {
+ if (aZ < 0.25) return tBack;
+ if (aZ > 0.75) return tBack;
+ return WEST;
+ }
+ if (aX > 0.75) {
+ if (aZ < 0.25) return tBack;
+ if (aZ > 0.75) return tBack;
+ return EAST;
+ }
+ if (aZ < 0.25) return NORTH;
+ if (aZ > 0.75) return SOUTH;
+ return side;
+ }
+ case NORTH, SOUTH -> {
+ if (aX < 0.25) {
+ if (aY < 0.25) return tBack;
+ if (aY > 0.75) return tBack;
+ return WEST;
+ }
+ if (aX > 0.75) {
+ if (aY < 0.25) return tBack;
+ if (aY > 0.75) return tBack;
+ return EAST;
+ }
+ if (aY < 0.25) return DOWN;
+ if (aY > 0.75) return UP;
+ return side;
+ }
+ case WEST, EAST -> {
+ if (aZ < 0.25) {
+ if (aY < 0.25) return tBack;
+ if (aY > 0.75) return tBack;
+ return NORTH;
+ }
+ if (aZ > 0.75) {
+ if (aY < 0.25) return tBack;
+ if (aY > 0.75) return tBack;
+ return SOUTH;
+ }
+ if (aY < 0.25) return DOWN;
+ if (aY > 0.75) return UP;
+ return side;
+ }
+ }
+ return UNKNOWN;
+ }
+
+ private static DecimalFormat getDecimalFormat() {
+ return decimalFormatters.computeIfAbsent(Locale.getDefault(Locale.Category.FORMAT), locale -> {
+ DecimalFormat numberFormat = new DecimalFormat(); // uses the necessary locale inside anyway
+ numberFormat.setGroupingUsed(true);
+ numberFormat.setMaximumFractionDigits(2);
+ numberFormat.setRoundingMode(RoundingMode.HALF_UP);
+ DecimalFormatSymbols decimalFormatSymbols = numberFormat.getDecimalFormatSymbols();
+ decimalFormatSymbols.setGroupingSeparator(','); // Use sensible separator for best clarity.
+ numberFormat.setDecimalFormatSymbols(decimalFormatSymbols);
+ return numberFormat;
+ });
+ }
+
+ public static String formatNumbers(BigInteger aNumber) {
+ return getDecimalFormat().format(aNumber);
+ }
+
+ public static String formatNumbers(long aNumber) {
+ return getDecimalFormat().format(aNumber);
+ }
+
+ public static String formatNumbers(double aNumber) {
+ return getDecimalFormat().format(aNumber);
+ }
+
+ /*
+ * Check if stack has enough items of given type and subtract from stack, if there's no creative or 111 stack.
+ */
+ public static boolean consumeItems(EntityPlayer player, ItemStack stack, Item item, int count) {
+ if (stack != null && stack.getItem() == item && stack.stackSize >= count) {
+ if ((!player.capabilities.isCreativeMode) && (stack.stackSize != 111)) stack.stackSize -= count;
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * Check if stack has enough items of given gregtech material (will be oredicted) and subtract from stack, if
+ * there's no creative or 111 stack.
+ */
+ public static boolean consumeItems(EntityPlayer player, ItemStack stack, gregtech.api.enums.Materials mat,
+ int count) {
+ if (stack != null && GTOreDictUnificator.getItemData(stack).mMaterial.mMaterial == mat
+ && stack.stackSize >= count) {
+ if ((!player.capabilities.isCreativeMode) && (stack.stackSize != 111)) stack.stackSize -= count;
+ return true;
+ }
+ return false;
+ }
+
+ public static ArrayList<String> sortByValueToList(Map<String, Integer> map) {
+ List<Map.Entry<String, Integer>> list = new LinkedList<>(map.entrySet());
+ list.sort((o1, o2) -> o2.getValue() - o1.getValue());
+
+ ArrayList<String> result = new ArrayList<>();
+ for (Map.Entry<String, Integer> e : list) result.add(e.getKey());
+ return result;
+ }
+
+ public static String joinListToString(List<String> list) {
+ StringBuilder result = new StringBuilder(32);
+ for (String s : list) result.append(result.length() == 0 ? s : '|' + s);
+ return result.toString();
+ }
+
+ public static ItemStack getIntegratedCircuit(int config) {
+ return ItemList.Circuit_Integrated.getWithDamage(0, config);
+ }
+
+ public static float getBlockHardnessAt(World aWorld, int aX, int aY, int aZ) {
+ return aWorld.getBlock(aX, aY, aZ)
+ .getBlockHardness(aWorld, aX, aY, aZ);
+ }
+
+ public static FakePlayer getFakePlayer(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (aBaseMetaTileEntity.getWorld() instanceof WorldServer) {
+ return FakePlayerFactory.get(
+ (WorldServer) aBaseMetaTileEntity.getWorld(),
+ new GameProfile(aBaseMetaTileEntity.getOwnerUuid(), aBaseMetaTileEntity.getOwnerName()));
+ }
+ return null;
+ }
+
+ public static boolean eraseBlockByFakePlayer(FakePlayer aPlayer, int aX, int aY, int aZ, boolean isSimulate) {
+ if (aPlayer == null) return false;
+ World aWorld = aPlayer.worldObj;
+ BlockEvent.BreakEvent event = new BlockEvent.BreakEvent(
+ aX,
+ aY,
+ aZ,
+ aWorld,
+ aWorld.getBlock(aX, aY, aZ),
+ aWorld.getBlockMetadata(aX, aY, aZ),
+ aPlayer);
+ MinecraftForge.EVENT_BUS.post(event);
+ if (!event.isCanceled()) {
+ if (!isSimulate) return aWorld.setBlockToAir(aX, aY, aZ);
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean setBlockByFakePlayer(FakePlayer aPlayer, int aX, int aY, int aZ, Block aBlock, int aMeta,
+ boolean isSimulate) {
+ if (aPlayer == null) return false;
+ World aWorld = aPlayer.worldObj;
+ BlockEvent.PlaceEvent event = ForgeEventFactory
+ .onPlayerBlockPlace(aPlayer, new BlockSnapshot(aWorld, aX, aY, aZ, aBlock, aMeta), UNKNOWN);
+ if (!event.isCanceled()) {
+ if (!isSimulate) return aWorld.setBlock(aX, aY, aZ, aBlock, aMeta, 3);
+ return true;
+ }
+ return false;
+ }
+
+ public static int findMatchingStackInList(List<ItemStack> aStacks, ItemStack aStack) {
+ if (isStackInvalid(aStack)) return -1;
+ for (int i = 0, aStacksSize = aStacks.size(); i < aStacksSize; i++) {
+ ItemStack tStack = aStacks.get(i);
+ if (areStacksEqual(aStack, tStack)) return i;
+ }
+ return -1;
+ }
+
+ public static Map<GTUtility.ItemId, Long> convertItemListToMap(Collection<ItemStack> itemStacks) {
+ Map<GTUtility.ItemId, Long> result = new Object2LongOpenHashMap<>();
+ for (ItemStack itemStack : itemStacks) {
+ if (itemStack != null && itemStack.stackSize > 0) {
+ GTUtility.ItemId itemId = GTUtility.ItemId.createNoCopy(itemStack);
+ result.merge(itemId, (long) itemStack.stackSize, Long::sum);
+ }
+ }
+ return result;
+ }
+
+ public static Map<Fluid, Long> convertFluidListToMap(Collection<FluidStack> fluidStacks) {
+ Map<Fluid, Long> result = new Reference2LongOpenHashMap<>();
+ for (FluidStack fluidStack : fluidStacks) {
+ if (fluidStack != null && fluidStack.amount > 0) {
+ result.merge(fluidStack.getFluid(), (long) fluidStack.amount, Long::sum);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return Supplied collection that doesn't contain invalid MetaTileEntities
+ */
+ public static <T extends Collection<E>, E extends MetaTileEntity> T filterValidMTEs(T metaTileEntities) {
+ metaTileEntities.removeIf(mte -> mte == null || !mte.isValid());
+ return metaTileEntities;
+ }
+
+ public static ForgeDirection getSideFromPlayerFacing(Entity player) {
+ if (player == null) return ForgeDirection.UNKNOWN;
+ if (player.rotationPitch >= 65) return ForgeDirection.UP;
+ if (player.rotationPitch <= -65) return ForgeDirection.DOWN;
+ final byte facing = COMPASS_DIRECTIONS[MathHelper.floor_double(0.5D + 4.0F * player.rotationYaw / 360.0F)
+ & 0x3];
+ return ForgeDirection.getOrientation(facing);
+ }
+
+ public static class ItemNBT {
+
+ public static void setNBT(ItemStack aStack, NBTTagCompound aNBT) {
+ if (aNBT == null) {
+ aStack.setTagCompound(null);
+ return;
+ }
+ ArrayList<String> tTagsToRemove = new ArrayList<>();
+ for (String tKey : aNBT.func_150296_c()) {
+ NBTBase tValue = aNBT.getTag(tKey);
+ if (tValue == null || (tValue instanceof NBTPrimitive && ((NBTPrimitive) tValue).func_150291_c() == 0)
+ || (tValue instanceof NBTTagString && isStringInvalid(((NBTTagString) tValue).func_150285_a_())))
+ tTagsToRemove.add(tKey);
+ }
+ for (String tKey : tTagsToRemove) aNBT.removeTag(tKey);
+ aStack.setTagCompound(aNBT.hasNoTags() ? null : aNBT);
+ }
+
+ public static NBTTagCompound getNBT(ItemStack aStack) {
+ NBTTagCompound rNBT = aStack.getTagCompound();
+ return rNBT == null ? new NBTTagCompound() : rNBT;
+ }
+
+ public static void setPunchCardData(ItemStack aStack, String aPunchCardData) {
+ NBTTagCompound tNBT = getNBT(aStack);
+ tNBT.setString("GT.PunchCardData", aPunchCardData);
+ setNBT(aStack, tNBT);
+ }
+
+ public static String getPunchCardData(ItemStack aStack) {
+ NBTTagCompound tNBT = getNBT(aStack);
+ return tNBT.getString("GT.PunchCardData");
+ }
+
+ public static void setLighterFuel(ItemStack aStack, long aFuel) {
+ NBTTagCompound tNBT = getNBT(aStack);
+ tNBT.setLong("GT.LighterFuel", aFuel);
+ setNBT(aStack, tNBT);
+ }
+
+ public static long getLighterFuel(ItemStack aStack) {
+ NBTTagCompound tNBT = getNBT(aStack);
+ return tNBT.getLong("GT.LighterFuel");
+ }
+
+ public static void setMapID(ItemStack aStack, short aMapID) {
+ NBTTagCompound tNBT = getNBT(aStack);
+ tNBT.setShort("map_id", aMapID);
+ setNBT(aStack, tNBT);
+ }
+
+ public static short getMapID(ItemStack aStack) {
+ NBTTagCompound tNBT = getNBT(aStack);
+ if (!tNBT.hasKey("map_id")) return -1;
+ return tNBT.getShort("map_id");
+ }
+
+ public static void setBookTitle(ItemStack aStack, String aTitle) {
+ NBTTagCompound tNBT = getNBT(aStack);
+ tNBT.setString("title", aTitle);
+ setNBT(aStack, tNBT);
+ }
+
+ public static String getBookTitle(ItemStack aStack) {
+ NBTTagCompound tNBT = getNBT(aStack);
+ return tNBT.getString("title");
+ }
+
+ public static void setBookAuthor(ItemStack aStack, String aAuthor) {
+ NBTTagCompound tNBT = getNBT(aStack);
+ tNBT.setString("author", aAuthor);
+ setNBT(aStack, tNBT);
+ }
+
+ public static String getBookAuthor(ItemStack aStack) {
+ NBTTagCompound tNBT = getNBT(aStack);
+ return tNBT.getString("author");
+ }
+
+ public static void setProspectionData(ItemStack aStack, int aX, int aY, int aZ, int aDim, FluidStack aFluid,
+ String... aOres) {
+ NBTTagCompound tNBT = getNBT(aStack);
+ StringBuilder tData = new StringBuilder(aX + "," + aY + "," + aZ + "," + aDim + ",");
+ if (aFluid != null) tData.append(aFluid.amount)
+ .append(",")
+ .append(aFluid.getLocalizedName())
+ .append(","); // TODO
+ // CHECK
+ // IF
+ // THAT
+ // /5000
+ // is
+ // needed
+ // (Not
+ // needed)
+ for (String tString : aOres) {
+ tData.append(tString)
+ .append(",");
+ }
+ tNBT.setString("prospection", tData.toString());
+ setNBT(aStack, tNBT);
+ }
+
+ public static void setAdvancedProspectionData(byte aTier, ItemStack aStack, int aX, short aY, int aZ, int aDim,
+ ArrayList<String> aOils, ArrayList<String> aOres, int aRadius) {
+
+ setBookTitle(aStack, "Raw Prospection Data");
+
+ NBTTagCompound tNBT = getNBT(aStack);
+
+ tNBT.setByte("prospection_tier", aTier);
+ tNBT.setString("prospection_pos", "Dim: " + aDim + "\nX: " + aX + " Y: " + aY + " Z: " + aZ);
+
+ // ores
+ Collections.sort(aOres);
+ tNBT.setString("prospection_ores", joinListToString(aOres));
+
+ // oils
+ ArrayList<String> tOilsTransformed = new ArrayList<>(aOils.size());
+ for (String aStr : aOils) {
+ String[] aStats = aStr.split(",");
+ tOilsTransformed.add(aStats[0] + ": " + aStats[1] + "L " + aStats[2]);
+ }
+
+ tNBT.setString("prospection_oils", joinListToString(tOilsTransformed));
+
+ String tOilsPosStr = "X: " + Math.floorDiv(aX, 16 * 8) * 16 * 8
+ + " Z: "
+ + Math.floorDiv(aZ, 16 * 8) * 16 * 8
+ + "\n";
+ int xOff = aX - Math.floorDiv(aX, 16 * 8) * 16 * 8;
+ xOff = xOff / 16;
+ int xOffRemain = 7 - xOff;
+
+ int zOff = aZ - Math.floorDiv(aZ, 16 * 8) * 16 * 8;
+ zOff = zOff / 16;
+ int zOffRemain = 7 - zOff;
+
+ for (; zOff > 0; zOff--) {
+ tOilsPosStr = tOilsPosStr.concat("--------\n");
+ }
+ for (; xOff > 0; xOff--) {
+ tOilsPosStr = tOilsPosStr.concat("-");
+ }
+
+ tOilsPosStr = tOilsPosStr.concat("P");
+
+ for (; xOffRemain > 0; xOffRemain--) {
+ tOilsPosStr = tOilsPosStr.concat("-");
+ }
+ tOilsPosStr = tOilsPosStr.concat("\n");
+ for (; zOffRemain > 0; zOffRemain--) {
+ tOilsPosStr = tOilsPosStr.concat("--------\n");
+ }
+ tOilsPosStr = tOilsPosStr.concat(
+ " X: " + (Math.floorDiv(aX, 16 * 8) + 1) * 16 * 8
+ + " Z: "
+ + (Math.floorDiv(aZ, 16 * 8) + 1) * 16 * 8); // +1 oilfied to find bottomright of [5]
+
+ tNBT.setString("prospection_oils_pos", tOilsPosStr);
+
+ tNBT.setString("prospection_radius", String.valueOf(aRadius));
+
+ setNBT(aStack, tNBT);
+ }
+
+ public static void convertProspectionData(ItemStack aStack) {
+ NBTTagCompound tNBT = getNBT(aStack);
+ byte tTier = tNBT.getByte("prospection_tier");
+
+ if (tTier == 0) { // basic prospection data
+ String tData = tNBT.getString("prospection");
+ String[] tDataArray = tData.split(",");
+ if (tDataArray.length > 6) {
+ tNBT.setString(
+ "author",
+ " Dim: " + tDataArray[3]
+ + "X: "
+ + tDataArray[0]
+ + " Y: "
+ + tDataArray[1]
+ + " Z: "
+ + tDataArray[2]);
+ NBTTagList tNBTList = new NBTTagList();
+ StringBuilder tOres = new StringBuilder(" Prospected Ores: ");
+ for (int i = 6; tDataArray.length > i; i++) {
+ tOres.append(tDataArray[i])
+ .append(" ");
+ }
+ tNBTList.appendTag(
+ new NBTTagString(
+ "Tier " + tTier
+ + " Prospecting Data From: X"
+ + tDataArray[0]
+ + " Z:"
+ + tDataArray[2]
+ + " Dim:"
+ + tDataArray[3]
+ + " Produces "
+ + tDataArray[4]
+ + "L "
+ + tDataArray[5]
+ + " "
+ + tOres));
+ tNBT.setTag("pages", tNBTList);
+ }
+ } else { // advanced prospection data
+ String tPos = tNBT.getString("prospection_pos");
+ String tRadius = tNBT.getString("prospection_radius");
+
+ String tOresStr = tNBT.getString("prospection_ores");
+ String tOilsStr = tNBT.getString("prospection_oils");
+ String tOilsPosStr = tNBT.getString("prospection_oils_pos");
+
+ String[] tOres = tOresStr.isEmpty() ? null : tOresStr.split("\\|");
+ String[] tOils = tOilsStr.isEmpty() ? null : tOilsStr.split("\\|");
+
+ NBTTagList tNBTList = new NBTTagList();
+
+ String tPageText = "Prospector report\n" + tPos
+ + "\n\n"
+ + "Oils: "
+ + (tOils != null ? tOils.length : 0)
+ + "\n\n"
+ + "Ores within "
+ + tRadius
+ + " blocks\n\n"
+ + "Location is center of orevein\n\n"
+ + "Check NEI to confirm orevein type";
+ tNBTList.appendTag(new NBTTagString(tPageText));
+
+ if (tOres != null) fillBookWithList(tNBTList, "Ores Found %s\n\n", "\n", 7, tOres);
+
+ if (tOils != null) fillBookWithList(tNBTList, "Oils%s\n\n", "\n", 9, tOils);
+
+ tPageText = """
+ Oil notes
+
+ Prospects from NW to SE 576 chunks(9 8x8 oilfields)
+ around and gives min-max amount
+
+ [1][2][3]
+ [4][5][6]
+ [7][8][9]
+
+ [5] - Prospector in this 8x8 area""";
+ tNBTList.appendTag(new NBTTagString(tPageText));
+
+ tPageText = "Corners of [5] are \n" + tOilsPosStr + "\n" + "P - Prospector in 8x8 field";
+ tNBTList.appendTag(new NBTTagString(tPageText));
+
+ tNBT.setString("author", tPos.replace("\n", " "));
+ tNBT.setTag("pages", tNBTList);
+ }
+ setNBT(aStack, tNBT);
+ }
+
+ public static void fillBookWithList(NBTTagList aBook, String aPageHeader, String aListDelimiter,
+ int aItemsPerPage, String[] list) {
+ String aPageFormatter = " %d/%d";
+ int tTotalPages = list.length / aItemsPerPage + (list.length % aItemsPerPage > 0 ? 1 : 0);
+ int tPage = 0;
+ StringBuilder tPageText;
+ do {
+ tPageText = new StringBuilder();
+ for (int i = tPage * aItemsPerPage; i < (tPage + 1) * aItemsPerPage && i < list.length; i += 1)
+ tPageText.append((tPageText.length() == 0) ? "" : aListDelimiter)
+ .append(list[i]);
+
+ if (tPageText.length() > 0) {
+ String tPageCounter = tTotalPages > 1 ? String.format(aPageFormatter, tPage + 1, tTotalPages) : "";
+ NBTTagString tPageTag = new NBTTagString(String.format(aPageHeader, tPageCounter) + tPageText);
+ aBook.appendTag(tPageTag);
+ }
+
+ ++tPage;
+ } while (tPageText.length() > 0);
+ }
+
+ public static void addEnchantment(ItemStack aStack, Enchantment aEnchantment, int aLevel) {
+ NBTTagCompound tNBT = getNBT(aStack), tEnchantmentTag;
+ if (!tNBT.hasKey("ench", 9)) tNBT.setTag("ench", new NBTTagList());
+ NBTTagList tList = tNBT.getTagList("ench", 10);
+
+ boolean temp = true;
+
+ for (int i = 0; i < tList.tagCount(); i++) {
+ tEnchantmentTag = tList.getCompoundTagAt(i);
+ if (tEnchantmentTag.getShort("id") == aEnchantment.effectId) {
+ tEnchantmentTag.setShort("id", (short) aEnchantment.effectId);
+ tEnchantmentTag.setShort("lvl", (byte) aLevel);
+ temp = false;
+ break;
+ }
+ }
+
+ if (temp) {
+ tEnchantmentTag = new NBTTagCompound();
+ tEnchantmentTag.setShort("id", (short) aEnchantment.effectId);
+ tEnchantmentTag.setShort("lvl", (byte) aLevel);
+ tList.appendTag(tEnchantmentTag);
+ }
+ aStack.setTagCompound(tNBT);
+ }
+ }
+
+ public static String toSubscript(long no) {
+ char[] chars = Long.toString(no)
+ .toCharArray();
+ for (int i = 0; i < chars.length; i++) {
+ chars[i] += 8272;
+ }
+ return new String(chars);
+ }
+
+ public static boolean isPartOfMaterials(ItemStack aStack, Materials aMaterials) {
+ return GTOreDictUnificator.getAssociation(aStack) != null
+ && GTOreDictUnificator.getAssociation(aStack).mMaterial.mMaterial.equals(aMaterials);
+ }
+
+ public static boolean isPartOfOrePrefix(ItemStack aStack, OrePrefixes aPrefix) {
+ return GTOreDictUnificator.getAssociation(aStack) != null
+ && GTOreDictUnificator.getAssociation(aStack).mPrefix.equals(aPrefix);
+ }
+
+ public static final ImmutableSet<String> ORE_BLOCK_CLASSES = ImmutableSet.of(
+ "bartworks.system.material.BWMetaGeneratedOres",
+ "bartworks.system.material.BWMetaGeneratedSmallOres",
+ "gtPlusPlus.core.block.base.BlockBaseOre");
+
+ public static boolean isOre(Block aBlock, int aMeta) {
+ return (aBlock instanceof BlockOresAbstract) || isOre(new ItemStack(aBlock, 1, aMeta))
+ || ORE_BLOCK_CLASSES.contains(
+ aBlock.getClass()
+ .getName());
+ }
+
+ public static boolean isOre(ItemStack aStack) {
+ int tItem = GTUtility.stackToInt(aStack);
+ if (sOreTable.containsKey(tItem)) {
+ return sOreTable.get(tItem);
+ }
+ for (int id : OreDictionary.getOreIDs(aStack)) {
+ if (OreDictionary.getOreName(id)
+ .startsWith("ore")) {
+ sOreTable.put(tItem, true);
+ return true;
+ }
+ }
+ sOreTable.put(tItem, false);
+ return false;
+ }
+
+ /**
+ * Do <b>NOT</b> mutate the returned {@code ItemStack}! We return {@code ItemStack} instead of {@code Block} so that
+ * we can include metadata.
+ */
+ public static ItemStack getCobbleForOre(Block ore, short metaData) {
+ // We need to convert small ores to regular ores because small ores don't have associated ItemData.
+ // We take the modulus of the metadata by 16000 because that is the magic number to convert small ores to
+ // regular ores.
+ // See: GT_TileEntity_Ores.java
+ ItemData association = GTOreDictUnificator
+ .getAssociation(new ItemStack(Item.getItemFromBlock(ore), 1, metaData % 16000));
+ if (association != null) {
+ Supplier<ItemStack> supplier = sOreToCobble.get(association.mPrefix);
+ if (supplier != null) {
+ return supplier.get();
+ }
+ }
+ return new ItemStack(Blocks.cobblestone);
+ }
+
+ public static Optional<GTRecipe> reverseShapelessRecipe(ItemStack output, Object... aRecipe) {
+ if (output == null) {
+ return Optional.empty();
+ }
+
+ List<ItemStack> inputs = new ArrayList<>();
+
+ for (Object o : aRecipe) {
+ if (o instanceof ItemStack) {
+ ItemStack toAdd = ((ItemStack) o).copy();
+ inputs.add(toAdd);
+ } else if (o instanceof String) {
+ ItemStack stack = GTOreDictUnificator.get(o, 1);
+ if (stack == null) {
+ Optional<ItemStack> oStack = OreDictionary.getOres((String) o)
+ .stream()
+ .findAny();
+ if (oStack.isPresent()) {
+ ItemStack copy = oStack.get()
+ .copy();
+ inputs.add(copy);
+ }
+ } else {
+ ItemStack copy = stack.copy();
+ inputs.add(copy);
+ }
+ } else if (o instanceof Item) inputs.add(new ItemStack((Item) o));
+ else if (o instanceof Block) inputs.add(new ItemStack((Block) o));
+ else throw new IllegalStateException("A Recipe contains an invalid input! Output: " + output);
+ }
+
+ inputs.removeIf(x -> x.getItem() instanceof MetaGeneratedTool);
+
+ return Optional.of(
+ new GTRecipe(
+ false,
+ new ItemStack[] { output },
+ inputs.toArray(new ItemStack[0]),
+ null,
+ null,
+ null,
+ null,
+ 300,
+ 30,
+ 0));
+ }
+
+ public static Optional<GTRecipe> reverseShapedRecipe(ItemStack output, Object... aRecipe) {
+ if (output == null) {
+ return Optional.empty();
+ }
+
+ Map<Object, Integer> recipeAsMap = new HashMap<>();
+ Map<Character, Object> ingridients = new HashMap<>();
+ Map<Character, Integer> amounts = new HashMap<>();
+ boolean startFound = false;
+ for (int i = 0, aRecipeLength = aRecipe.length; i < aRecipeLength; i++) {
+ Object o = aRecipe[i];
+ if (!startFound) {
+ if (o instanceof String) {
+ for (Character c : ((String) o).toCharArray()) amounts.merge(c, 1, (a, b) -> ++a);
+ } else if (o instanceof Character) startFound = true;
+ } else if (!(o instanceof Character)) ingridients.put((Character) aRecipe[i - 1], o);
+ }
+ for (Map.Entry<Character, Object> characterObjectEntry : ingridients.entrySet()) {
+ for (Map.Entry<Character, Integer> characterIntegerEntry : amounts.entrySet()) {
+ if (characterObjectEntry.getKey() != characterIntegerEntry.getKey()) continue;
+ recipeAsMap.put(characterObjectEntry.getValue(), characterIntegerEntry.getValue());
+ }
+ }
+ List<ItemStack> inputs = new ArrayList<>();
+
+ for (Map.Entry<Object, Integer> o : recipeAsMap.entrySet()) {
+ final int amount = o.getValue();
+ if (o.getKey() instanceof ItemStack) {
+ ItemStack toAdd = ((ItemStack) o.getKey()).copy();
+ toAdd.stackSize = amount;
+ inputs.add(toAdd);
+ } else if (o.getKey() instanceof String dictName) {
+ // Do not register tools dictName in inputs
+ if (ToolDictNames.contains(dictName)) continue;
+ ItemStack stack = GTOreDictUnificator.get(dictName, null, amount, false, true);
+ if (stack == null) {
+ Optional<ItemStack> oStack = OreDictionary.getOres(dictName)
+ .stream()
+ .findAny();
+ if (oStack.isPresent()) {
+ ItemStack copy = oStack.get()
+ .copy();
+ copy.stackSize = amount;
+ inputs.add(copy);
+ }
+ } else {
+ ItemStack copy = stack.copy();
+ copy.stackSize = amount;
+ inputs.add(copy);
+ }
+ } else if (o.getKey() instanceof Item) inputs.add(new ItemStack((Item) o.getKey(), amount));
+ else if (o.getKey() instanceof Block) inputs.add(new ItemStack((Block) o.getKey(), amount));
+ else throw new IllegalStateException("A Recipe contains an invalid input! Output: " + output);
+ }
+
+ // Remove tools from inputs in case a recipe has one as a direct Item or ItemStack reference
+ inputs.removeIf(x -> x.getItem() instanceof MetaGeneratedTool);
+
+ return Optional.of(
+ new GTRecipe(
+ false,
+ new ItemStack[] { output },
+ inputs.toArray(new ItemStack[0]),
+ null,
+ null,
+ null,
+ null,
+ 300,
+ 30,
+ 0));
+ }
+
+ /**
+ * Add an itemstack to player inventory, or drop on ground if full. Can be called on client but it probably won't
+ * work very well.
+ */
+ public static void addItemToPlayerInventory(EntityPlayer aPlayer, ItemStack aStack) {
+ if (isStackInvalid(aStack)) return;
+ if (!aPlayer.inventory.addItemStackToInventory(aStack) && !aPlayer.worldObj.isRemote) {
+ EntityItem dropItem = aPlayer.entityDropItem(aStack, 0);
+ dropItem.delayBeforeCanPickup = 0;
+ }
+ }
+
+ public static long getNonnullElementCount(Object[] tArray) {
+ return Arrays.stream(tArray)
+ .filter(Objects::nonNull)
+ .count();
+ }
+
+ public static int clamp(int val, int lo, int hi) {
+ return MathHelper.clamp_int(val, lo, hi);
+ }
+
+ public static int ceilDiv(int lhs, int rhs) {
+ return (lhs + rhs - 1) / rhs;
+ }
+
+ public static long ceilDiv(long lhs, long rhs) {
+ return (lhs + rhs - 1) / rhs;
+ }
+
+ /**
+ * Hash an item stack for the purpose of storing hash across launches
+ */
+ public static int persistentHash(ItemStack aStack, boolean aUseStackSize, boolean aUseNBT) {
+ if (aStack == null) return 0;
+ int result = Objects.hashCode(GameRegistry.findUniqueIdentifierFor(aStack.getItem()));
+ result = result * 31 + Items.feather.getDamage(aStack);
+
+ if (aUseStackSize) result = result * 31 + aStack.stackSize;
+ if (aUseNBT) result = result * 31 + Objects.hashCode(aStack.stackTagCompound);
+ return result;
+ }
+
+ /**
+ * Hash an item stack for the purpose of storing hash across launches
+ */
+ public static int persistentHash(FluidStack aStack, boolean aUseStackSize, boolean aUseNBT) {
+ if (aStack == null) return 0;
+ int base = Objects.hashCode(
+ aStack.getFluid()
+ .getName());
+
+ if (aUseStackSize) base = base * 31 + aStack.amount;
+ if (aUseNBT) base = base * 31 + Objects.hashCode(aStack.tag);
+ return base;
+ }
+
+ public static int getCasingTextureIndex(Block block, int meta) {
+ if (block instanceof IHasIndexedTexture) return ((IHasIndexedTexture) block).getTextureIndex(meta);
+ return Textures.BlockIcons.ERROR_TEXTURE_INDEX;
+ }
+
+ public static boolean isCellEmpty(ItemStack itemStack) {
+ if (itemStack == null) return false;
+ ItemStack tStack = ItemList.Cell_Empty.get(1);
+ tStack.stackSize = itemStack.stackSize;
+ return GTUtility.areStacksEqual(itemStack, tStack);
+ }
+
+ /**
+ * Convert a cell to fluid. If given itemstack does not contain any fluid, return null. Will correctly multiple
+ * output fluid amount if input stack size is greater than 1.
+ */
+ public static FluidStack convertCellToFluid(ItemStack itemStack) {
+ if (itemStack == null) return null;
+ if (getFluidForFilledItem(itemStack, true) != null) {
+ FluidStack fluidStack = getFluidForFilledItem(itemStack, true);
+ if (fluidStack != null) fluidStack.amount = fluidStack.amount * itemStack.stackSize;
+ return fluidStack;
+ }
+ return null;
+ }
+
+ public static boolean isAnyIntegratedCircuit(ItemStack itemStack) {
+ if (itemStack == null) return false;
+ return itemStack.getItem() == ItemList.Circuit_Integrated.getItem() && 0 <= itemStack.getItemDamage()
+ && itemStack.getItemDamage() < 25;
+ }
+
+ public static byte convertRatioToRedstone(long used, long max, int threshold, boolean inverted) {
+ byte signal;
+ if (used <= 0) { // Empty
+ signal = 0;
+ } else if (used >= max) { // Full
+ signal = 15;
+ } else { // Range 1-14
+ signal = (byte) (1 + (14 * used) / max);
+ }
+
+ if (inverted) {
+ signal = (byte) (15 - signal);
+ }
+
+ if (threshold > 0) {
+ if (inverted && used >= threshold) {
+ return 0;
+ } else if (!inverted && used < threshold) {
+ return 0;
+ }
+ }
+
+ return signal;
+ }
+
+ public static ItemStack getNaniteAsCatalyst(Materials material) {
+ ItemStack aItem = material.getNanite(1);
+ return new ItemStack(aItem.getItem(), 0, aItem.getItemDamage());
+ }
+
+ public static Stream<NBTTagCompound> streamCompounds(NBTTagList list) {
+ if (list == null) return Stream.empty();
+ return IntStream.range(0, list.tagCount())
+ .mapToObj(list::getCompoundTagAt);
+ }
+
+ public static boolean equals(ItemStack[] a, ItemStack[] b) {
+ // because stupid mojang didn't override equals for us
+ if (a == null && b == null) return true;
+ if ((a == null) != (b == null)) return false;
+ if (a.length != b.length) return false;
+ for (int i = 0; i < a.length; i++) {
+ if (!areStacksEqual(a[i], b[i], false)) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Guava ImmutableMap variant of Collectors.toMap. Optimized for serial streams.
+ */
+ public static <T, K, U> Collector<T, ?, ImmutableMap<K, U>> toImmutableMapSerial(
+ Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) {
+ // petty type inference cannot work out the correct type parameter
+ return Collector.<T, ImmutableMap.Builder<K, U>, ImmutableMap<K, U>>of(
+ ImmutableMap::builder,
+ (b, t) -> b.put(keyMapper.apply(t), valueMapper.apply(t)),
+ (b1, b2) -> b1.putAll(b2.build()),
+ ImmutableMap.Builder::build);
+ }
+
+ public static boolean isArrayEmptyOrNull(Object[] arr) {
+ return arr == null || arr.length == 0;
+ }
+
+ public static boolean isArrayOfLength(Object[] arr, int expectedLength) {
+ return arr != null && arr.length == expectedLength;
+ }
+
+ @SafeVarargs
+ public static <E> Collection<E> concat(Collection<E>... colls) {
+ return concat(Arrays.asList(colls));
+ }
+
+ public static <E> Collection<E> concat(Collection<Collection<E>> colls) {
+ return new ConcatCollection<>(colls);
+ }
+
+ private static class ConcatCollection<E> extends AbstractCollection<E> {
+
+ private final Collection<Collection<E>> colls;
+ private final int size;
+
+ public ConcatCollection(Collection<Collection<E>> lists) {
+ Collection<Collection<E>> colls1 = null;
+ for (Collection<E> list : lists) {
+ if (list == null || list.isEmpty()) {
+ colls1 = lists.stream()
+ .filter(c -> c != null && !c.isEmpty())
+ .collect(Collectors.toList());
+ break;
+ }
+ }
+ if (colls1 == null) colls1 = lists;
+ colls = colls1;
+ int sum = 0;
+ for (Collection<E> list : colls) {
+ sum += list.size();
+ }
+ size = sum;
+ }
+
+ @Nonnull
+ @Override
+ public Iterator<E> iterator() {
+ return colls.stream()
+ .flatMap(Collection::stream)
+ .iterator();
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+ }
+
+ @AutoValue
+ public abstract static class ItemId {
+
+ public static ItemId create(NBTTagCompound tag) {
+ return new AutoValue_GTUtility_ItemId(
+ Item.getItemById(tag.getShort("item")),
+ tag.getShort("meta"),
+ tag.hasKey("tag", Constants.NBT.TAG_COMPOUND) ? tag.getCompoundTag("tag") : null);
+ }
+
+ /**
+ * This method copies NBT, as it is mutable.
+ */
+ public static ItemId create(ItemStack itemStack) {
+ NBTTagCompound nbt = itemStack.getTagCompound();
+ if (nbt != null) {
+ nbt = (NBTTagCompound) nbt.copy();
+ }
+
+ return new AutoValue_GTUtility_ItemId(itemStack.getItem(), Items.feather.getDamage(itemStack), nbt);
+ }
+
+ /**
+ * This method copies NBT, as it is mutable.
+ */
+ public static ItemId create(Item item, int metaData, @Nullable NBTTagCompound nbt) {
+ if (nbt != null) {
+ nbt = (NBTTagCompound) nbt.copy();
+ }
+ return new AutoValue_GTUtility_ItemId(item, metaData, nbt);
+ }
+
+ /**
+ * This method stores metadata as wildcard and NBT as null.
+ */
+ public static ItemId createAsWildcard(ItemStack itemStack) {
+ return new AutoValue_GTUtility_ItemId(itemStack.getItem(), W, null);
+ }
+
+ /**
+ * This method stores NBT as null.
+ */
+ public static ItemId createWithoutNBT(ItemStack itemStack) {
+ return new AutoValue_GTUtility_ItemId(itemStack.getItem(), Items.feather.getDamage(itemStack), null);
+ }
+
+ /**
+ * This method does not copy NBT in order to save time. Make sure not to mutate it!
+ */
+ public static ItemId createNoCopy(ItemStack itemStack) {
+ return new AutoValue_GTUtility_ItemId(
+ itemStack.getItem(),
+ Items.feather.getDamage(itemStack),
+ itemStack.getTagCompound());
+ }
+
+ /**
+ * This method does not copy NBT in order to save time. Make sure not to mutate it!
+ */
+ public static ItemId createNoCopy(Item item, int metaData, @Nullable NBTTagCompound nbt) {
+ return new AutoValue_GTUtility_ItemId(item, metaData, nbt);
+ }
+
+ protected abstract Item item();
+
+ protected abstract int metaData();
+
+ @Nullable
+ protected abstract NBTTagCompound nbt();
+
+ public NBTTagCompound writeToNBT() {
+ NBTTagCompound tag = new NBTTagCompound();
+ tag.setShort("item", (short) Item.getIdFromItem(item()));
+ tag.setShort("meta", (short) metaData());
+ if (nbt() != null) tag.setTag("tag", nbt());
+ return tag;
+ }
+
+ @Nonnull
+ public ItemStack getItemStack() {
+ ItemStack itemStack = new ItemStack(item(), 1, metaData());
+ itemStack.setTagCompound(nbt());
+ return itemStack;
+ }
+ }
+
+ public static int getPlasmaFuelValueInEUPerLiterFromMaterial(Materials material) {
+ return getPlasmaFuelValueInEUPerLiterFromFluid(material.getPlasma(1));
+ }
+
+ public static int getPlasmaFuelValueInEUPerLiterFromFluid(FluidStack aLiquid) {
+ if (aLiquid == null) return 0;
+ GTRecipe tFuel = RecipeMaps.plasmaFuels.getBackend()
+ .findFuel(aLiquid);
+ if (tFuel != null) return tFuel.mSpecialValue;
+ return 0;
+ }
+
+ public static MovingObjectPosition getPlayerLookingTarget(EntityPlayer viewpoint) {
+ double reachDistance = viewpoint instanceof EntityPlayerMP mp ? mp.theItemInWorldManager.getBlockReachDistance()
+ : getClientReachDistance();
+ Vec3 posVec = Vec3.createVectorHelper(
+ viewpoint.posX,
+ viewpoint.posY + (viewpoint.getEyeHeight() - viewpoint.getDefaultEyeHeight()),
+ viewpoint.posZ);
+ Vec3 lookVec = viewpoint.getLook(0);
+ Vec3 modifiedPosVec = posVec
+ .addVector(lookVec.xCoord * reachDistance, lookVec.yCoord * reachDistance, lookVec.zCoord * reachDistance);
+
+ return viewpoint.worldObj.rayTraceBlocks(posVec, modifiedPosVec);
+ }
+
+ public static float getClientReachDistance() {
+ return Minecraft.getMinecraft().playerController.getBlockReachDistance();
+ }
+}